summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp8
-rw-r--r--MULTIUSER_OWNERS4
-rw-r--r--ProtoLibraries.bp6
-rw-r--r--apct-tests/perftests/core/AndroidManifest.xml1
-rw-r--r--apct-tests/perftests/core/AndroidTest.xml5
-rw-r--r--apct-tests/perftests/core/jni/Android.bp7
-rw-r--r--apct-tests/perftests/core/jni/SystemPerfTest.cpp30
-rw-r--r--apct-tests/perftests/core/res/layout/linear_layout_for_xmlblock_benchmark.xml110
-rw-r--r--apct-tests/perftests/core/src/android/content/res/XmlBlockBenchmark.java321
-rw-r--r--apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java5
-rw-r--r--apct-tests/perftests/core/src/android/opengl/perftests/MatrixPerfTest.java397
-rw-r--r--apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java102
-rw-r--r--apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt18
-rw-r--r--apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java27
-rw-r--r--apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java320
-rw-r--r--apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java32
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java73
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java4
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobStore.java68
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/BatteryController.java20
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/DeviceIdleJobsController.java2
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java2
-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.java11
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/controllers/StateController.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java3
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java21
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java91
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java43
-rw-r--r--apex/jobscheduler/service/java/com/android/server/tare/Ledger.java2
-rw-r--r--api/Android.bp17
-rw-r--r--api/Android.mk2
-rw-r--r--cmds/idmap2/Android.bp1
-rw-r--r--cmds/idmap2/include/idmap2/Idmap.h96
-rw-r--r--cmds/idmap2/include/idmap2/ResourceMapping.h3
-rw-r--r--cmds/idmap2/include/idmap2/ResourceUtils.h4
-rw-r--r--cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp29
-rw-r--r--cmds/idmap2/libidmap2/Idmap.cpp70
-rw-r--r--cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp19
-rw-r--r--cmds/idmap2/libidmap2/RawPrintVisitor.cpp34
-rw-r--r--cmds/idmap2/libidmap2/ResourceMapping.cpp21
-rw-r--r--cmds/idmap2/tests/BinaryStreamVisitorTests.cpp6
-rw-r--r--cmds/idmap2/tests/IdmapTests.cpp39
-rw-r--r--cmds/idmap2/tests/RawPrintVisitorTests.cpp18
-rw-r--r--cmds/idmap2/tests/ResourceMappingTests.cpp13
-rw-r--r--cmds/idmap2/tests/TestHelpers.h97
-rw-r--r--core/api/current.txt112
-rw-r--r--core/api/system-current.txt21
-rw-r--r--core/api/test-current.txt13
-rw-r--r--core/api/test-lint-baseline.txt4
-rw-r--r--core/java/android/app/Activity.java33
-rw-r--r--core/java/android/app/ActivityManager.java87
-rw-r--r--core/java/android/app/ActivityManagerInternal.java2
-rw-r--r--core/java/android/app/ActivityThread.java3
-rw-r--r--core/java/android/app/AppOpInfo.java19
-rw-r--r--core/java/android/app/AppOpsManager.java20
-rw-r--r--core/java/android/app/BroadcastOptions.java46
-rw-r--r--core/java/android/app/ContextImpl.java6
-rw-r--r--core/java/android/app/EventLogTags.logtags16
-rw-r--r--core/java/android/app/Fragment.java5
-rw-r--r--core/java/android/app/IActivityManager.aidl11
-rw-r--r--core/java/android/app/Notification.java42
-rw-r--r--core/java/android/app/PropertyInvalidatedCache.java98
-rw-r--r--core/java/android/app/SystemServiceRegistry.java13
-rw-r--r--core/java/android/app/UiAutomationConnection.java15
-rw-r--r--core/java/android/app/UidObserver.java50
-rw-r--r--core/java/android/app/ambientcontext/AmbientContextEvent.java2
-rw-r--r--core/java/android/app/cloudsearch/CloudSearchManager.java66
-rw-r--r--core/java/android/app/cloudsearch/ICloudSearchManager.aidl33
-rw-r--r--core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl31
-rw-r--r--core/java/android/app/cloudsearch/SearchRequest.java194
-rw-r--r--core/java/android/app/cloudsearch/SearchResponse.java100
-rw-r--r--core/java/android/app/cloudsearch/SearchResult.java145
-rw-r--r--core/java/android/companion/virtual/IVirtualDevice.aidl7
-rw-r--r--core/java/android/companion/virtual/VirtualDeviceManager.java31
-rw-r--r--core/java/android/companion/virtual/audio/VirtualAudioDevice.java20
-rw-r--r--core/java/android/content/Context.java7
-rw-r--r--core/java/android/content/pm/OWNERS5
-rw-r--r--core/java/android/content/pm/PackageManager.java7
-rw-r--r--core/java/android/content/pm/PermissionMethod.java36
-rw-r--r--core/java/android/content/pm/parsing/ApkLiteParseUtils.java55
-rw-r--r--core/java/android/content/res/ResourceTimer.java323
-rw-r--r--core/java/android/content/res/XmlBlock.java203
-rw-r--r--core/java/android/ddm/DdmHandleViewDebug.java297
-rw-r--r--core/java/android/hardware/DataSpace.java49
-rw-r--r--core/java/android/hardware/biometrics/BiometricFingerprintConstants.java17
-rw-r--r--core/java/android/hardware/camera2/params/OutputConfiguration.java8
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManager.java47
-rw-r--r--core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java85
-rw-r--r--core/java/android/hardware/devicestate/IDeviceStateManager.aidl45
-rw-r--r--core/java/android/hardware/display/DisplayManager.java10
-rw-r--r--core/java/android/hardware/display/DisplayManagerGlobal.java16
-rw-r--r--core/java/android/hardware/display/DisplayManagerInternal.java10
-rw-r--r--core/java/android/hardware/display/IDisplayManager.aidl8
-rw-r--r--core/java/android/hardware/display/VirtualDisplay.java7
-rw-r--r--core/java/android/hardware/input/VirtualDpad.java94
-rw-r--r--core/java/android/hardware/input/VirtualKeyEvent.java1
-rw-r--r--core/java/android/hardware/input/VirtualKeyboard.java7
-rw-r--r--core/java/android/hardware/lights/Light.java24
-rw-r--r--core/java/android/hardware/radio/TunerAdapter.java171
-rw-r--r--core/java/android/hardware/radio/TunerCallbackAdapter.java62
-rw-r--r--core/java/android/inputmethodservice/IInputMethodWrapper.java134
-rw-r--r--core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java39
-rw-r--r--core/java/android/inputmethodservice/InputMethodService.java62
-rw-r--r--core/java/android/inputmethodservice/InputMethodServiceInternal.java7
-rw-r--r--core/java/android/inputmethodservice/RemoteInputConnection.java11
-rw-r--r--core/java/android/net/Ikev2VpnProfile.java5
-rw-r--r--core/java/android/os/Binder.java63
-rw-r--r--core/java/android/os/FileUtils.java81
-rw-r--r--core/java/android/os/IUserManager.aidl2
-rw-r--r--core/java/android/os/Parcel.java3
-rw-r--r--core/java/android/os/PersistableBundle.java42
-rw-r--r--core/java/android/os/Process.java19
-rw-r--r--core/java/android/os/StrictMode.java4
-rw-r--r--core/java/android/os/SystemClock.java2
-rw-r--r--core/java/android/os/UserManager.java41
-rw-r--r--core/java/android/os/storage/StorageManager.java9
-rw-r--r--core/java/android/preference/GenericInflater.java2
-rw-r--r--core/java/android/preference/SeekBarVolumizer.java3
-rw-r--r--core/java/android/provider/DeviceConfig.java25
-rw-r--r--core/java/android/provider/Settings.java8
-rw-r--r--core/java/android/provider/Telephony.java21
-rw-r--r--core/java/android/security/keymaster/KeymasterDefs.java2
-rw-r--r--core/java/android/security/keymaster/OWNERS2
-rw-r--r--core/java/android/service/cloudsearch/CloudSearchService.java54
-rw-r--r--core/java/android/service/credentials/Action.java98
-rw-r--r--core/java/android/service/credentials/CreateCredentialCallback.java64
-rw-r--r--core/java/android/service/credentials/CreateCredentialRequest.aidl3
-rw-r--r--core/java/android/service/credentials/CreateCredentialRequest.java103
-rw-r--r--core/java/android/service/credentials/CreateCredentialResponse.aidl (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt)7
-rw-r--r--core/java/android/service/credentials/CreateCredentialResponse.java140
-rw-r--r--core/java/android/service/credentials/Credential.java100
-rw-r--r--core/java/android/service/credentials/CredentialEntry.java216
-rw-r--r--core/java/android/service/credentials/CredentialProviderService.java119
-rw-r--r--core/java/android/service/credentials/CredentialsDisplayContent.java188
-rw-r--r--core/java/android/service/credentials/GetCredentialOption.java96
-rw-r--r--core/java/android/service/credentials/GetCredentialsCallback.java65
-rw-r--r--core/java/android/service/credentials/GetCredentialsRequest.aidl3
-rw-r--r--core/java/android/service/credentials/GetCredentialsRequest.java156
-rw-r--r--core/java/android/service/credentials/GetCredentialsResponse.aidl3
-rw-r--r--core/java/android/service/credentials/GetCredentialsResponse.java128
-rw-r--r--core/java/android/service/credentials/ICreateCredentialCallback.aidl13
-rw-r--r--core/java/android/service/credentials/ICredentialProviderService.aidl33
-rw-r--r--core/java/android/service/credentials/IGetCredentialsCallback.aidl13
-rw-r--r--core/java/android/service/credentials/SaveEntry.java162
-rw-r--r--core/java/android/service/dreams/DreamOverlayService.java12
-rw-r--r--core/java/android/service/dreams/DreamService.java7
-rw-r--r--core/java/android/service/notification/Condition.java38
-rw-r--r--core/java/android/service/voice/HotwordDetectionService.java8
-rw-r--r--core/java/android/service/voice/VoiceInteractionService.java28
-rw-r--r--core/java/android/service/wallpaper/WallpaperService.java7
-rw-r--r--core/java/android/text/Layout.java26
-rw-r--r--core/java/android/text/TextUtils.java26
-rwxr-xr-xcore/java/android/text/format/DateFormat.java2
-rw-r--r--core/java/android/text/method/WordIterator.java36
-rw-r--r--core/java/android/util/FeatureFlagUtils.java14
-rw-r--r--core/java/android/util/SparseArrayMap.java12
-rw-r--r--core/java/android/view/GestureDetector.java5
-rw-r--r--core/java/android/view/IWindowManager.aidl9
-rw-r--r--core/java/android/view/ImeFocusController.java213
-rw-r--r--core/java/android/view/InsetsSource.java3
-rw-r--r--core/java/android/view/KeyEvent.java22
-rw-r--r--core/java/android/view/MotionEvent.java16
-rw-r--r--core/java/android/view/SurfaceControl.java616
-rw-r--r--core/java/android/view/SurfaceView.java128
-rw-r--r--core/java/android/view/ThreadedRenderer.java8
-rw-r--r--core/java/android/view/View.java33
-rw-r--r--core/java/android/view/ViewRootImpl.java17
-rw-r--r--core/java/android/view/WindowManagerGlobal.java61
-rw-r--r--core/java/android/view/accessibility/AccessibilityEvent.java32
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java19
-rw-r--r--core/java/android/view/contentcapture/MainContentCaptureSession.java7
-rw-r--r--core/java/android/view/inputmethod/BaseInputConnection.java334
-rw-r--r--core/java/android/view/inputmethod/DeleteGesture.java4
-rw-r--r--core/java/android/view/inputmethod/DeleteRangeGesture.aidl (renamed from core/java/android/app/cloudsearch/SearchResult.aidl)10
-rw-r--r--core/java/android/view/inputmethod/DeleteRangeGesture.java243
-rw-r--r--core/java/android/view/inputmethod/EditorInfo.java4
-rw-r--r--core/java/android/view/inputmethod/HandwritingGesture.java19
-rw-r--r--core/java/android/view/inputmethod/InputConnection.java40
-rw-r--r--core/java/android/view/inputmethod/InputMethodManager.java118
-rw-r--r--core/java/android/view/inputmethod/InsertGesture.java4
-rw-r--r--core/java/android/view/inputmethod/SelectGesture.java4
-rw-r--r--core/java/android/view/inputmethod/SelectRangeGesture.aidl (renamed from core/java/android/app/cloudsearch/SearchRequest.aidl)10
-rw-r--r--core/java/android/view/inputmethod/SelectRangeGesture.java249
-rw-r--r--core/java/android/view/translation/UiTranslationController.java5
-rw-r--r--core/java/android/widget/Editor.java49
-rw-r--r--core/java/android/widget/TextView.java220
-rw-r--r--core/java/android/window/ScreenCapture.aidl (renamed from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/Apps.kt)13
-rw-r--r--core/java/android/window/ScreenCapture.java718
-rw-r--r--core/java/android/window/TaskFragmentInfo.java4
-rw-r--r--core/java/android/window/TaskFragmentParentInfo.aidl (renamed from core/java/android/service/cloudsearch/ICloudSearchService.aidl)11
-rw-r--r--core/java/android/window/TaskFragmentParentInfo.java158
-rw-r--r--core/java/android/window/TaskFragmentTransaction.java36
-rw-r--r--core/java/android/window/TransitionInfo.java52
-rw-r--r--core/java/android/window/WindowContainerTransaction.java20
-rw-r--r--core/java/com/android/internal/app/ChooserActivity.java22
-rw-r--r--core/java/com/android/internal/app/ILogAccessDialogCallback.aidl25
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl2
-rw-r--r--core/java/com/android/internal/app/LogAccessDialogActivity.java (renamed from services/core/java/com/android/server/logcat/LogAccessDialogActivity.java)80
-rw-r--r--core/java/com/android/internal/app/SimpleIconFactory.java15
-rw-r--r--core/java/com/android/internal/app/procstats/ProcessState.java5
-rw-r--r--core/java/com/android/internal/inputmethod/EditableInputConnection.java6
-rw-r--r--core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl11
-rw-r--r--core/java/com/android/internal/inputmethod/InputMethodDebug.java7
-rw-r--r--core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java42
-rw-r--r--core/java/com/android/internal/jank/EventLogTags.logtags10
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java54
-rw-r--r--core/java/com/android/internal/jank/TEST_MAPPING22
-rw-r--r--core/java/com/android/internal/os/BatteryStatsHistory.java27
-rw-r--r--core/java/com/android/internal/os/LongArrayMultiStateCounter.java16
-rw-r--r--core/java/com/android/internal/os/TimeoutRecord.java8
-rw-r--r--core/java/com/android/internal/os/Zygote.java2
-rw-r--r--core/java/com/android/internal/os/anr/AnrLatencyTracker.java456
-rw-r--r--core/java/com/android/internal/protolog/ProtoLogGroup.java1
-rw-r--r--core/java/com/android/internal/util/LatencyTracker.java137
-rw-r--r--core/java/com/android/internal/util/ScreenshotHelper.java3
-rw-r--r--core/java/com/android/internal/util/TEST_MAPPING15
-rw-r--r--core/java/com/android/internal/util/TypedProperties.java50
-rwxr-xr-xcore/java/com/android/internal/util/function/pooled/PooledLambda.java501
-rw-r--r--core/java/com/android/internal/widget/LockPatternUtils.java51
-rw-r--r--core/java/com/android/internal/widget/PointerLocationView.java71
-rw-r--r--core/jni/Android.bp3
-rw-r--r--core/jni/AndroidRuntime.cpp6
-rw-r--r--core/jni/OWNERS2
-rw-r--r--core/jni/android_content_res_ResourceTimer.cpp129
-rw-r--r--core/jni/android_media_AudioSystem.cpp11
-rw-r--r--core/jni/android_os_MessageQueue.cpp36
-rw-r--r--core/jni/android_util_AssetManager.cpp18
-rw-r--r--core/jni/android_util_XmlBlock.cpp113
-rw-r--r--core/jni/android_view_SurfaceControl.cpp368
-rw-r--r--core/jni/android_window_ScreenCapture.cpp356
-rw-r--r--core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp34
-rw-r--r--core/jni/include/android_runtime/android_view_SurfaceControl.h38
-rw-r--r--core/jni/jni_common.cpp52
-rw-r--r--core/jni/jni_common.h (renamed from core/java/android/app/cloudsearch/SearchResponse.aidl)17
-rw-r--r--core/proto/android/os/incident.proto6
-rw-r--r--core/res/Android.bp4
-rw-r--r--core/res/AndroidManifest.xml3
-rw-r--r--core/res/OWNERS3
-rw-r--r--core/res/res/values-af/strings.xml18
-rw-r--r--core/res/res/values-am/strings.xml18
-rw-r--r--core/res/res/values-ar/strings.xml18
-rw-r--r--core/res/res/values-as/strings.xml18
-rw-r--r--core/res/res/values-az/strings.xml18
-rw-r--r--core/res/res/values-b+sr+Latn/strings.xml18
-rw-r--r--core/res/res/values-be/strings.xml18
-rw-r--r--core/res/res/values-bg/strings.xml18
-rw-r--r--core/res/res/values-bn/strings.xml18
-rw-r--r--core/res/res/values-bs/strings.xml18
-rw-r--r--core/res/res/values-ca/strings.xml22
-rw-r--r--core/res/res/values-cs/strings.xml18
-rw-r--r--core/res/res/values-da/strings.xml18
-rw-r--r--core/res/res/values-de/strings.xml18
-rw-r--r--core/res/res/values-el/strings.xml18
-rw-r--r--core/res/res/values-en-rAU/strings.xml18
-rw-r--r--core/res/res/values-en-rCA/strings.xml18
-rw-r--r--core/res/res/values-en-rGB/strings.xml18
-rw-r--r--core/res/res/values-en-rIN/strings.xml18
-rw-r--r--core/res/res/values-en-rXC/strings.xml15
-rw-r--r--core/res/res/values-es-rUS/strings.xml18
-rw-r--r--core/res/res/values-es/strings.xml18
-rw-r--r--core/res/res/values-et/strings.xml18
-rw-r--r--core/res/res/values-eu/strings.xml18
-rw-r--r--core/res/res/values-fa/strings.xml22
-rw-r--r--core/res/res/values-fi/strings.xml18
-rw-r--r--core/res/res/values-fr-rCA/strings.xml18
-rw-r--r--core/res/res/values-fr/strings.xml18
-rw-r--r--core/res/res/values-gl/strings.xml18
-rw-r--r--core/res/res/values-gu/strings.xml18
-rw-r--r--core/res/res/values-hi/strings.xml18
-rw-r--r--core/res/res/values-hr/strings.xml18
-rw-r--r--core/res/res/values-hu/strings.xml18
-rw-r--r--core/res/res/values-hy/strings.xml18
-rw-r--r--core/res/res/values-in/strings.xml18
-rw-r--r--core/res/res/values-is/strings.xml18
-rw-r--r--core/res/res/values-it/strings.xml18
-rw-r--r--core/res/res/values-iw/strings.xml18
-rw-r--r--core/res/res/values-ja/strings.xml15
-rw-r--r--core/res/res/values-ka/strings.xml18
-rw-r--r--core/res/res/values-kk/strings.xml31
-rw-r--r--core/res/res/values-km/strings.xml18
-rw-r--r--core/res/res/values-kn/strings.xml18
-rw-r--r--core/res/res/values-ko/strings.xml18
-rw-r--r--core/res/res/values-ky/strings.xml18
-rw-r--r--core/res/res/values-lo/strings.xml18
-rw-r--r--core/res/res/values-lt/strings.xml18
-rw-r--r--core/res/res/values-lv/strings.xml18
-rw-r--r--core/res/res/values-mk/strings.xml18
-rw-r--r--core/res/res/values-ml/strings.xml18
-rw-r--r--core/res/res/values-mn/strings.xml18
-rw-r--r--core/res/res/values-mr/strings.xml18
-rw-r--r--core/res/res/values-ms/strings.xml18
-rw-r--r--core/res/res/values-my/strings.xml18
-rw-r--r--core/res/res/values-nb/strings.xml18
-rw-r--r--core/res/res/values-ne/strings.xml18
-rw-r--r--core/res/res/values-nl/strings.xml18
-rw-r--r--core/res/res/values-or/strings.xml20
-rw-r--r--core/res/res/values-pa/strings.xml18
-rw-r--r--core/res/res/values-pl/strings.xml18
-rw-r--r--core/res/res/values-pt-rBR/strings.xml18
-rw-r--r--core/res/res/values-pt-rPT/strings.xml22
-rw-r--r--core/res/res/values-pt/strings.xml18
-rw-r--r--core/res/res/values-ro/strings.xml926
-rw-r--r--core/res/res/values-ru/strings.xml20
-rw-r--r--core/res/res/values-si/strings.xml18
-rw-r--r--core/res/res/values-sk/strings.xml18
-rw-r--r--core/res/res/values-sl/strings.xml18
-rw-r--r--core/res/res/values-sq/strings.xml18
-rw-r--r--core/res/res/values-sr/strings.xml18
-rw-r--r--core/res/res/values-sv/strings.xml18
-rw-r--r--core/res/res/values-sw/strings.xml18
-rw-r--r--core/res/res/values-ta/strings.xml18
-rw-r--r--core/res/res/values-te/strings.xml18
-rw-r--r--core/res/res/values-th/strings.xml18
-rw-r--r--core/res/res/values-tl/strings.xml18
-rw-r--r--core/res/res/values-tr/strings.xml18
-rw-r--r--core/res/res/values-uk/strings.xml20
-rw-r--r--core/res/res/values-ur/strings.xml18
-rw-r--r--core/res/res/values-uz/strings.xml18
-rw-r--r--core/res/res/values-vi/strings.xml18
-rw-r--r--core/res/res/values-zh-rCN/strings.xml20
-rw-r--r--core/res/res/values-zh-rHK/strings.xml20
-rw-r--r--core/res/res/values-zh-rTW/strings.xml18
-rw-r--r--core/res/res/values-zu/strings.xml18
-rw-r--r--core/res/res/values/config.xml14
-rw-r--r--core/res/res/values/config_device_idle.xml123
-rw-r--r--core/res/res/values/config_telephony.xml6
-rw-r--r--core/res/res/values/strings.xml13
-rw-r--r--core/res/res/values/symbols.xml37
-rw-r--r--core/tests/coretests/src/android/ddm/DdmHandleViewDebugTest.java398
-rw-r--r--core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java13
-rw-r--r--core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java16
-rw-r--r--core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java244
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java50
-rw-r--r--core/tests/coretests/src/android/os/ProcessTest.java3
-rw-r--r--core/tests/coretests/src/android/service/notification/ConditionTest.java101
-rw-r--r--core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java3
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java759
-rw-r--r--core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java112
-rw-r--r--core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java6
-rw-r--r--core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java29
-rw-r--r--core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java116
-rw-r--r--core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java148
-rw-r--r--core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java64
-rw-r--r--data/etc/services.core.protolog.json27
-rw-r--r--data/keyboards/Generic.kl7
-rw-r--r--data/sounds/AudioTv.mk1
-rw-r--r--docs/html/reference/images/input_method/stylus_handwriting/delete_range_gesture_rects.pngbin0 -> 32318 bytes
-rw-r--r--docs/html/reference/images/input_method/stylus_handwriting/select_range_gesture_rects.pngbin0 -> 35546 bytes
-rw-r--r--graphics/java/android/graphics/BaseCanvas.java7
-rw-r--r--graphics/java/android/graphics/BaseRecordingCanvas.java7
-rw-r--r--graphics/java/android/graphics/ColorSpace.java6
-rw-r--r--graphics/java/android/graphics/HardwareRenderer.java102
-rw-r--r--graphics/java/android/graphics/Typeface.java47
-rw-r--r--graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java177
-rw-r--r--keystore/java/android/security/keystore/KeyProperties.java22
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java13
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java22
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java24
-rw-r--r--keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java24
-rw-r--r--keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java1
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java2
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java29
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java270
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java545
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java103
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java98
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java20
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java10
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java4
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java50
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java17
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java88
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java11
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java113
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java332
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java48
-rw-r--r--libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java43
-rw-r--r--libs/WindowManager/Jetpack/window-extensions-release.aarbin21513 -> 30713 bytes
-rw-r--r--libs/WindowManager/Shell/res/values-af/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-am/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ar/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-as/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-az/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-be/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-bg/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-bn/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-bs/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ca/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-cs/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-da/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-de/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-el/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-es-rUS/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-es/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-et/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-eu/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-fa/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-fi/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-fr-rCA/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-fr/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-gl/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-gu/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-hi/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-hr/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-hu/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-hy/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-in/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-is/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-it/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-iw/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ja/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ka/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-kk/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-km/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-kn/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ko/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ky/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-lo/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-lt/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-lv/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-mk/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ml/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-mn/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-mr/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ms/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-my/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-nb/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ne/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-nl/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-or/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-pa/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-pl/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rBR/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-pt-rPT/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-pt/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings.xml41
-rw-r--r--libs/WindowManager/Shell/res/values-ro/strings_tv.xml18
-rw-r--r--libs/WindowManager/Shell/res/values-ru/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-si/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-sk/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-sl/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-sq/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-sr/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-sv/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-sw/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ta/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-te/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-th/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-tl/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-tr/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-uk/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-ur/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-uz/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-vi/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rCN/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rHK/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-zh-rTW/strings.xml9
-rw-r--r--libs/WindowManager/Shell/res/values-zu/strings.xml9
-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/ShellTaskOrganizer.java3
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java104
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java149
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt10
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java92
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java53
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java168
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java58
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt89
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl26
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragAndDropPolicy.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java259
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java41
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java455
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/NonResizeableActivity.java)19
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java687
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java385
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java39
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java64
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java7
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java1
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java93
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SideStage.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java156
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java62
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java234
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java105
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java9
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java38
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/Android.bp2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/AndroidTest.xml2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonAssertions.kt88
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/CommonConstants.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt55
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt10
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt67
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt76
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt354
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientationTest.kt11
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipToAppTransition.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaExpandButtonClickTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExpandPipOnDoubleClickTest.kt2
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipDownShelfHeightChangeTest.kt41
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipShelfHeightTransition.kt53
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt50
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipRotationTest.kt32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipTransition.kt24
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/SetRequestedOrientationWhilePinnedTest.kt6
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt86
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt3
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt34
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt31
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt22
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt30
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt15
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt28
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt17
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt5
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt341
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt80
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt18
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt16
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp35
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml147
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml27
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml30
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_simple.xml23
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml32
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java108
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java66
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java61
-rw-r--r--libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SimpleActivity.java33
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java21
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java24
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java76
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java121
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt99
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java248
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java10
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java59
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java23
-rw-r--r--libs/androidfw/Android.bp2
-rw-r--r--libs/androidfw/AssetManager2.cpp20
-rw-r--r--libs/androidfw/Idmap.cpp43
-rw-r--r--libs/androidfw/ResourceTimer.cpp271
-rw-r--r--libs/androidfw/include/androidfw/Idmap.h26
-rw-r--r--libs/androidfw/include/androidfw/ResourceTimer.h221
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h4
-rw-r--r--libs/androidfw/tests/ResourceTimer_test.cpp245
-rw-r--r--libs/androidfw/tests/data/overlay/overlay.idmapbin636 -> 732 bytes
-rw-r--r--libs/hwui/Android.bp1
-rw-r--r--libs/hwui/DeviceInfo.cpp6
-rw-r--r--libs/hwui/HardwareBitmapUploader.cpp6
-rw-r--r--libs/hwui/MemoryPolicy.cpp69
-rw-r--r--libs/hwui/MemoryPolicy.h62
-rw-r--r--libs/hwui/Properties.cpp4
-rw-r--r--libs/hwui/Properties.h13
-rw-r--r--libs/hwui/SkiaCanvas.cpp8
-rw-r--r--libs/hwui/SkiaCanvas.h2
-rw-r--r--libs/hwui/hwui/Canvas.h2
-rw-r--r--libs/hwui/jni/android_graphics_Canvas.cpp7
-rw-r--r--libs/hwui/jni/android_graphics_HardwareRenderer.cpp13
-rw-r--r--libs/hwui/pipeline/skia/RenderNodeDrawable.cpp3
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp16
-rw-r--r--libs/hwui/pipeline/skia/SkiaRecordingCanvas.h2
-rw-r--r--libs/hwui/pipeline/skia/TransformCanvas.cpp6
-rw-r--r--libs/hwui/renderthread/CacheManager.cpp183
-rw-r--r--libs/hwui/renderthread/CacheManager.h46
-rw-r--r--libs/hwui/renderthread/CanvasContext.cpp36
-rw-r--r--libs/hwui/renderthread/CanvasContext.h4
-rw-r--r--libs/hwui/renderthread/DrawFrameTask.cpp4
-rw-r--r--libs/hwui/renderthread/RenderProxy.cpp5
-rw-r--r--libs/hwui/renderthread/RenderThread.cpp9
-rw-r--r--libs/hwui/renderthread/RenderThread.h5
-rw-r--r--libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp4
-rw-r--r--libs/hwui/tests/unit/CacheManagerTests.cpp6
-rw-r--r--location/java/android/location/LocationTime.java34
-rw-r--r--media/java/android/media/AudioAttributes.java2
-rw-r--r--media/java/android/media/AudioFormat.java3
-rw-r--r--media/java/android/media/AudioPlaybackConfiguration.java48
-rw-r--r--media/java/android/media/MediaFormat.java65
-rw-r--r--media/java/android/media/MediaPlayer.java37
-rw-r--r--media/java/android/media/audio/common/AidlConversion.java8
-rw-r--r--media/java/android/media/audiopolicy/AudioProductStrategy.java13
-rw-r--r--media/java/android/media/projection/MediaProjectionGlobal.java120
-rw-r--r--media/java/android/media/session/PlaybackState.java38
-rw-r--r--media/jni/android_media_MediaPlayer.cpp30
-rw-r--r--media/jni/android_media_MediaProfiles.cpp31
-rw-r--r--media/native/midi/Android.bp4
-rw-r--r--native/android/Android.bp3
-rw-r--r--native/android/OWNERS3
-rw-r--r--native/android/input.cpp2
-rw-r--r--native/android/libandroid.map.txt2
-rw-r--r--native/android/surface_control.cpp14
-rw-r--r--packages/BackupRestoreConfirmation/res/values-ro/strings.xml30
-rw-r--r--packages/CompanionDeviceManager/res/values-be/strings.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values-eu/strings.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values-is/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-ja/strings.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values-ky/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-nl/strings.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values-ro/strings.xml10
-rw-r--r--packages/CompanionDeviceManager/res/values-sk/strings.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values-sw/strings.xml4
-rw-r--r--packages/CompanionDeviceManager/res/values-te/strings.xml2
-rw-r--r--packages/CompanionDeviceManager/res/values-ur/strings.xml4
-rw-r--r--packages/CredentialManager/Android.bp35
-rw-r--r--packages/CredentialManager/AndroidManifest.xml46
-rw-r--r--packages/CredentialManager/OWNERS1
-rw-r--r--packages/CredentialManager/res/drawable-v24/ic_launcher_foreground.xml (renamed from packages/SystemUI/compose/gallery/res/drawable-v24/ic_launcher_foreground.xml)0
-rw-r--r--packages/CredentialManager/res/drawable/ic_launcher_background.xml (renamed from packages/SystemUI/compose/gallery/res/drawable/ic_launcher_background.xml)0
-rw-r--r--packages/CredentialManager/res/drawable/ic_passkey.xml16
-rw-r--r--packages/CredentialManager/res/mipmap-anydpi-v26/ic_launcher.xml (renamed from packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher.xml)0
-rw-r--r--packages/CredentialManager/res/mipmap-anydpi-v26/ic_launcher_round.xml (renamed from packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher_round.xml)0
-rw-r--r--packages/CredentialManager/res/mipmap-hdpi/ic_launcher.webp (renamed from packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher.webp)bin1404 -> 1404 bytes
-rw-r--r--packages/CredentialManager/res/mipmap-hdpi/ic_launcher_round.webp (renamed from packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher_round.webp)bin2898 -> 2898 bytes
-rw-r--r--packages/CredentialManager/res/mipmap-mdpi/ic_launcher.webp (renamed from packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher.webp)bin982 -> 982 bytes
-rw-r--r--packages/CredentialManager/res/mipmap-mdpi/ic_launcher_round.webp (renamed from packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher_round.webp)bin1772 -> 1772 bytes
-rw-r--r--packages/CredentialManager/res/mipmap-xhdpi/ic_launcher.webp (renamed from packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher.webp)bin1900 -> 1900 bytes
-rw-r--r--packages/CredentialManager/res/mipmap-xhdpi/ic_launcher_round.webp (renamed from packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher_round.webp)bin3918 -> 3918 bytes
-rw-r--r--packages/CredentialManager/res/mipmap-xxhdpi/ic_launcher.webp (renamed from packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher.webp)bin2884 -> 2884 bytes
-rw-r--r--packages/CredentialManager/res/mipmap-xxhdpi/ic_launcher_round.webp (renamed from packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher_round.webp)bin5914 -> 5914 bytes
-rw-r--r--packages/CredentialManager/res/mipmap-xxxhdpi/ic_launcher.webp (renamed from packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher.webp)bin3844 -> 3844 bytes
-rw-r--r--packages/CredentialManager/res/mipmap-xxxhdpi/ic_launcher_round.webp (renamed from packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher_round.webp)bin7778 -> 7778 bytes
-rw-r--r--packages/CredentialManager/res/values/colors.xml10
-rw-r--r--packages/CredentialManager/res/values/strings.xml13
-rw-r--r--packages/CredentialManager/res/values/themes.xml12
-rw-r--r--packages/CredentialManager/res/xml/backup_rules.xml13
-rw-r--r--packages/CredentialManager/res/xml/data_extraction_rules.xml19
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt151
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt52
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt18
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt25
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt398
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt58
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt203
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt30
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt23
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt14
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Shape.kt11
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt47
-rw-r--r--packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt56
-rw-r--r--packages/DynamicSystemInstallationService/res/values-ro/strings.xml10
-rw-r--r--packages/PackageInstaller/res/values-ro/strings.xml52
-rw-r--r--packages/PrintSpooler/res/values-ro/strings.xml42
-rw-r--r--packages/SettingsLib/Android.bp4
-rw-r--r--packages/SettingsLib/FooterPreference/res/values-ro/strings.xml2
-rw-r--r--packages/SettingsLib/Spa/TEST_MAPPING3
-rw-r--r--packages/SettingsLib/Spa/build.gradle6
-rw-r--r--packages/SettingsLib/Spa/gallery/AndroidManifest.xml11
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt21
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt (renamed from packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt)47
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt2
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt77
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt28
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt46
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt35
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt184
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt115
-rw-r--r--packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt96
-rw-r--r--packages/SettingsLib/Spa/spa/build.gradle1
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt53
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt97
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt260
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntryMacro.kt (renamed from packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ExampleFeatureScreen.kt)17
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntrySearchData.kt33
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt2
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt255
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt13
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt132
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt8
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt31
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt3
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/MaterialColors.kt15
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt43
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt78
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt24
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Parameter.kt24
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt135
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt38
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt4
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt78
-rw-r--r--packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt5
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt69
-rw-r--r--packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt70
-rw-r--r--packages/SettingsLib/SpaPrivileged/Android.bp6
-rw-r--r--packages/SettingsLib/SpaPrivileged/res/values/strings.xml4
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt66
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt (renamed from packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt)43
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt18
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt39
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt2
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt88
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt76
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfoPage.kt9
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt19
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt10
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt39
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt29
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt68
-rw-r--r--packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt113
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/Android.bp (renamed from packages/SystemUI/compose/gallery/tests/Android.bp)33
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/AndroidManifest.xml28
-rw-r--r--packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt113
-rw-r--r--packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml18
-rw-r--r--packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml18
-rw-r--r--packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml18
-rw-r--r--packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml18
-rw-r--r--packages/SettingsLib/res/drawable/ic_5g_uc_mobiledata.xml33
-rw-r--r--packages/SettingsLib/res/drawable/ic_5g_uw_mobiledata.xml33
-rw-r--r--packages/SettingsLib/res/layout/preference_category_divider.xml29
-rw-r--r--packages/SettingsLib/res/layout/preference_category_material_settings_with_divider.xml28
-rw-r--r--packages/SettingsLib/res/layout/restricted_popup_menu_item.xml37
-rw-r--r--packages/SettingsLib/res/layout/settings_dialog_title.xml55
-rw-r--r--packages/SettingsLib/res/values-af/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-am/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ar/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-as/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-az/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-b+sr+Latn/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-be/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-bg/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-bn/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-bs/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ca/strings.xml14
-rw-r--r--packages/SettingsLib/res/values-cs/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-da/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-de/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-el/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-en-rAU/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-en-rCA/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-en-rGB/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-en-rIN/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-en-rXC/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-es-rUS/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-es/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-et/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-eu/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-fa/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-fi/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-fr-rCA/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-fr/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-gl/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-gu/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-hi/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-hr/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-hu/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-hy/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-in/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-is/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-it/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-iw/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ja/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ka/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-kk/strings.xml12
-rw-r--r--packages/SettingsLib/res/values-km/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-kn/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ko/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ky/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-lo/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-lt/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-lv/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-mk/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ml/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-mn/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-mr/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ms/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-my/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-nb/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ne/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-nl/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-or/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-pa/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-pl/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-pt-rBR/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-pt-rPT/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-pt/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ro/arrays.xml2
-rw-r--r--packages/SettingsLib/res/values-ro/strings.xml54
-rw-r--r--packages/SettingsLib/res/values-ru/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-si/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-sk/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-sl/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-sq/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-sr/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-sv/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-sw/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ta/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-te/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-th/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-tl/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-tr/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-uk/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-ur/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-uz/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-vi/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-zh-rCN/strings.xml12
-rw-r--r--packages/SettingsLib/res/values-zh-rHK/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-zh-rTW/strings.xml10
-rw-r--r--packages/SettingsLib/res/values-zu/strings.xml10
-rw-r--r--packages/SettingsLib/res/values/arrays.xml44
-rw-r--r--packages/SettingsLib/res/values/colors.xml3
-rw-r--r--packages/SettingsLib/res/values/dimens.xml19
-rw-r--r--packages/SettingsLib/res/values/strings.xml21
-rw-r--r--packages/SettingsLib/search/Android.bp4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java60
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java13
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java49
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java23
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java5
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java20
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java4
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java8
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/DataServiceUtils.java367
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java41
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java76
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java56
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java14
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java3
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java1
-rw-r--r--packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java2
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/Shell/res/values-ro/strings.xml18
-rw-r--r--packages/Shell/src/com/android/shell/Screenshooter.java9
-rw-r--r--packages/SimAppDialog/res/values-ro/strings.xml4
-rw-r--r--packages/SoundPicker/res/values-ro/strings.xml8
-rw-r--r--packages/SystemUI/Android.bp2
-rw-r--r--packages/SystemUI/TEST_MAPPING19
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt7
-rw-r--r--packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt2
-rw-r--r--packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt356
-rw-r--r--packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt348
-rw-r--r--packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt270
-rw-r--r--packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt142
-rw-r--r--packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt247
-rw-r--r--packages/SystemUI/compose/gallery/Android.bp86
-rw-r--r--packages/SystemUI/compose/gallery/AndroidManifest.xml55
-rw-r--r--packages/SystemUI/compose/gallery/TEST_MAPPING15
-rw-r--r--packages/SystemUI/compose/gallery/app/AndroidManifest.xml39
-rw-r--r--packages/SystemUI/compose/gallery/proguard-rules.pro21
-rw-r--r--packages/SystemUI/compose/gallery/res/values/colors.xml19
-rw-r--r--packages/SystemUI/compose/gallery/res/values/strings.xml20
-rw-r--r--packages/SystemUI/compose/gallery/res/values/themes.xml30
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt77
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt139
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt210
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt80
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt155
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/PeopleScreen.kt46
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt126
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/TypographyScreen.kt67
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/people/Fakes.kt156
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt164
-rw-r--r--packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt99
-rw-r--r--packages/SystemUI/compose/gallery/tests/AndroidManifest.xml28
-rw-r--r--packages/SystemUI/compose/gallery/tests/src/com/android/systemui/compose/gallery/ScreenshotsTests.kt36
-rw-r--r--packages/SystemUI/ktfmt_includes.txt5
-rw-r--r--packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java4
-rw-r--r--packages/SystemUI/res-keyguard/drawable/kg_bouncer_secondary_button.xml43
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_off.xml109
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_on.xml109
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_off.xml109
-rw-r--r--packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_on.xml109
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml18
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml3
-rw-r--r--packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml3
-rw-r--r--packages/SystemUI/res-keyguard/values-ro/strings.xml50
-rw-r--r--packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml4
-rw-r--r--packages/SystemUI/res-keyguard/values/dimens.xml8
-rw-r--r--packages/SystemUI/res-product/values-ro/strings.xml30
-rw-r--r--packages/SystemUI/res/drawable/ic_magnification_menu_large.xml18
-rw-r--r--packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml18
-rw-r--r--packages/SystemUI/res/drawable/ic_magnification_menu_small.xml18
-rw-r--r--packages/SystemUI/res/drawable/ic_move_magnification.xml60
-rw-r--r--packages/SystemUI/res/drawable/ic_no_sim.xml10
-rw-r--r--packages/SystemUI/res/drawable/media_output_dialog_background.xml22
-rw-r--r--packages/SystemUI/res/drawable/qs_airplane_icon_off.xml25
-rw-r--r--packages/SystemUI/res/drawable/qs_airplane_icon_on.xml147
-rw-r--r--packages/SystemUI/res/drawable/qs_data_saver_icon_off.xml84
-rw-r--r--packages/SystemUI/res/drawable/qs_data_saver_icon_on.xml110
-rw-r--r--packages/SystemUI/res/drawable/qs_extra_dim_icon_off.xml197
-rw-r--r--packages/SystemUI/res/drawable/qs_extra_dim_icon_on.xml197
-rw-r--r--packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml84
-rw-r--r--packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml84
-rw-r--r--packages/SystemUI/res/drawable/qs_hotspot_icon_off.xml99
-rw-r--r--packages/SystemUI/res/drawable/qs_hotspot_icon_on.xml110
-rw-r--r--packages/SystemUI/res/drawable/qs_hotspot_icon_search.xml132
-rw-r--r--packages/SystemUI/res/drawable/qs_nightlight_icon_off.xml94
-rw-r--r--packages/SystemUI/res/drawable/qs_nightlight_icon_on.xml94
-rw-r--r--packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml218
-rw-r--r--packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml286
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml14
-rw-r--r--packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml16
-rw-r--r--packages/SystemUI/res/layout/media_output_dialog.xml1
-rw-r--r--packages/SystemUI/res/layout/media_projection_app_selector.xml5
-rw-r--r--packages/SystemUI/res/layout/media_projection_recent_tasks.xml54
-rw-r--r--packages/SystemUI/res/layout/media_projection_task_item.xml38
-rw-r--r--packages/SystemUI/res/layout/super_notification_shade.xml1
-rw-r--r--packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json2
-rw-r--r--packages/SystemUI/res/values-af/strings.xml6
-rw-r--r--packages/SystemUI/res/values-am/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ar/strings.xml9
-rw-r--r--packages/SystemUI/res/values-as/strings.xml9
-rw-r--r--packages/SystemUI/res/values-az/strings.xml9
-rw-r--r--packages/SystemUI/res/values-b+sr+Latn/strings.xml9
-rw-r--r--packages/SystemUI/res/values-be/strings.xml9
-rw-r--r--packages/SystemUI/res/values-bg/strings.xml9
-rw-r--r--packages/SystemUI/res/values-bn/strings.xml6
-rw-r--r--packages/SystemUI/res/values-bs/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ca/strings.xml17
-rw-r--r--packages/SystemUI/res/values-cs/strings.xml9
-rw-r--r--packages/SystemUI/res/values-da/strings.xml9
-rw-r--r--packages/SystemUI/res/values-de/strings.xml9
-rw-r--r--packages/SystemUI/res/values-el/strings.xml9
-rw-r--r--packages/SystemUI/res/values-es-rUS/strings.xml9
-rw-r--r--packages/SystemUI/res/values-es/strings.xml9
-rw-r--r--packages/SystemUI/res/values-et/strings.xml9
-rw-r--r--packages/SystemUI/res/values-eu/strings.xml6
-rw-r--r--packages/SystemUI/res/values-fa/strings.xml9
-rw-r--r--packages/SystemUI/res/values-fi/strings.xml9
-rw-r--r--packages/SystemUI/res/values-fr-rCA/strings.xml6
-rw-r--r--packages/SystemUI/res/values-fr/strings.xml9
-rw-r--r--packages/SystemUI/res/values-gl/strings.xml9
-rw-r--r--packages/SystemUI/res/values-gu/strings.xml6
-rw-r--r--packages/SystemUI/res/values-hi/strings.xml9
-rw-r--r--packages/SystemUI/res/values-hr/strings.xml9
-rw-r--r--packages/SystemUI/res/values-hu/strings.xml9
-rw-r--r--packages/SystemUI/res/values-hy/strings.xml9
-rw-r--r--packages/SystemUI/res/values-in/strings.xml9
-rw-r--r--packages/SystemUI/res/values-is/strings.xml9
-rw-r--r--packages/SystemUI/res/values-it/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ka/strings.xml9
-rw-r--r--packages/SystemUI/res/values-kk/strings.xml23
-rw-r--r--packages/SystemUI/res/values-km/strings.xml9
-rw-r--r--packages/SystemUI/res/values-kn/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ko/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ky/strings.xml9
-rw-r--r--packages/SystemUI/res/values-lo/strings.xml9
-rw-r--r--packages/SystemUI/res/values-lt/strings.xml9
-rw-r--r--packages/SystemUI/res/values-lv/strings.xml9
-rw-r--r--packages/SystemUI/res/values-mk/strings.xml6
-rw-r--r--packages/SystemUI/res/values-ml/strings.xml9
-rw-r--r--packages/SystemUI/res/values-mn/strings.xml6
-rw-r--r--packages/SystemUI/res/values-mr/strings.xml15
-rw-r--r--packages/SystemUI/res/values-ms/strings.xml9
-rw-r--r--packages/SystemUI/res/values-my/strings.xml9
-rw-r--r--packages/SystemUI/res/values-nb/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ne/strings.xml9
-rw-r--r--packages/SystemUI/res/values-nl/strings.xml6
-rw-r--r--packages/SystemUI/res/values-or/strings.xml11
-rw-r--r--packages/SystemUI/res/values-pa/strings.xml9
-rw-r--r--packages/SystemUI/res/values-pl/strings.xml9
-rw-r--r--packages/SystemUI/res/values-pt-rBR/strings.xml9
-rw-r--r--packages/SystemUI/res/values-pt-rPT/strings.xml3
-rw-r--r--packages/SystemUI/res/values-pt/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ro-ldrtl/strings.xml2
-rw-r--r--packages/SystemUI/res/values-ro/strings.xml493
-rw-r--r--packages/SystemUI/res/values-ru/strings.xml9
-rw-r--r--packages/SystemUI/res/values-si/strings.xml9
-rw-r--r--packages/SystemUI/res/values-sk/strings.xml6
-rw-r--r--packages/SystemUI/res/values-sl/strings.xml9
-rw-r--r--packages/SystemUI/res/values-sq/strings.xml9
-rw-r--r--packages/SystemUI/res/values-sr/strings.xml9
-rw-r--r--packages/SystemUI/res/values-sv/strings.xml9
-rw-r--r--packages/SystemUI/res/values-sw/strings.xml9
-rw-r--r--packages/SystemUI/res/values-sw600dp-land/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-sw600dp/dimens.xml2
-rw-r--r--packages/SystemUI/res/values-ta/strings.xml9
-rw-r--r--packages/SystemUI/res/values-te/strings.xml6
-rw-r--r--packages/SystemUI/res/values-th/strings.xml9
-rw-r--r--packages/SystemUI/res/values-tl/strings.xml6
-rw-r--r--packages/SystemUI/res/values-tr/strings.xml9
-rw-r--r--packages/SystemUI/res/values-uk/strings.xml9
-rw-r--r--packages/SystemUI/res/values-ur/strings.xml9
-rw-r--r--packages/SystemUI/res/values-vi/strings.xml9
-rw-r--r--packages/SystemUI/res/values-zh-rCN/strings.xml9
-rw-r--r--packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml4
-rw-r--r--packages/SystemUI/res/values-zh-rHK/strings.xml11
-rw-r--r--packages/SystemUI/res/values-zh-rTW/strings.xml9
-rw-r--r--packages/SystemUI/res/values-zu/strings.xml6
-rw-r--r--packages/SystemUI/res/values/colors.xml6
-rw-r--r--packages/SystemUI/res/values/config.xml27
-rw-r--r--packages/SystemUI/res/values/dimens.xml21
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt2
-rw-r--r--packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt4
-rw-r--r--packages/SystemUI/shared/res/values/attrs.xml35
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt3
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java8
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt124
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextClock.kt100
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextHelper.kt63
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt127
-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.java4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt4
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java21
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java10
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java6
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt54
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java29
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java23
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java15
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java41
-rw-r--r--packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java2
-rw-r--r--packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt71
-rw-r--r--packages/SystemUI/src/com/android/systemui/Dependency.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/ScreenDecorations.java34
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIApplication.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java93
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt89
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt141
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java67
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java48
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/Classifier.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java47
-rw-r--r--packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java86
-rw-r--r--packages/SystemUI/src/com/android/systemui/common/ui/drawable/CircularDrawable.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java22
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java35
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java83
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java57
-rw-r--r--packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt121
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java55
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt48
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt154
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt102
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt324
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt26
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt24
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt220
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt119
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/BiometricMessagesLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/NotificationRenderLog.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt12
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt67
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselControllerLogger.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaHost.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt91
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt58
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt112
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt41
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt39
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/TransferStatus.kt31
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt49
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorView.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt56
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt76
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt43
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt78
-rw-r--r--packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt64
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java85
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java31
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanel.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java19
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java15
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt7
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java1
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java23
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java63
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java62
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java72
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java73
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java5
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java276
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt55
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt10
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt119
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java9
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java74
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java17
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt182
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt269
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java (renamed from packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java)313
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt95
-rw-r--r--packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt36
-rw-r--r--packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt202
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt9
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java10
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java288
-rw-r--r--packages/SystemUI/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRenderer.java145
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt14
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt103
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java123
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java56
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt26
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt147
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt188
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt33
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java103
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt29
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java137
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt12
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java16
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java32
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java128
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt69
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt86
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt278
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt118
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt24
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java236
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java82
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt44
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt138
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt62
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt120
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt120
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt112
-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.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt106
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt108
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java37
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt132
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java25
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java36
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt132
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt63
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java115
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java40
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt)4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt264
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt (renamed from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt)10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt145
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt70
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRendererTest.java133
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java11
-rw-r--r--packages/VpnDialogs/res/values-ro/strings.xml18
-rw-r--r--packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml2
-rw-r--r--packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml2
-rw-r--r--services/Android.bp2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java12
-rw-r--r--services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java19
-rw-r--r--services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java40
-rw-r--r--services/autofill/java/com/android/server/autofill/Session.java83
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java6
-rw-r--r--services/cloudsearch/Android.bp22
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java202
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java89
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java401
-rw-r--r--services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java113
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java11
-rw-r--r--services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java5
-rw-r--r--services/companion/java/com/android/server/companion/virtual/CameraAccessController.java99
-rw-r--r--services/companion/java/com/android/server/companion/virtual/InputController.java46
-rw-r--r--services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java37
-rw-r--r--services/contentcapture/Android.bp5
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java30
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags13
-rw-r--r--services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java9
-rw-r--r--services/core/Android.bp12
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java8
-rw-r--r--services/core/java/com/android/server/BatteryService.java29
-rw-r--r--services/core/java/com/android/server/HardwarePropertiesManagerService.java11
-rw-r--r--services/core/java/com/android/server/SystemServerInitThreadPool.java6
-rw-r--r--services/core/java/com/android/server/TEST_MAPPING8
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java60
-rw-r--r--services/core/java/com/android/server/Watchdog.java2
-rw-r--r--services/core/java/com/android/server/am/ActiveServices.java218
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java681
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerShellCommand.java17
-rw-r--r--services/core/java/com/android/server/am/AnrHelper.java82
-rw-r--r--services/core/java/com/android/server/am/AppProfiler.java67
-rw-r--r--services/core/java/com/android/server/am/BroadcastConstants.java66
-rw-r--r--services/core/java/com/android/server/am/BroadcastProcessQueue.java499
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java31
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueImpl.java258
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueueModernImpl.java1260
-rw-r--r--services/core/java/com/android/server/am/BroadcastRecord.java189
-rw-r--r--services/core/java/com/android/server/am/BroadcastSkipPolicy.java14
-rw-r--r--services/core/java/com/android/server/am/ContentProviderHelper.java98
-rw-r--r--services/core/java/com/android/server/am/ProcessErrorStateRecord.java30
-rw-r--r--services/core/java/com/android/server/am/ProcessList.java2
-rw-r--r--services/core/java/com/android/server/am/ProcessProfileRecord.java13
-rw-r--r--services/core/java/com/android/server/am/ProcessReceiverRecord.java34
-rw-r--r--services/core/java/com/android/server/am/ProcessRecord.java7
-rw-r--r--services/core/java/com/android/server/am/ProcessStateRecord.java13
-rw-r--r--services/core/java/com/android/server/am/TEST_MAPPING8
-rw-r--r--services/core/java/com/android/server/am/UserController.java10
-rw-r--r--services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java5
-rw-r--r--services/core/java/com/android/server/appop/AppOpsRestrictions.java147
-rw-r--r--services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java452
-rw-r--r--services/core/java/com/android/server/appop/AppOpsService.java1534
-rw-r--r--services/core/java/com/android/server/appop/AppOpsServiceInterface.java9
-rw-r--r--services/core/java/com/android/server/appop/AppOpsUidStateTracker.java121
-rw-r--r--services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java623
-rw-r--r--services/core/java/com/android/server/appop/AttributedOp.java870
-rw-r--r--services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java12
-rw-r--r--services/core/java/com/android/server/audio/PlaybackActivityMonitor.java35
-rw-r--r--services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java10
-rw-r--r--services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java124
-rw-r--r--services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java (renamed from services/core/java/com/android/server/broadcastradio/BroadcastRadioServiceHidl.java)4
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java213
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java285
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java483
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/ProgramInfoCache.java304
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/RadioLogger.java52
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java523
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java390
-rw-r--r--services/core/java/com/android/server/broadcastradio/aidl/Utils.java44
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java88
-rw-r--r--services/core/java/com/android/server/content/SyncStorageEngine.java4
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerService.java161
-rw-r--r--services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java52
-rw-r--r--services/core/java/com/android/server/devicestate/OverrideRequest.java45
-rw-r--r--services/core/java/com/android/server/devicestate/OverrideRequestController.java64
-rw-r--r--services/core/java/com/android/server/display/AutomaticBrightnessController.java48
-rw-r--r--services/core/java/com/android/server/display/ColorFade.java20
-rw-r--r--services/core/java/com/android/server/display/DisplayControl.java11
-rw-r--r--services/core/java/com/android/server/display/DisplayDeviceConfig.java157
-rw-r--r--services/core/java/com/android/server/display/DisplayManagerService.java130
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController.java72
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerController2.java3071
-rw-r--r--services/core/java/com/android/server/display/DisplayPowerControllerInterface.java164
-rw-r--r--services/core/java/com/android/server/display/HysteresisLevels.java61
-rw-r--r--services/core/java/com/android/server/display/TEST_MAPPING9
-rw-r--r--services/core/java/com/android/server/display/VirtualDisplayAdapter.java16
-rw-r--r--services/core/java/com/android/server/display/WifiDisplayController.java2
-rw-r--r--services/core/java/com/android/server/display/color/ColorDisplayService.java4
-rw-r--r--services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java19
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java2
-rw-r--r--services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java229
-rw-r--r--services/core/java/com/android/server/input/BatteryController.java288
-rw-r--r--services/core/java/com/android/server/input/InputManagerInternal.java (renamed from core/java/android/hardware/input/InputManagerInternal.java)2
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java8
-rw-r--r--services/core/java/com/android/server/inputmethod/HandwritingModeController.java2
-rw-r--r--services/core/java/com/android/server/inputmethod/InputMethodManagerService.java11
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java12
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java13
-rw-r--r--services/core/java/com/android/server/location/contexthub/ContextHubService.java2
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java35
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssLocationProvider.java7
-rw-r--r--services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java4
-rw-r--r--services/core/java/com/android/server/location/injector/Injector.java3
-rw-r--r--services/core/java/com/android/server/location/injector/PackageResetHelper.java101
-rw-r--r--services/core/java/com/android/server/location/injector/SystemPackageResetHelper.java144
-rw-r--r--services/core/java/com/android/server/location/provider/LocationProviderManager.java35
-rw-r--r--services/core/java/com/android/server/logcat/LogcatManagerService.java17
-rw-r--r--services/core/java/com/android/server/media/MediaButtonReceiverHolder.java11
-rw-r--r--services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java7
-rw-r--r--services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java105
-rw-r--r--services/core/java/com/android/server/media/MediaRouterService.java43
-rw-r--r--services/core/java/com/android/server/notification/ManagedServices.java10
-rwxr-xr-xservices/core/java/com/android/server/notification/NotificationManagerService.java76
-rw-r--r--services/core/java/com/android/server/om/IdmapDaemon.java2
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java42
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerServiceImpl.java1
-rw-r--r--services/core/java/com/android/server/pm/ApexPackageInfo.java8
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterBase.java17
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterImpl.java7
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterLocked.java8
-rw-r--r--services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java1
-rw-r--r--services/core/java/com/android/server/pm/ComputerEngine.java6
-rw-r--r--services/core/java/com/android/server/pm/DeletePackageHelper.java17
-rw-r--r--services/core/java/com/android/server/pm/DexOptHelper.java12
-rw-r--r--services/core/java/com/android/server/pm/InitAppsHelper.java43
-rw-r--r--services/core/java/com/android/server/pm/InstallArgs.java19
-rw-r--r--services/core/java/com/android/server/pm/InstallPackageHelper.java529
-rw-r--r--services/core/java/com/android/server/pm/InstallRequest.java387
-rw-r--r--services/core/java/com/android/server/pm/InstallSource.java3
-rw-r--r--services/core/java/com/android/server/pm/InstallingSession.java99
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageHandler.java16
-rw-r--r--services/core/java/com/android/server/pm/PackageInstalledInfo.java78
-rw-r--r--services/core/java/com/android/server/pm/PackageInstallerService.java11
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerInternalBase.java14
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java93
-rw-r--r--services/core/java/com/android/server/pm/PackageRemovedInfo.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java31
-rw-r--r--services/core/java/com/android/server/pm/ParallelPackageParser.java11
-rw-r--r--services/core/java/com/android/server/pm/PostInstallData.java40
-rw-r--r--services/core/java/com/android/server/pm/ReconcilePackageUtils.java18
-rw-r--r--services/core/java/com/android/server/pm/ReconcileRequest.java11
-rw-r--r--services/core/java/com/android/server/pm/ReconciledPackage.java9
-rw-r--r--services/core/java/com/android/server/pm/RemovePackageHelper.java28
-rw-r--r--services/core/java/com/android/server/pm/ResolveIntentHelper.java51
-rw-r--r--services/core/java/com/android/server/pm/ScanPackageUtils.java4
-rw-r--r--services/core/java/com/android/server/pm/Settings.java9
-rw-r--r--services/core/java/com/android/server/pm/SharedLibrariesImpl.java14
-rw-r--r--services/core/java/com/android/server/pm/SharedLibraryUtils.java5
-rw-r--r--services/core/java/com/android/server/pm/SuspendPackageHelper.java62
-rw-r--r--services/core/java/com/android/server/pm/UserManagerInternal.java37
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java111
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java7
-rw-r--r--services/core/java/com/android/server/pm/parsing/PackageParser2.java12
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java6
-rw-r--r--services/core/java/com/android/server/pm/parsing/pkg/PackageImpl.java149
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/AndroidPackage.java339
-rw-r--r--services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt)38
-rw-r--r--services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java126
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageState.java202
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateImpl.java37
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateInternal.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java43
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageStateUtils.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserState.java68
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java2
-rw-r--r--services/core/java/com/android/server/pm/pkg/SharedLibrary.java92
-rw-r--r--services/core/java/com/android/server/pm/pkg/SharedLibraryWrapper.java109
-rw-r--r--services/core/java/com/android/server/pm/pkg/SharedUserApi.java1
-rw-r--r--services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java3
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java8
-rw-r--r--services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java44
-rw-r--r--services/core/java/com/android/server/policy/DeviceStateProviderImpl.java2
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java19
-rw-r--r--services/core/java/com/android/server/power/Notifier.java17
-rw-r--r--services/core/java/com/android/server/resources/OWNERS4
-rw-r--r--services/core/java/com/android/server/resources/ResourcesManagerService.java3
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java68
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java2
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java29
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java21
-rw-r--r--services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java59
-rw-r--r--services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java5
-rw-r--r--services/core/java/com/android/server/trust/TrustManagerService.java106
-rw-r--r--services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java7
-rw-r--r--services/core/java/com/android/server/utils/AlarmQueue.java15
-rw-r--r--services/core/java/com/android/server/vibrator/VibrationSettings.java48
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityController.java32
-rw-r--r--services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java68
-rw-r--r--services/core/java/com/android/server/wm/ActivityMetricsLogger.java100
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java153
-rw-r--r--services/core/java/com/android/server/wm/ActivityStarter.java5
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskManagerService.java8
-rw-r--r--services/core/java/com/android/server/wm/ActivityTaskSupervisor.java65
-rw-r--r--services/core/java/com/android/server/wm/AnrController.java220
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java15
-rw-r--r--services/core/java/com/android/server/wm/AsyncRotationController.java23
-rw-r--r--services/core/java/com/android/server/wm/BackNavigationController.java3
-rw-r--r--services/core/java/com/android/server/wm/ContentRecorder.java15
-rw-r--r--services/core/java/com/android/server/wm/DisplayContent.java56
-rw-r--r--services/core/java/com/android/server/wm/DisplayHashController.java8
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java5
-rw-r--r--services/core/java/com/android/server/wm/DragState.java4
-rw-r--r--services/core/java/com/android/server/wm/EmbeddedWindowController.java5
-rw-r--r--services/core/java/com/android/server/wm/InsetsPolicy.java16
-rw-r--r--services/core/java/com/android/server/wm/Letterbox.java4
-rw-r--r--services/core/java/com/android/server/wm/LetterboxUiController.java2
-rw-r--r--services/core/java/com/android/server/wm/RecentsAnimationController.java30
-rw-r--r--services/core/java/com/android/server/wm/ResetTargetTaskHelper.java3
-rw-r--r--services/core/java/com/android/server/wm/RootDisplayArea.java24
-rw-r--r--services/core/java/com/android/server/wm/RootWindowContainer.java13
-rw-r--r--services/core/java/com/android/server/wm/RunningTasks.java16
-rw-r--r--services/core/java/com/android/server/wm/ScreenRotationAnimation.java15
-rw-r--r--services/core/java/com/android/server/wm/Session.java4
-rw-r--r--services/core/java/com/android/server/wm/SurfaceAnimationRunner.java9
-rw-r--r--services/core/java/com/android/server/wm/SurfaceFreezer.java17
-rw-r--r--services/core/java/com/android/server/wm/Task.java187
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java36
-rw-r--r--services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java59
-rw-r--r--services/core/java/com/android/server/wm/TaskSnapshotController.java21
-rw-r--r--services/core/java/com/android/server/wm/Transition.java52
-rw-r--r--services/core/java/com/android/server/wm/TransitionController.java2
-rw-r--r--services/core/java/com/android/server/wm/WallpaperController.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowContainer.java24
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java155
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerShellCommand.java19
-rw-r--r--services/core/java/com/android/server/wm/WindowOrganizerController.java38
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java30
-rw-r--r--services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java5
-rw-r--r--services/core/jni/com_android_server_companion_virtual_InputController.cpp42
-rw-r--r--services/core/jni/com_android_server_display_DisplayControl.cpp24
-rw-r--r--services/core/jni/com_android_server_input_InputManagerService.cpp36
-rw-r--r--services/core/jni/gnss/Utils.cpp2
-rw-r--r--services/core/xsd/display-device-config/display-device-config.xsd14
-rw-r--r--services/core/xsd/display-device-config/schema/current.txt4
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java2
-rw-r--r--services/incremental/IncrementalService.cpp4
-rw-r--r--services/java/com/android/server/SystemServer.java8
-rw-r--r--services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java3
-rw-r--r--services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java2
-rw-r--r--services/tests/PackageManagerServiceTests/unit/Android.bp1
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt44
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParcelableComponentTest.kt23
-rw-r--r--services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt315
-rw-r--r--services/tests/mockingservicestests/AndroidManifest.xml5
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java165
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java332
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java747
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java277
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java121
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java917
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java94
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java235
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java2
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java26
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/FakePackageResetHelper.java38
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java7
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java38
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt20
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt6
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt4
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java41
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java32
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java36
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java45
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java334
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java21
-rw-r--r--services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java79
-rw-r--r--services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java5
-rw-r--r--services/tests/servicestests/src/com/android/server/am/UserControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java36
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java151
-rw-r--r--services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java102
-rw-r--r--services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java177
-rw-r--r--services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java8
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/TEST_MAPPING16
-rw-r--r--services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java13
-rw-r--r--services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt169
-rw-r--r--services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt7
-rw-r--r--services/tests/servicestests/src/com/android/server/location/contexthub/ConcurrentLinkedEvictingDequeTest.java46
-rw-r--r--services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEventLoggerTest.java156
-rw-r--r--services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java20
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ScanTests.java4
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java19
-rw-r--r--services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java36
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java32
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java152
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java51
-rw-r--r--services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java18
-rw-r--r--services/tests/wmtests/AndroidManifest.xml5
-rw-r--r--services/tests/wmtests/res/values/styles.xml (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml)20
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java143
-rw-r--r--services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java71
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java3
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java5
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java8
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java91
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java181
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java101
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java25
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java17
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/TransitionTests.java180
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java41
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java2
-rw-r--r--services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java62
-rw-r--r--services/usage/java/com/android/server/usage/UsageStatsService.java13
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java15
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java11
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java85
-rw-r--r--telephony/OWNERS3
-rw-r--r--telephony/java/android/telephony/AnomalyReporter.java16
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java148
-rw-r--r--telephony/java/android/telephony/NetworkRegistrationInfo.java25
-rw-r--r--telephony/java/android/telephony/NetworkService.java2
-rw-r--r--telephony/java/android/telephony/SignalThresholdInfo.java134
-rw-r--r--telephony/java/android/telephony/SubscriptionInfo.java1223
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java37
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java260
-rw-r--r--telephony/java/android/telephony/data/DataService.java2
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl13
-rw-r--r--tests/Codegen/Android.bp8
-rw-r--r--tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java2
-rw-r--r--tests/FlickerTests/AndroidManifest.xml2
-rw-r--r--tests/FlickerTests/res/anim/show_2000ms.xml21
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt26
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt29
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt)4
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt16
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt107
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt16
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppHelper.kt13
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt10
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt9
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt)12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt70
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt)17
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt6
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt9
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt21
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt (renamed from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt)86
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt9
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt9
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt9
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt13
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt41
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt20
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt12
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt8
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt115
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt11
-rw-r--r--tests/FlickerTests/src/com/android/server/wm/flicker/rotation/SeamlessAppRotationTest.kt26
-rw-r--r--tests/FlickerTests/test-apps/Android.bp0
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml240
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/drawable/bg.png (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/bg.png)bin1966 -> 1966 bytes
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_bubble.xml (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_bubble.xml)28
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_message.xml (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_message.xml)28
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_bubble.xml)28
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml (renamed from tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml)0
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_main.xml (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_main.xml)28
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml)28
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml)28
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml32
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/layout/assistant_session.xml26
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/xml/interaction_service.xml20
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/res/xml/recognition_service.xml17
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java23
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java237
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionService.java22
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSession.java53
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSessionService.java28
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantRecognitionService.java37
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleActivity.java (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleActivity.java)4
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java)6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/FixedActivity.java (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/FixedActivity.java)6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/LaunchBubbleActivity.java)4
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchNewActivity.java (renamed from tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ButtonActivity.java)6
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java8
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java)13
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java2
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenActivity.java)4
-rw-r--r--tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenSecondaryActivity.java (renamed from libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java)4
-rw-r--r--tests/HandwritingIme/OWNERS3
-rw-r--r--tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java15
-rw-r--r--tests/HwAccelerationTest/AndroidManifest.xml9
-rw-r--r--tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java168
-rw-r--r--tests/TouchLatency/OWNERS2
-rw-r--r--tests/TouchLatency/app/build.gradle4
-rw-r--r--tests/TouchLatency/app/src/main/AndroidManifest.xml10
-rw-r--r--tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java271
-rw-r--r--tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivityPresentation.java128
-rw-r--r--tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java219
-rw-r--r--tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml6
-rw-r--r--tests/TouchLatency/app/src/main/res/values/strings.xml1
-rw-r--r--tests/TouchLatency/build.gradle2
-rw-r--r--tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java912
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java100
-rw-r--r--tools/aapt2/Android.bp4
-rw-r--r--tools/aapt2/ResourceMetadata.proto49
-rw-r--r--tools/aapt2/cmd/Convert.cpp6
-rw-r--r--tools/aapt2/cmd/Convert.h16
-rw-r--r--tools/aapt2/cmd/Link.cpp3
-rw-r--r--tools/aapt2/cmd/Link.h5
-rw-r--r--tools/aapt2/cmd/Optimize.cpp21
-rw-r--r--tools/aapt2/cmd/Optimize.h20
-rw-r--r--tools/aapt2/format/binary/TableFlattener.cpp18
-rw-r--r--tools/aapt2/format/binary/TableFlattener.h16
-rw-r--r--tools/aapt2/format/binary/TableFlattener_test.cpp30
-rw-r--r--tools/aapt2/optimize/Obfuscator.cpp (renamed from tools/aapt2/optimize/ResourcePathShortener.cpp)33
-rw-r--r--tools/aapt2/optimize/Obfuscator.h (renamed from tools/aapt2/optimize/ResourcePathShortener.h)16
-rw-r--r--tools/aapt2/optimize/Obfuscator_test.cpp (renamed from tools/aapt2/optimize/ResourcePathShortener_test.cpp)47
-rw-r--r--tools/fonts/Android.bp5
-rw-r--r--tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt12
-rw-r--r--tools/processors/immutability/Android.bp76
-rw-r--r--tools/processors/immutability/OWNERS1
-rw-r--r--tools/processors/immutability/TEST_MAPPING7
-rw-r--r--tools/processors/immutability/src/android/processor/immutability/ImmutabilityProcessor.kt372
-rw-r--r--tools/processors/immutability/src/android/processor/immutability/Immutable.java74
-rw-r--r--tools/processors/immutability/src/android/processor/immutability/MessageUtils.kt45
-rw-r--r--tools/processors/immutability/test/android/processor/ImmutabilityProcessorTest.kt310
-rw-r--r--tools/processors/staledataclass/Android.bp18
-rw-r--r--tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt1
1757 files changed, 68679 insertions, 22709 deletions
diff --git a/Android.bp b/Android.bp
index 2c550fdfb675..2f5c851f6d80 100644
--- a/Android.bp
+++ b/Android.bp
@@ -342,6 +342,14 @@ java_defaults {
"staledataclass-annotation-processor",
"error_prone_android_framework",
],
+ // Exports needed for staledataclass-annotation-processor, see b/139342589.
+ javacflags: [
+ "-J--add-modules=jdk.compiler",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
required: [
// TODO(b/120066492): remove default_television.xml when the build system
// propagates "required" properly.
diff --git a/MULTIUSER_OWNERS b/MULTIUSER_OWNERS
index 9d92e0fc50f7..b8857ecd0a93 100644
--- a/MULTIUSER_OWNERS
+++ b/MULTIUSER_OWNERS
@@ -1,5 +1,9 @@
# OWNERS of Multiuser related files
+annabauza@google.com
bookatz@google.com
+nykkumar@google.com
olilan@google.com
omakoto@google.com
+tetianameronyk@google.com
+tyk@google.com
yamasani@google.com
diff --git a/ProtoLibraries.bp b/ProtoLibraries.bp
index c12f5b4395ec..56d91b2e4bc5 100644
--- a/ProtoLibraries.bp
+++ b/ProtoLibraries.bp
@@ -41,7 +41,6 @@ gensrcs {
":libtombstone_proto-src",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
- ":service-permission-streaming-proto-sources",
],
output_extension: "srcjar",
}
@@ -72,7 +71,6 @@ gensrcs {
":libstats_atom_message_protos",
"core/proto/**/*.proto",
"libs/incident/**/*.proto",
- ":service-permission-streaming-proto-sources",
],
output_extension: "proto.h",
@@ -91,7 +89,6 @@ java_library_host {
"cmds/statsd/src/**/*.proto",
"core/proto/**/*.proto",
"libs/incident/proto/**/*.proto",
- ":service-permission-streaming-proto-sources",
],
proto: {
include_dirs: [
@@ -126,7 +123,6 @@ java_library {
":libstats_atom_message_protos",
"core/proto/**/*.proto",
"libs/incident/proto/android/os/**/*.proto",
- ":service-permission-streaming-proto-sources",
],
// Protos have lots of MissingOverride and similar.
errorprone: {
@@ -148,7 +144,6 @@ java_library {
":libstats_atom_message_protos",
"core/proto/**/*.proto",
"libs/incident/proto/android/os/**/*.proto",
- ":service-permission-streaming-proto-sources",
],
exclude_srcs: [
"core/proto/android/privacy.proto",
@@ -184,7 +179,6 @@ cc_defaults {
":libstats_atom_enum_protos",
":libstats_atom_message_protos",
"core/proto/**/*.proto",
- ":service-permission-streaming-proto-sources",
],
}
diff --git a/apct-tests/perftests/core/AndroidManifest.xml b/apct-tests/perftests/core/AndroidManifest.xml
index bb57161b4f6b..4e24909f5d3b 100644
--- a/apct-tests/perftests/core/AndroidManifest.xml
+++ b/apct-tests/perftests/core/AndroidManifest.xml
@@ -16,6 +16,7 @@
<application>
<uses-library android:name="android.test.runner" />
+ <profileable android:shell="true" />
<activity android:name="android.perftests.utils.PerfTestActivity"
android:exported="true">
<intent-filter>
diff --git a/apct-tests/perftests/core/AndroidTest.xml b/apct-tests/perftests/core/AndroidTest.xml
index 4f8ee2927d51..86f41e1f496c 100644
--- a/apct-tests/perftests/core/AndroidTest.xml
+++ b/apct-tests/perftests/core/AndroidTest.xml
@@ -27,6 +27,11 @@
<option name="test-file-name" value="CorePerfTests.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
+ <option name="run-command" value="wm dismiss-keyguard" />
+ </target_preparer>
+
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="pull-pattern-keys" value="perfetto_file_path" />
</metrics_collector>
diff --git a/apct-tests/perftests/core/jni/Android.bp b/apct-tests/perftests/core/jni/Android.bp
index 1e4405dea2d2..b92b13b5b75a 100644
--- a/apct-tests/perftests/core/jni/Android.bp
+++ b/apct-tests/perftests/core/jni/Android.bp
@@ -9,7 +9,6 @@ package {
cc_library_shared {
name: "libperftestscore_jni",
- sdk_version: "21",
srcs: ["SystemPerfTest.cpp"],
@@ -20,4 +19,10 @@ cc_library_shared {
"-Wunreachable-code",
],
header_libs: ["jni_headers"],
+
+ shared_libs: [
+ "libandroid_runtime",
+ "liblog",
+ "libnativehelper",
+ ],
}
diff --git a/apct-tests/perftests/core/jni/SystemPerfTest.cpp b/apct-tests/perftests/core/jni/SystemPerfTest.cpp
index f102e3ec0a71..e7d451d894b2 100644
--- a/apct-tests/perftests/core/jni/SystemPerfTest.cpp
+++ b/apct-tests/perftests/core/jni/SystemPerfTest.cpp
@@ -16,6 +16,9 @@
#include <jni.h>
+#include "nativehelper/JNIHelp.h"
+#include "core_jni_helpers.h"
+
#define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
static void jintarrayArgumentNoop(JNIEnv*, jclass, jintArray, jint) {
@@ -48,11 +51,38 @@ static jint jintarrayBasicAccess(JNIEnv* env, jclass, jintArray jarray, jint ind
return ret;
}
+static jint jintFastNativeAccess(JNIEnv*, jclass, jint number) {
+ return number;
+}
+
+static jint jintCriticalNativeAccess(CRITICAL_JNI_PARAMS_COMMA jint number) {
+ return number;
+}
+
+static jint jintFastNativeCheckNullPointer(JNIEnv* env, jclass, jint number) {
+ if (number == 0) {
+ jniThrowNullPointerException(env, NULL);
+ return -1;
+ }
+ return number;
+}
+
+static jint jintCriticalNativeCheckNullPointer(CRITICAL_JNI_PARAMS_COMMA jint number) {
+ if (number == 0) {
+ return -1;
+ }
+ return number;
+}
+
static const JNINativeMethod sMethods[] = {
{"jintarrayArgumentNoop", "([II)V", (void *) jintarrayArgumentNoop},
{"jintarrayGetLength", "([I)I", (void *) jintarrayGetLength},
{"jintarrayCriticalAccess", "([II)I", (void *) jintarrayCriticalAccess},
{"jintarrayBasicAccess", "([II)I", (void *) jintarrayBasicAccess},
+ {"jintFastNativeAccess", "(I)I", (void *) jintFastNativeAccess},
+ {"jintCriticalNativeAccess", "(I)I", (void *) jintCriticalNativeAccess},
+ {"jintFastNativeCheckNullPointer", "(I)I", (void *) jintFastNativeCheckNullPointer},
+ {"jintCriticalNativeCheckNullPointer", "(I)I", (void *) jintCriticalNativeCheckNullPointer},
};
static int registerNativeMethods(JNIEnv* env, const char* className,
diff --git a/apct-tests/perftests/core/res/layout/linear_layout_for_xmlblock_benchmark.xml b/apct-tests/perftests/core/res/layout/linear_layout_for_xmlblock_benchmark.xml
new file mode 100644
index 000000000000..9005d6fa9e76
--- /dev/null
+++ b/apct-tests/perftests/core/res/layout/linear_layout_for_xmlblock_benchmark.xml
@@ -0,0 +1,110 @@
+<!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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/linear_layout_root"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="10dp" >
+
+ <view class="com.android.systemui.statusbar.policy.RemoteInputView$RemoteEditText"
+ android:id="@+id/remote_input_text"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:paddingTop="14dp"
+ android:paddingStart="4dp"
+ android:paddingBottom="16dp"
+ android:paddingEnd="12dp"
+ android:layout_gravity="start|center_vertical"
+ android:textAppearance="?android:attr/textAppearance"
+ android:textSize="16sp"
+ android:background="@null"
+ android:maxLines="4"
+ android:ellipsize="start"
+ android:inputType="textShortMessage|textMultiLine|textAutoCorrect|textCapSentences"
+ android:imeOptions="actionSend|flagNoExtractUi|flagNoFullscreen" />
+
+ <ImageButton
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ style="@android:style/MediaButton.Previous"
+ id="@+id/my_image_button_previous"
+ />
+
+ <ImageButton
+ id="@+id/my_image_button_next"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_alignParentRight="true"
+ android:layout_centerVertical="true"
+ style="@android:style/MediaButton.Next"
+ />
+
+ <LinearLayout
+ id="@+id/my_linear_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="10dp" >
+
+ <ImageButton
+ android:id="@+id/button3"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_centerVertical="true" />
+
+ <LinearLayout
+ id="@+id/my_inner_linear_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="10dp" >
+
+ <ImageButton
+ android:id="@+id/button5"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_alignParentBottom="true" />
+ </LinearLayout>
+
+ <ImageButton
+ android:id="@+id/button4"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/button2"
+ android:layout_centerHorizontal="true" />
+
+ <ImageButton
+ android:id="@+id/button6"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_above="@+id/button4"
+ android:layout_centerHorizontal="true" />
+
+ <ImageButton
+ android:id="@+id/button7"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignBottom="@+id/button"
+ android:layout_toEndOf="@+id/button"
+ android:layout_toRightOf="@+id/button" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/apct-tests/perftests/core/src/android/content/res/XmlBlockBenchmark.java b/apct-tests/perftests/core/src/android/content/res/XmlBlockBenchmark.java
new file mode 100644
index 000000000000..bdce902e4aef
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/content/res/XmlBlockBenchmark.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.res;
+
+import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_DOCUMENT;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import android.content.Context;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.perftests.core.R;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.function.Function;
+import java.util.function.Supplier;
+
+@LargeTest
+public class XmlBlockBenchmark {
+ private static final String TAG = "XmlBlockBenchmark";
+ private static final String NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android";
+
+ @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+ private XmlBlock.Parser mParser;
+
+ private void cleanCache() {
+ if (mParser != null) {
+ mParser.close();
+ }
+
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final Resources resources = context.getResources();
+ resources.getImpl().clearAllCaches();
+ Log.d(TAG, "cleanCache");
+ }
+
+ private XmlBlock.Parser getNewParser() {
+ cleanCache();
+
+ final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ final Resources resources = context.getResources();
+ return (XmlBlock.Parser) resources.getXml(R.layout.linear_layout_for_xmlblock_benchmark);
+ }
+
+ @Before
+ public void setUp() {
+ mParser = getNewParser();
+ }
+
+ @After
+ public void tearDown() {
+ cleanCache();
+ }
+
+ int safeNext() throws XmlPullParserException, IOException {
+ while (true) {
+ int parseState = mParser.next();
+ if (parseState == START_TAG) {
+ return parseState;
+ } else if (parseState == END_DOCUMENT) {
+ mParser = getNewParser();
+ }
+ }
+ }
+
+ @Test
+ public void throwNpeCausedByNullDocument() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ mParser.close();
+ while (state.keepRunning()) {
+ try {
+ mParser.getClassAttribute();
+ } catch (NullPointerException e) {
+ continue;
+ }
+ Assert.fail("It shouldn't be here!");
+ }
+ }
+
+ @Test
+ public void getNext() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ int parseState = mParser.next();
+ state.pauseTiming();
+ if (parseState == END_DOCUMENT) {
+ mParser = getNewParser();
+ }
+ state.resumeTiming();
+ }
+ }
+
+ private <T> void benchmarkTagFunction(BenchmarkState state, String name,
+ Supplier<T> measureTarget)
+ throws XmlPullParserException, IOException {
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ int parseState = safeNext();
+
+ if (parseState != END_DOCUMENT) {
+ final String tagName = mParser.getName();
+ state.resumeTiming();
+ final T value = measureTarget.get();
+ state.pauseTiming();
+ Log.d(TAG,
+ TextUtils.formatSimple("%s() in tag %s is %s", name, tagName, value));
+ }
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void getNamespace() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkTagFunction(state, "getNamespace", () -> mParser.getNamespace());
+ }
+
+ @Test
+ public void getName() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkTagFunction(state, "getName", () -> mParser.getName());
+ }
+
+ @Test
+ public void getText() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkTagFunction(state, "getText", () -> mParser.getText());
+ }
+
+ @Test
+ public void getLineNumber() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkTagFunction(state, "getLineNumber", () -> mParser.getLineNumber());
+ }
+
+ @Test
+ public void getAttributeCount() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkTagFunction(state, "getAttributeCount", () -> mParser.getAttributeCount());
+ }
+
+ private <T> void benchmarkAttributeFunction(BenchmarkState state, String name,
+ Function<Integer, T> measureTarget)
+ throws XmlPullParserException, IOException {
+ boolean needNext = true;
+ boolean needGetCount = false;
+ int attributeCount = 0;
+ int i = 0;
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ if (needNext) {
+ int parseState = safeNext();
+ if (parseState == START_TAG) {
+ needNext = false;
+ needGetCount = true;
+ }
+ }
+
+ if (needGetCount) {
+ attributeCount = mParser.getAttributeCount();
+ needGetCount = false;
+ i = 0;
+ }
+
+ if (i < attributeCount) {
+ final String tagName = mParser.getName();
+ final String attributeName = mParser.getAttributeName(i);
+ state.resumeTiming();
+ final T value = measureTarget.apply(i);
+ state.pauseTiming();
+ Log.d(TAG,
+ TextUtils.formatSimple("%s(%d:%s) in tag %s is %s", name, i, attributeName,
+ tagName, value));
+ i++;
+ }
+
+ if (i >= attributeCount) {
+ needNext = true;
+ }
+ state.resumeTiming();
+ }
+ }
+
+ @Test
+ public void getAttributeNamespace() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkAttributeFunction(state, "getAttributeNamespace",
+ attributeIndex -> mParser.getAttributeNamespace(attributeIndex));
+ }
+
+ @Test
+ public void getAttributeName() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkAttributeFunction(state, "getAttributeName",
+ attributeIndex -> mParser.getAttributeName(attributeIndex));
+ }
+
+ @Test
+ public void getAttributeNameResource() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkAttributeFunction(state, "getAttributeNameResource",
+ attributeIndex -> mParser.getAttributeNameResource(attributeIndex));
+ }
+
+ /**
+ * benchmark {@link android.content.res.XmlBlock#nativeGetAttributeDataType(long, int)} and
+ * {@link android.content.res.XmlBlock#nativeGetAttributeData(long, int)}
+ */
+ @Test
+ public void getAttributeDataXXX() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkAttributeFunction(state, "getAttributeDataXXX",
+ attributeIndex -> mParser.getAttributeValue(attributeIndex));
+ }
+
+ @Test
+ public void getSourceResId() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkTagFunction(state, "getSourceResId", () -> mParser.getSourceResId());
+ }
+
+ @Test
+ public void getIdAttribute() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkTagFunction(state, "getIdAttribute", () -> mParser.getIdAttribute());
+ }
+
+ @Test
+ public void getClassAttribute() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkTagFunction(state, "getClassAttribute", () -> mParser.getClassAttribute());
+ }
+
+ @Test
+ public void getStyleAttribute() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkTagFunction(state, "getStyleAttribute", () -> mParser.getStyleAttribute());
+ }
+
+ @Test
+ public void getAttributeIndex() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ benchmarkTagFunction(state, "getAttributeValue",
+ () -> mParser.getAttributeValue(NAMESPACE_ANDROID, "layout_width"));
+ }
+
+ @Test
+ public void parseOneXmlDocument() throws XmlPullParserException, IOException {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ state.pauseTiming();
+ mParser = getNewParser();
+ state.resumeTiming();
+
+ int parseState;
+ while ((parseState = mParser.next()) != END_DOCUMENT) {
+ if (parseState == START_DOCUMENT) {
+ state.pauseTiming();
+ Log.d(TAG, "parseOneXmlDocument: start document");
+ state.resumeTiming();
+ } else if (parseState == START_TAG) {
+ final String tagName = mParser.getName();
+ state.pauseTiming();
+ Log.d(TAG, TextUtils.formatSimple("parseOneXmlDocument: tag %s {[", tagName));
+ state.resumeTiming();
+ for (int i = 0, count = mParser.getAttributeCount(); i < count; i++) {
+ final String attributeName = mParser.getAttributeName(i);
+ final String attributeValue = mParser.getAttributeValue(i);
+
+ state.pauseTiming();
+ Log.d(TAG, TextUtils.formatSimple(
+ "parseOneXmlDocument: attribute %d {%s = %s},", i, attributeName,
+ attributeValue));
+ state.resumeTiming();
+ }
+ state.pauseTiming();
+ Log.d(TAG, "parseOneXmlDocument: ]");
+ state.resumeTiming();
+ } else if (parseState == END_TAG) {
+ state.pauseTiming();
+ Log.d(TAG, "parseOneXmlDocument: }");
+ state.resumeTiming();
+ } else {
+ final String text = mParser.getText();
+ state.pauseTiming();
+ Log.d(TAG, TextUtils.formatSimple(
+ "parseOneXmlDocument: parseState = %d, text = %s", parseState, text));
+ state.resumeTiming();
+ }
+ }
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
index adcd5710ef19..b995b0688970 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ExpensiveObjectsPerfTest.java
@@ -158,11 +158,10 @@ public class ExpensiveObjectsPerfTest {
}
@Test
- public void timeClonedSimpleDateFormat() {
- SimpleDateFormat sdf = new SimpleDateFormat();
+ public void timeNewSimpleDateFormat() {
BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
while (state.keepRunning()) {
- sdf.clone();
+ new SimpleDateFormat();
}
}
diff --git a/apct-tests/perftests/core/src/android/opengl/perftests/MatrixPerfTest.java b/apct-tests/perftests/core/src/android/opengl/perftests/MatrixPerfTest.java
new file mode 100644
index 000000000000..b29614817ee2
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/opengl/perftests/MatrixPerfTest.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.opengl.perftests;
+
+import android.opengl.Matrix;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.Random;
+
+@LargeTest
+public class MatrixPerfTest {
+ @Rule
+ public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+ @Rule
+ public float[] array = new float[48];
+
+ @Rule
+ public float[] bigArray = new float[16 * 1024 * 1024];
+
+
+ @Test
+ public void testMultiplyMM() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(array, 32, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMMLeftOverlapResult() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(array, 16, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMMRightOverlapResult() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(array, 0, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMMAllOverlap() {
+ Random rng = new Random();
+ for (int i = 0; i < 16; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(array, 0, array, 0, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMMOutputBigArray() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(bigArray, 1024, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMMAllBigArray() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ bigArray[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMM(bigArray, 1024, bigArray, 16, bigArray, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMV() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(array, 20, array, 4, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMVLeftOverlapResult() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(array, 4, array, 4, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMVRightOverlapResult() {
+ Random rng = new Random();
+ for (int i = 0; i < 32; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(array, 0, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMVAllOverlap() {
+ Random rng = new Random();
+ for (int i = 0; i < 16; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(array, 0, array, 0, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMVOutputBigArray() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(bigArray, 1024, array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testMultiplyMVAllBigArray() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ bigArray[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.multiplyMV(bigArray, 1024, bigArray, 16, bigArray, 0);
+ }
+ }
+
+ @Test
+ public void testTransposeM() {
+ Random rng = new Random();
+ for (int i = 0; i < 16; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.transposeM(array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testInvertM() {
+ // non-singular matrix
+ array[ 0] = 0.814f;
+ array[ 1] = 4.976f;
+ array[ 2] = -3.858f;
+ array[ 3] = 7.206f;
+ array[ 4] = 5.112f;
+ array[ 5] = -2.420f;
+ array[ 6] = 8.791f;
+ array[ 7] = 6.426f;
+ array[ 8] = 2.945f;
+ array[ 9] = 1.801f;
+ array[10] = -2.594f;
+ array[11] = 2.663f;
+ array[12] = -5.003f;
+ array[13] = -4.188f;
+ array[14] = 3.340f;
+ array[15] = -1.235f;
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.invertM(array, 16, array, 0);
+ }
+ }
+
+ @Test
+ public void testOrthoM() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.orthoM(array, 0, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
+ }
+ }
+
+ @Test
+ public void testFrustumM() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.frustumM(array, 0, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
+ }
+ }
+
+ @Test
+ public void testPerspectiveM() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.perspectiveM(array, 0, 45.0f, 1.0f, 1.0f, 100.0f);
+ }
+ }
+
+ @Test
+ public void testLength() {
+ Random rng = new Random();
+ for (int i = 0; i < 3; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.length(array[0], array[1], array[2]);
+ }
+ }
+
+ @Test
+ public void testSetIdentityM() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.setIdentityM(array, 0);
+ }
+ }
+
+ @Test
+ public void testScaleM() {
+ Random rng = new Random();
+ for (int i = 0; i < 19; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.scaleM(array, 19, array, 0, array[16], array[17], array[18]);
+ }
+ }
+
+ @Test
+ public void testScaleMInPlace() {
+ Random rng = new Random();
+ for (int i = 0; i < 19; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.scaleM(array, 0, array[16], array[17], array[18]);
+ }
+ }
+
+ @Test
+ public void testTranslateM() {
+ Random rng = new Random();
+ for (int i = 0; i < 19; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.translateM(array, 19, array, 0, array[16], array[17], array[18]);
+ }
+ }
+
+ @Test
+ public void testTranslateMInPlace() {
+ Random rng = new Random();
+ for (int i = 0; i < 19; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.translateM(array, 0, array[16], array[17], array[18]);
+ }
+ }
+
+ @Test
+ public void testRotateM() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.rotateM(array, 20, array, 0, array[16], array[17], array[18], array[19]);
+ }
+ }
+
+ @Test
+ public void testRotateMInPlace() {
+ Random rng = new Random();
+ for (int i = 0; i < 20; i++) {
+ array[i] = rng.nextFloat();
+ }
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.rotateM(array, 0, array[16], array[17], array[18], array[19]);
+ }
+ }
+
+ @Test
+ public void testSetRotateM() {
+ Random rng = new Random();
+ array[0] = rng.nextFloat() * 90.0f;
+ array[1] = rng.nextFloat() + 0.5f;
+ array[2] = rng.nextFloat() + 0.5f;
+ array[3] = rng.nextFloat() + 0.5f;
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.setRotateM(array, 4, array[0], array[1], array[2], array[3]);
+ }
+ }
+
+ @Test
+ public void testSetRotateEulerM() {
+ Random rng = new Random();
+ array[0] = rng.nextFloat() * 90.0f;
+ array[1] = rng.nextFloat() * 90.0f;
+ array[2] = rng.nextFloat() * 90.0f;
+
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.setRotateEulerM(array, 3, array[0], array[1], array[2]);
+ }
+ }
+
+ @Test
+ public void testSetLookAtM() {
+ final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ Matrix.setLookAtM(array, 9,
+ 1.0f, 0.0f, 0.0f,
+ 1.0f, 0.0f, 1.0f,
+ 0.0f, 1.0f, 0.0f);
+ }
+ }
+}
diff --git a/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java b/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java
index 4f0d1084b767..c636be9a096d 100644
--- a/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java
+++ b/apct-tests/perftests/core/src/android/perftests/SystemPerfTest.java
@@ -22,8 +22,10 @@ import android.perftests.utils.PerfStatusReporter;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
+import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
+import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -106,6 +108,93 @@ public class SystemPerfTest {
}
}
+ /** this result should be compared with {@link #testJniCriticalNativeAccess()}. */
+ @Test
+ public void testJniFastNativeAccess() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ jintFastNativeAccess(50);
+ }
+ }
+
+ /**
+ * This result should be compared with {@link #testJniFastNativeAccess()}.
+ *
+ * <p>In theory, the result should be better than {@link #testJniFastNativeAccess()}.
+ */
+ @Test
+ public void testJniCriticalNativeAccess() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ jintCriticalNativeAccess(50);
+ }
+ }
+
+ /** The result should be compared with {@link #testJniCriticalNativeCheckNullPointer()}. */
+ @Test
+ public void testJniFastNativeCheckNullPointer() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ final int echoNumber = jintFastNativeCheckNullPointer(50);
+ }
+ }
+
+ /**
+ * The result should be compared with {@link #testJniFastNativeCheckNullPointer()}.
+ *
+ * <p>CriticalNative can't reference JavaEnv in native layer. It means it should check the null
+ * pointer in java layer. It's a comparison between native layer and java layer.
+ */
+ @Test
+ public void testJniCriticalNativeCheckNullPointer() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ final int echoNumber = jintCriticalNativeCheckNullPointer(50);
+ if (echoNumber == -1) {
+ Assert.fail("It shouldn't be here");
+ }
+ }
+ }
+
+ /**
+ * The result should be compared with {@link #testJniCriticalNativeThrowNullPointerException()}.
+ */
+ @Test
+ public void testJniFastNativeThrowNullPointerException() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ try {
+ jintFastNativeCheckNullPointer(0);
+ } catch (NullPointerException e) {
+ continue;
+ }
+ Assert.fail("It shouldn't be here");
+ }
+ }
+
+ /**
+ * The result should be compared with {@link #testJniFastNativeThrowNullPointerException()}.
+ *
+ * <p>CriticalNative can't reference JavaEnv in native layer. It means it should check the null
+ * pointer in java layer. It's a comparison between native layer and java layer.
+ */
+ @Test
+ public void testJniCriticalNativeThrowNullPointerException() {
+ BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+ while (state.keepRunning()) {
+ try {
+ final int echoNumber = jintCriticalNativeCheckNullPointer(0);
+ if (echoNumber == -1) {
+ throw new NullPointerException();
+ }
+ } catch (NullPointerException e) {
+ continue;
+ }
+ Assert.fail("It shouldn't be here");
+ }
+ }
+
+ // ----------- @FastNative ------------------
@FastNative
private static native void jintarrayArgumentNoop(int[] array, int length);
@FastNative
@@ -114,4 +203,17 @@ public class SystemPerfTest {
private static native int jintarrayCriticalAccess(int[] array, int index);
@FastNative
private static native int jintarrayBasicAccess(int[] array, int index);
+
+ @FastNative
+ private static native int jintFastNativeAccess(int echoNumber);
+
+ @FastNative
+ private static native int jintFastNativeCheckNullPointer(int echoNumber);
+
+ // ----------- @CriticalNative ------------------
+ @CriticalNative
+ private static native int jintCriticalNativeAccess(int echoNumber);
+
+ @CriticalNative
+ private static native int jintCriticalNativeCheckNullPointer(int echoNumber);
}
diff --git a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
index 3452f587e3ff..6d1e6d0cbd73 100644
--- a/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
+++ b/apct-tests/perftests/packagemanager/src/android/os/PackageParsingPerfTest.kt
@@ -28,16 +28,16 @@ import androidx.test.filters.LargeTest
import com.android.internal.util.ConcurrentUtils
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.pkg.parsing.ParsingPackageUtils
+import java.io.File
+import java.io.FileOutputStream
+import java.util.concurrent.ArrayBlockingQueue
+import java.util.concurrent.TimeUnit
import libcore.io.IoUtils
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.junit.runners.Parameterized
-import java.io.File
-import java.io.FileOutputStream
-import java.util.concurrent.ArrayBlockingQueue
-import java.util.concurrent.TimeUnit
@LargeTest
@RunWith(Parameterized::class)
@@ -180,8 +180,8 @@ public class PackageParsingPerfTest {
protected abstract fun parseImpl(file: File): PackageType
}
- class ParallelParser1(private val cacher: PackageCacher1? = null)
- : ParallelParser<PackageParser.Package>(cacher) {
+ class ParallelParser1(private val cacher: PackageCacher1? = null) :
+ ParallelParser<PackageParser.Package>(cacher) {
val parser = PackageParser().apply {
setCallback { true }
}
@@ -189,8 +189,8 @@ public class PackageParsingPerfTest {
override fun parseImpl(file: File) = parser.parsePackage(file, 0, cacher != null)
}
- class ParallelParser2(cacher: PackageCacher2? = null)
- : ParallelParser<PackageImpl>(cacher) {
+ class ParallelParser2(cacher: PackageCacher2? = null) :
+ ParallelParser<PackageImpl>(cacher) {
val input = ThreadLocal.withInitial {
// For testing, just disable enforcement to avoid hooking up to compat framework
ParseTypeImpl(ParseInput.Callback { _, _, _ -> false })
@@ -218,7 +218,7 @@ public class PackageParsingPerfTest {
})
override fun parseImpl(file: File) =
- parser.parsePackage(input.get()!!.reset(), file, 0, null).result
+ parser.parsePackage(input.get()!!.reset(), file, 0).result
as PackageImpl
}
diff --git a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
index c3fc7b16ebdf..6af24be3a372 100644
--- a/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
+++ b/apex/jobscheduler/framework/java/android/app/tare/EconomyManager.java
@@ -227,6 +227,9 @@ public class EconomyManager {
public static final String KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP =
"js_min_satiated_balance_other_app";
/** @hide */
+ public static final String KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER =
+ "js_min_satiated_balance_increment_updater";
+ /** @hide */
public static final String KEY_JS_MAX_SATIATED_BALANCE =
"js_max_satiated_balance";
/** @hide */
@@ -235,6 +238,15 @@ public class EconomyManager {
public static final String KEY_JS_HARD_CONSUMPTION_LIMIT = "js_hard_consumption_limit";
// TODO: Add JobScheduler modifier keys
/** @hide */
+ public static final String KEY_JS_REWARD_APP_INSTALL_INSTANT =
+ "js_reward_app_install_instant";
+ /** @hide */
+ public static final String KEY_JS_REWARD_APP_INSTALL_ONGOING =
+ "js_reward_app_install_ongoing";
+ /** @hide */
+ public static final String KEY_JS_REWARD_APP_INSTALL_MAX =
+ "js_reward_app_install_max";
+ /** @hide */
public static final String KEY_JS_REWARD_TOP_ACTIVITY_INSTANT =
"js_reward_top_activity_instant";
/** @hide */
@@ -463,6 +475,12 @@ public class EconomyManager {
public static final long DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES = arcToCake(250_000);
// TODO: add JobScheduler modifier default values
/** @hide */
+ public static final long DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES = arcToCake(408);
+ /** @hide */
+ public static final long DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES = arcToCake(0);
+ /** @hide */
+ public static final long DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES = arcToCake(4000);
+ /** @hide */
public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_INSTANT_CAKES = arcToCake(0);
/** @hide */
public static final long DEFAULT_JS_REWARD_TOP_ACTIVITY_ONGOING_CAKES = CAKE_IN_ARC / 2;
@@ -494,6 +512,15 @@ public class EconomyManager {
public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_ONGOING_CAKES = arcToCake(0);
/** @hide */
public static final long DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES = arcToCake(5000);
+ /**
+ * How many credits to increase the updating app's min satiated balance by for each app that it
+ * is responsible for updating.
+ * @hide
+ */
+ public static final long DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES =
+ // Research indicates that the median time between popular app updates is 13-14 days,
+ // so adjust by 14 to amortize over that time.
+ DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES / 14;
/** @hide */
public static final long DEFAULT_JS_ACTION_JOB_MAX_START_CTP_CAKES = arcToCake(3);
/** @hide */
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 40d1b4c9b267..bd475e9dd734 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -37,6 +37,7 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
+import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
@@ -993,69 +994,69 @@ public class DeviceIdleController extends SystemService
"pre_idle_factor_short";
private static final String KEY_USE_WINDOW_ALARMS = "use_window_alarms";
- private static final long DEFAULT_FLEX_TIME_SHORT =
+ private long mDefaultFlexTimeShort =
!COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
- private static final long DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT =
+ private long mDefaultLightIdleAfterInactiveTimeout =
!COMPRESS_TIME ? 4 * 60 * 1000L : 30 * 1000L;
- private static final long DEFAULT_LIGHT_IDLE_TIMEOUT =
+ private long mDefaultLightIdleTimeout =
!COMPRESS_TIME ? 5 * 60 * 1000L : 15 * 1000L;
- private static final float DEFAULT_LIGHT_IDLE_FACTOR = 2f;
- private static final long DEFAULT_LIGHT_MAX_IDLE_TIMEOUT =
+ private float mDefaultLightIdleFactor = 2f;
+ private long mDefaultLightMaxIdleTimeout =
!COMPRESS_TIME ? 15 * 60 * 1000L : 60 * 1000L;
- private static final long DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET =
+ private long mDefaultLightIdleMaintenanceMinBudget =
!COMPRESS_TIME ? 1 * 60 * 1000L : 15 * 1000L;
- private static final long DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET =
+ private long mDefaultLightIdleMaintenanceMaxBudget =
!COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L;
- private static final long DEFAULT_MIN_LIGHT_MAINTENANCE_TIME =
+ private long mDefaultMinLightMaintenanceTime =
!COMPRESS_TIME ? 5 * 1000L : 1 * 1000L;
- private static final long DEFAULT_MIN_DEEP_MAINTENANCE_TIME =
+ private long mDefaultMinDeepMaintenanceTime =
!COMPRESS_TIME ? 30 * 1000L : 5 * 1000L;
- private static final long DEFAULT_INACTIVE_TIMEOUT =
+ private long mDefaultInactiveTimeout =
(30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
private static final long DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY =
(15 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
- private static final long DEFAULT_SENSING_TIMEOUT =
+ private long mDefaultSensingTimeout =
!COMPRESS_TIME ? 4 * 60 * 1000L : 60 * 1000L;
- private static final long DEFAULT_LOCATING_TIMEOUT =
+ private long mDefaultLocatingTimeout =
!COMPRESS_TIME ? 30 * 1000L : 15 * 1000L;
- private static final float DEFAULT_LOCATION_ACCURACY = 20f;
- private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT =
+ private float mDefaultLocationAccuracy = 20f;
+ private long mDefaultMotionInactiveTimeout =
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
- private static final long DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX =
+ private long mDefaultMotionInactiveTimeoutFlex =
!COMPRESS_TIME ? 60 * 1000L : 5 * 1000L;
- private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT =
+ private long mDefaultIdleAfterInactiveTimeout =
(30 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
private static final long DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY =
(15 * 60 * 1000L) / (!COMPRESS_TIME ? 1 : 10);
- private static final long DEFAULT_IDLE_PENDING_TIMEOUT =
+ private long mDefaultIdlePendingTimeout =
!COMPRESS_TIME ? 5 * 60 * 1000L : 30 * 1000L;
- private static final long DEFAULT_MAX_IDLE_PENDING_TIMEOUT =
+ private long mDefaultMaxIdlePendingTimeout =
!COMPRESS_TIME ? 10 * 60 * 1000L : 60 * 1000L;
- private static final float DEFAULT_IDLE_PENDING_FACTOR = 2f;
- private static final long DEFAULT_QUICK_DOZE_DELAY_TIMEOUT =
+ private float mDefaultIdlePendingFactor = 2f;
+ private long mDefaultQuickDozeDelayTimeout =
!COMPRESS_TIME ? 60 * 1000L : 15 * 1000L;
- private static final long DEFAULT_IDLE_TIMEOUT =
+ private long mDefaultIdleTimeout =
!COMPRESS_TIME ? 60 * 60 * 1000L : 6 * 60 * 1000L;
- private static final long DEFAULT_MAX_IDLE_TIMEOUT =
+ private long mDefaultMaxIdleTimeout =
!COMPRESS_TIME ? 6 * 60 * 60 * 1000L : 30 * 60 * 1000L;
- private static final float DEFAULT_IDLE_FACTOR = 2f;
- private static final long DEFAULT_MIN_TIME_TO_ALARM =
+ private float mDefaultIdleFactor = 2f;
+ private long mDefaultMinTimeToAlarm =
!COMPRESS_TIME ? 30 * 60 * 1000L : 6 * 60 * 1000L;
- private static final long DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS = 5 * 60 * 1000L;
- private static final long DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS = 60 * 1000L;
- private static final long DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS = 20 * 1000L;
- private static final long DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS = 30 * 1000L;
- private static final boolean DEFAULT_WAIT_FOR_UNLOCK = true;
- private static final float DEFAULT_PRE_IDLE_FACTOR_LONG = 1.67f;
- private static final float DEFAULT_PRE_IDLE_FACTOR_SHORT = .33f;
- private static final boolean DEFAULT_USE_WINDOW_ALARMS = true;
+ private long mDefaultMaxTempAppAllowlistDurationMs = 5 * 60 * 1000L;
+ private long mDefaultMmsTempAppAllowlistDurationMs = 60 * 1000L;
+ private long mDefaultSmsTempAppAllowlistDurationMs = 20 * 1000L;
+ private long mDefaultNotificationAllowlistDurationMs = 30 * 1000L;
+ private boolean mDefaultWaitForUnlock = true;
+ private float mDefaultPreIdleFactorLong = 1.67f;
+ private float mDefaultPreIdleFactorShort = .33f;
+ private boolean mDefaultUseWindowAlarms = true;
/**
* A somewhat short alarm window size that we will tolerate for various alarm timings.
*
* @see #KEY_FLEX_TIME_SHORT
*/
- public long FLEX_TIME_SHORT = DEFAULT_FLEX_TIME_SHORT;
+ public long FLEX_TIME_SHORT = mDefaultFlexTimeShort;
/**
* This is the time, after becoming inactive, that we go in to the first
@@ -1063,28 +1064,28 @@ public class DeviceIdleController extends SystemService
*
* @see #KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT
*/
- public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT;
+ public long LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultLightIdleAfterInactiveTimeout;
/**
* This is the initial time that we will run in light idle maintenance mode.
*
* @see #KEY_LIGHT_IDLE_TIMEOUT
*/
- public long LIGHT_IDLE_TIMEOUT = DEFAULT_LIGHT_IDLE_TIMEOUT;
+ public long LIGHT_IDLE_TIMEOUT = mDefaultLightIdleTimeout;
/**
* Scaling factor to apply to the light idle mode time each time we complete a cycle.
*
* @see #KEY_LIGHT_IDLE_FACTOR
*/
- public float LIGHT_IDLE_FACTOR = DEFAULT_LIGHT_IDLE_FACTOR;
+ public float LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor;
/**
* This is the maximum time we will stay in light idle mode.
*
* @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
*/
- public long LIGHT_MAX_IDLE_TIMEOUT = DEFAULT_LIGHT_MAX_IDLE_TIMEOUT;
+ public long LIGHT_MAX_IDLE_TIMEOUT = mDefaultLightMaxIdleTimeout;
/**
* This is the minimum amount of time we want to make available for maintenance mode
@@ -1093,7 +1094,7 @@ public class DeviceIdleController extends SystemService
*
* @see #KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET
*/
- public long LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET;
+ public long LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mDefaultLightIdleMaintenanceMinBudget;
/**
* This is the maximum amount of time we want to make available for maintenance mode
@@ -1104,7 +1105,7 @@ public class DeviceIdleController extends SystemService
*
* @see #KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET
*/
- public long LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET;
+ public long LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mDefaultLightIdleMaintenanceMaxBudget;
/**
* This is the minimum amount of time that we will stay in maintenance mode after
@@ -1115,7 +1116,7 @@ public class DeviceIdleController extends SystemService
*
* @see #KEY_MIN_LIGHT_MAINTENANCE_TIME
*/
- public long MIN_LIGHT_MAINTENANCE_TIME = DEFAULT_MIN_LIGHT_MAINTENANCE_TIME;
+ public long MIN_LIGHT_MAINTENANCE_TIME = mDefaultMinLightMaintenanceTime;
/**
* This is the minimum amount of time that we will stay in maintenance mode after
@@ -1125,7 +1126,7 @@ public class DeviceIdleController extends SystemService
* mode immediately.
* @see #KEY_MIN_DEEP_MAINTENANCE_TIME
*/
- public long MIN_DEEP_MAINTENANCE_TIME = DEFAULT_MIN_DEEP_MAINTENANCE_TIME;
+ public long MIN_DEEP_MAINTENANCE_TIME = mDefaultMinDeepMaintenanceTime;
/**
* This is the time, after becoming inactive, at which we start looking at the
@@ -1134,7 +1135,7 @@ public class DeviceIdleController extends SystemService
* the motion sensor whenever the screen is off.
* @see #KEY_INACTIVE_TIMEOUT
*/
- public long INACTIVE_TIMEOUT = DEFAULT_INACTIVE_TIMEOUT;
+ public long INACTIVE_TIMEOUT = mDefaultInactiveTimeout;
/**
* If we don't receive a callback from AnyMotion in this amount of time +
@@ -1143,14 +1144,14 @@ public class DeviceIdleController extends SystemService
* will be ignored.
* @see #KEY_SENSING_TIMEOUT
*/
- public long SENSING_TIMEOUT = DEFAULT_SENSING_TIMEOUT;
+ public long SENSING_TIMEOUT = mDefaultSensingTimeout;
/**
* This is how long we will wait to try to get a good location fix before going in to
* idle mode.
* @see #KEY_LOCATING_TIMEOUT
*/
- public long LOCATING_TIMEOUT = DEFAULT_LOCATING_TIMEOUT;
+ public long LOCATING_TIMEOUT = mDefaultLocatingTimeout;
/**
* The desired maximum accuracy (in meters) we consider the location to be good enough to go
@@ -1158,7 +1159,7 @@ public class DeviceIdleController extends SystemService
* {@link #LOCATING_TIMEOUT} expires.
* @see #KEY_LOCATION_ACCURACY
*/
- public float LOCATION_ACCURACY = DEFAULT_LOCATION_ACCURACY;
+ public float LOCATION_ACCURACY = mDefaultLocationAccuracy;
/**
* This is the time, after seeing motion, that we wait after becoming inactive from
@@ -1166,14 +1167,14 @@ public class DeviceIdleController extends SystemService
*
* @see #KEY_MOTION_INACTIVE_TIMEOUT
*/
- public long MOTION_INACTIVE_TIMEOUT = DEFAULT_MOTION_INACTIVE_TIMEOUT;
+ public long MOTION_INACTIVE_TIMEOUT = mDefaultMotionInactiveTimeout;
/**
* This is the alarm window size we will tolerate for motion detection timings.
*
* @see #KEY_MOTION_INACTIVE_TIMEOUT_FLEX
*/
- public long MOTION_INACTIVE_TIMEOUT_FLEX = DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX;
+ public long MOTION_INACTIVE_TIMEOUT_FLEX = mDefaultMotionInactiveTimeoutFlex;
/**
* This is the time, after the inactive timeout elapses, that we will wait looking
@@ -1181,7 +1182,7 @@ public class DeviceIdleController extends SystemService
*
* @see #KEY_IDLE_AFTER_INACTIVE_TIMEOUT
*/
- public long IDLE_AFTER_INACTIVE_TIMEOUT = DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
+ public long IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultIdleAfterInactiveTimeout;
/**
* This is the initial time, after being idle, that we will allow ourself to be back
@@ -1189,20 +1190,20 @@ public class DeviceIdleController extends SystemService
* idle.
* @see #KEY_IDLE_PENDING_TIMEOUT
*/
- public long IDLE_PENDING_TIMEOUT = DEFAULT_IDLE_PENDING_TIMEOUT;
+ public long IDLE_PENDING_TIMEOUT = mDefaultIdlePendingTimeout;
/**
* Maximum pending idle timeout (time spent running) we will be allowed to use.
* @see #KEY_MAX_IDLE_PENDING_TIMEOUT
*/
- public long MAX_IDLE_PENDING_TIMEOUT = DEFAULT_MAX_IDLE_PENDING_TIMEOUT;
+ public long MAX_IDLE_PENDING_TIMEOUT = mDefaultMaxIdlePendingTimeout;
/**
* Scaling factor to apply to current pending idle timeout each time we cycle through
* that state.
* @see #KEY_IDLE_PENDING_FACTOR
*/
- public float IDLE_PENDING_FACTOR = DEFAULT_IDLE_PENDING_FACTOR;
+ public float IDLE_PENDING_FACTOR = mDefaultIdlePendingFactor;
/**
* This is amount of time we will wait from the point where we go into
@@ -1210,33 +1211,33 @@ public class DeviceIdleController extends SystemService
* and other current activity to finish.
* @see #KEY_QUICK_DOZE_DELAY_TIMEOUT
*/
- public long QUICK_DOZE_DELAY_TIMEOUT = DEFAULT_QUICK_DOZE_DELAY_TIMEOUT;
+ public long QUICK_DOZE_DELAY_TIMEOUT = mDefaultQuickDozeDelayTimeout;
/**
* This is the initial time that we want to sit in the idle state before waking up
* again to return to pending idle and allowing normal work to run.
* @see #KEY_IDLE_TIMEOUT
*/
- public long IDLE_TIMEOUT = DEFAULT_IDLE_TIMEOUT;
+ public long IDLE_TIMEOUT = mDefaultIdleTimeout;
/**
* Maximum idle duration we will be allowed to use.
* @see #KEY_MAX_IDLE_TIMEOUT
*/
- public long MAX_IDLE_TIMEOUT = DEFAULT_MAX_IDLE_TIMEOUT;
+ public long MAX_IDLE_TIMEOUT = mDefaultMaxIdleTimeout;
/**
* Scaling factor to apply to current idle timeout each time we cycle through that state.
* @see #KEY_IDLE_FACTOR
*/
- public float IDLE_FACTOR = DEFAULT_IDLE_FACTOR;
+ public float IDLE_FACTOR = mDefaultIdleFactor;
/**
* This is the minimum time we will allow until the next upcoming alarm for us to
* actually go in to idle mode.
* @see #KEY_MIN_TIME_TO_ALARM
*/
- public long MIN_TIME_TO_ALARM = DEFAULT_MIN_TIME_TO_ALARM;
+ public long MIN_TIME_TO_ALARM = mDefaultMinTimeToAlarm;
/**
* Max amount of time to temporarily whitelist an app when it receives a high priority
@@ -1244,48 +1245,49 @@ public class DeviceIdleController extends SystemService
*
* @see #KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS
*/
- public long MAX_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS;
+ public long MAX_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMaxTempAppAllowlistDurationMs;
/**
* Amount of time we would like to whitelist an app that is receiving an MMS.
* @see #KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS
*/
- public long MMS_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS;
+ public long MMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMmsTempAppAllowlistDurationMs;
/**
* Amount of time we would like to whitelist an app that is receiving an SMS.
* @see #KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS
*/
- public long SMS_TEMP_APP_ALLOWLIST_DURATION_MS = DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS;
+ public long SMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultSmsTempAppAllowlistDurationMs;
/**
* Amount of time we would like to whitelist an app that is handling a
* {@link android.app.PendingIntent} triggered by a {@link android.app.Notification}.
* @see #KEY_NOTIFICATION_ALLOWLIST_DURATION_MS
*/
- public long NOTIFICATION_ALLOWLIST_DURATION_MS = DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS;
+ public long NOTIFICATION_ALLOWLIST_DURATION_MS = mDefaultNotificationAllowlistDurationMs;
/**
* Pre idle time factor use to make idle delay longer
*/
- public float PRE_IDLE_FACTOR_LONG = DEFAULT_PRE_IDLE_FACTOR_LONG;
+ public float PRE_IDLE_FACTOR_LONG = mDefaultPreIdleFactorLong;
/**
* Pre idle time factor use to make idle delay shorter
*/
- public float PRE_IDLE_FACTOR_SHORT = DEFAULT_PRE_IDLE_FACTOR_SHORT;
+ public float PRE_IDLE_FACTOR_SHORT = mDefaultPreIdleFactorShort;
- public boolean WAIT_FOR_UNLOCK = DEFAULT_WAIT_FOR_UNLOCK;
+ public boolean WAIT_FOR_UNLOCK = mDefaultWaitForUnlock;
/**
* Whether to use window alarms. True to use window alarms (call AlarmManager.setWindow()).
* False to use the legacy inexact alarms (call AlarmManager.set()).
*/
- public boolean USE_WINDOW_ALARMS = DEFAULT_USE_WINDOW_ALARMS;
+ public boolean USE_WINDOW_ALARMS = mDefaultUseWindowAlarms;
private final boolean mSmallBatteryDevice;
public Constants() {
+ initDefault();
mSmallBatteryDevice = ActivityManager.isSmallBatteryDevice();
if (mSmallBatteryDevice) {
INACTIVE_TIMEOUT = DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY;
@@ -1297,6 +1299,132 @@ public class DeviceIdleController extends SystemService
onPropertiesChanged(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_DEVICE_IDLE));
}
+ private void initDefault() {
+ final Resources res = getContext().getResources();
+
+ mDefaultFlexTimeShort = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_flex_time_short_ms),
+ mDefaultFlexTimeShort);
+ mDefaultLightIdleAfterInactiveTimeout = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_light_after_inactive_to_ms),
+ mDefaultLightIdleAfterInactiveTimeout);
+ mDefaultLightIdleTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_light_idle_to_ms),
+ mDefaultLightIdleTimeout);
+ mDefaultLightIdleFactor = res.getFloat(
+ com.android.internal.R.integer.device_idle_light_idle_factor);
+ mDefaultLightMaxIdleTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_light_max_idle_to_ms),
+ mDefaultLightMaxIdleTimeout);
+ mDefaultLightIdleMaintenanceMinBudget = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_light_idle_maintenance_min_budget_ms
+ ), mDefaultLightIdleMaintenanceMinBudget);
+ mDefaultLightIdleMaintenanceMaxBudget = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_light_idle_maintenance_max_budget_ms
+ ), mDefaultLightIdleMaintenanceMaxBudget);
+ mDefaultMinLightMaintenanceTime = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_min_light_maintenance_time_ms),
+ mDefaultMinLightMaintenanceTime);
+ mDefaultMinDeepMaintenanceTime = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_min_deep_maintenance_time_ms),
+ mDefaultMinDeepMaintenanceTime);
+ mDefaultInactiveTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_inactive_to_ms),
+ mDefaultInactiveTimeout);
+ mDefaultSensingTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_sensing_to_ms),
+ mDefaultSensingTimeout);
+ mDefaultLocatingTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_locating_to_ms),
+ mDefaultLocatingTimeout);
+ mDefaultLocationAccuracy = res.getFloat(
+ com.android.internal.R.integer.device_idle_location_accuracy);
+ mDefaultMotionInactiveTimeout = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_motion_inactive_to_ms),
+ mDefaultMotionInactiveTimeout);
+ mDefaultMotionInactiveTimeoutFlex = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_motion_inactive_to_flex_ms),
+ mDefaultMotionInactiveTimeoutFlex);
+ mDefaultIdleAfterInactiveTimeout = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_idle_after_inactive_to_ms),
+ mDefaultIdleAfterInactiveTimeout);
+ mDefaultIdlePendingTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_idle_pending_to_ms),
+ mDefaultIdlePendingTimeout);
+ mDefaultMaxIdlePendingTimeout = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_max_idle_pending_to_ms),
+ mDefaultMaxIdlePendingTimeout);
+ mDefaultIdlePendingFactor = res.getFloat(
+ com.android.internal.R.integer.device_idle_idle_pending_factor);
+ mDefaultQuickDozeDelayTimeout = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_quick_doze_delay_to_ms),
+ mDefaultQuickDozeDelayTimeout);
+ mDefaultIdleTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_idle_to_ms),
+ mDefaultIdleTimeout);
+ mDefaultMaxIdleTimeout = getTimeout(
+ res.getInteger(com.android.internal.R.integer.device_idle_max_idle_to_ms),
+ mDefaultMaxIdleTimeout);
+ mDefaultIdleFactor = res.getFloat(
+ com.android.internal.R.integer.device_idle_idle_factor);
+ mDefaultMinTimeToAlarm = getTimeout(res.getInteger(
+ com.android.internal.R.integer.device_idle_min_time_to_alarm_ms),
+ mDefaultMinTimeToAlarm);
+ mDefaultMaxTempAppAllowlistDurationMs = res.getInteger(
+ com.android.internal.R.integer.device_idle_max_temp_app_allowlist_duration_ms);
+ mDefaultMmsTempAppAllowlistDurationMs = res.getInteger(
+ com.android.internal.R.integer.device_idle_mms_temp_app_allowlist_duration_ms);
+ mDefaultSmsTempAppAllowlistDurationMs = res.getInteger(
+ com.android.internal.R.integer.device_idle_sms_temp_app_allowlist_duration_ms);
+ mDefaultNotificationAllowlistDurationMs = res.getInteger(
+ com.android.internal.R.integer.device_idle_notification_allowlist_duration_ms);
+ mDefaultWaitForUnlock = res.getBoolean(
+ com.android.internal.R.bool.device_idle_wait_for_unlock);
+ mDefaultPreIdleFactorLong = res.getFloat(
+ com.android.internal.R.integer.device_idle_pre_idle_factor_long);
+ mDefaultPreIdleFactorShort = res.getFloat(
+ com.android.internal.R.integer.device_idle_pre_idle_factor_short);
+ mDefaultUseWindowAlarms = res.getBoolean(
+ com.android.internal.R.bool.device_idle_use_window_alarms);
+
+ FLEX_TIME_SHORT = mDefaultFlexTimeShort;
+ LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultLightIdleAfterInactiveTimeout;
+ LIGHT_IDLE_TIMEOUT = mDefaultLightIdleTimeout;
+ LIGHT_IDLE_FACTOR = mDefaultLightIdleFactor;
+ LIGHT_MAX_IDLE_TIMEOUT = mDefaultLightMaxIdleTimeout;
+ LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = mDefaultLightIdleMaintenanceMinBudget;
+ LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = mDefaultLightIdleMaintenanceMaxBudget;
+ MIN_LIGHT_MAINTENANCE_TIME = mDefaultMinLightMaintenanceTime;
+ MIN_DEEP_MAINTENANCE_TIME = mDefaultMinDeepMaintenanceTime;
+ INACTIVE_TIMEOUT = mDefaultInactiveTimeout;
+ SENSING_TIMEOUT = mDefaultSensingTimeout;
+ LOCATING_TIMEOUT = mDefaultLocatingTimeout;
+ LOCATION_ACCURACY = mDefaultLocationAccuracy;
+ MOTION_INACTIVE_TIMEOUT = mDefaultMotionInactiveTimeout;
+ MOTION_INACTIVE_TIMEOUT_FLEX = mDefaultMotionInactiveTimeoutFlex;
+ IDLE_AFTER_INACTIVE_TIMEOUT = mDefaultIdleAfterInactiveTimeout;
+ IDLE_PENDING_TIMEOUT = mDefaultIdlePendingTimeout;
+ MAX_IDLE_PENDING_TIMEOUT = mDefaultMaxIdlePendingTimeout;
+ IDLE_PENDING_FACTOR = mDefaultIdlePendingFactor;
+ QUICK_DOZE_DELAY_TIMEOUT = mDefaultQuickDozeDelayTimeout;
+ IDLE_TIMEOUT = mDefaultIdleTimeout;
+ MAX_IDLE_TIMEOUT = mDefaultMaxIdleTimeout;
+ IDLE_FACTOR = mDefaultIdleFactor;
+ MIN_TIME_TO_ALARM = mDefaultMinTimeToAlarm;
+ MAX_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMaxTempAppAllowlistDurationMs;
+ MMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultMmsTempAppAllowlistDurationMs;
+ SMS_TEMP_APP_ALLOWLIST_DURATION_MS = mDefaultSmsTempAppAllowlistDurationMs;
+ NOTIFICATION_ALLOWLIST_DURATION_MS = mDefaultNotificationAllowlistDurationMs;
+ WAIT_FOR_UNLOCK = mDefaultWaitForUnlock;
+ PRE_IDLE_FACTOR_LONG = mDefaultPreIdleFactorLong;
+ PRE_IDLE_FACTOR_SHORT = mDefaultPreIdleFactorShort;
+ USE_WINDOW_ALARMS = mDefaultUseWindowAlarms;
+ }
+
+ private long getTimeout(long defTimeout, long compTimeout) {
+ return (!COMPRESS_TIME || defTimeout < compTimeout) ? defTimeout : compTimeout;
+ }
+
@Override
public void onPropertiesChanged(DeviceConfig.Properties properties) {
@@ -1308,147 +1436,147 @@ public class DeviceIdleController extends SystemService
switch (name) {
case KEY_FLEX_TIME_SHORT:
FLEX_TIME_SHORT = properties.getLong(
- KEY_FLEX_TIME_SHORT, DEFAULT_FLEX_TIME_SHORT);
+ KEY_FLEX_TIME_SHORT, mDefaultFlexTimeShort);
break;
case KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT:
LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
KEY_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT,
- DEFAULT_LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT);
+ mDefaultLightIdleAfterInactiveTimeout);
break;
case KEY_LIGHT_IDLE_TIMEOUT:
LIGHT_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_IDLE_TIMEOUT, DEFAULT_LIGHT_IDLE_TIMEOUT);
+ KEY_LIGHT_IDLE_TIMEOUT, mDefaultLightIdleTimeout);
break;
case KEY_LIGHT_IDLE_FACTOR:
LIGHT_IDLE_FACTOR = Math.max(1, properties.getFloat(
- KEY_LIGHT_IDLE_FACTOR, DEFAULT_LIGHT_IDLE_FACTOR));
+ KEY_LIGHT_IDLE_FACTOR, mDefaultLightIdleFactor));
break;
case KEY_LIGHT_MAX_IDLE_TIMEOUT:
LIGHT_MAX_IDLE_TIMEOUT = properties.getLong(
- KEY_LIGHT_MAX_IDLE_TIMEOUT, DEFAULT_LIGHT_MAX_IDLE_TIMEOUT);
+ KEY_LIGHT_MAX_IDLE_TIMEOUT, mDefaultLightMaxIdleTimeout);
break;
case KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET:
LIGHT_IDLE_MAINTENANCE_MIN_BUDGET = properties.getLong(
KEY_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET,
- DEFAULT_LIGHT_IDLE_MAINTENANCE_MIN_BUDGET);
+ mDefaultLightIdleMaintenanceMinBudget);
break;
case KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET:
LIGHT_IDLE_MAINTENANCE_MAX_BUDGET = properties.getLong(
KEY_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET,
- DEFAULT_LIGHT_IDLE_MAINTENANCE_MAX_BUDGET);
+ mDefaultLightIdleMaintenanceMaxBudget);
break;
case KEY_MIN_LIGHT_MAINTENANCE_TIME:
MIN_LIGHT_MAINTENANCE_TIME = properties.getLong(
KEY_MIN_LIGHT_MAINTENANCE_TIME,
- DEFAULT_MIN_LIGHT_MAINTENANCE_TIME);
+ mDefaultMinLightMaintenanceTime);
break;
case KEY_MIN_DEEP_MAINTENANCE_TIME:
MIN_DEEP_MAINTENANCE_TIME = properties.getLong(
KEY_MIN_DEEP_MAINTENANCE_TIME,
- DEFAULT_MIN_DEEP_MAINTENANCE_TIME);
+ mDefaultMinDeepMaintenanceTime);
break;
case KEY_INACTIVE_TIMEOUT:
final long defaultInactiveTimeout = mSmallBatteryDevice
? DEFAULT_INACTIVE_TIMEOUT_SMALL_BATTERY
- : DEFAULT_INACTIVE_TIMEOUT;
+ : mDefaultInactiveTimeout;
INACTIVE_TIMEOUT = properties.getLong(
KEY_INACTIVE_TIMEOUT, defaultInactiveTimeout);
break;
case KEY_SENSING_TIMEOUT:
SENSING_TIMEOUT = properties.getLong(
- KEY_SENSING_TIMEOUT, DEFAULT_SENSING_TIMEOUT);
+ KEY_SENSING_TIMEOUT, mDefaultSensingTimeout);
break;
case KEY_LOCATING_TIMEOUT:
LOCATING_TIMEOUT = properties.getLong(
- KEY_LOCATING_TIMEOUT, DEFAULT_LOCATING_TIMEOUT);
+ KEY_LOCATING_TIMEOUT, mDefaultLocatingTimeout);
break;
case KEY_LOCATION_ACCURACY:
LOCATION_ACCURACY = properties.getFloat(
- KEY_LOCATION_ACCURACY, DEFAULT_LOCATION_ACCURACY);
+ KEY_LOCATION_ACCURACY, mDefaultLocationAccuracy);
break;
case KEY_MOTION_INACTIVE_TIMEOUT:
MOTION_INACTIVE_TIMEOUT = properties.getLong(
- KEY_MOTION_INACTIVE_TIMEOUT, DEFAULT_MOTION_INACTIVE_TIMEOUT);
+ KEY_MOTION_INACTIVE_TIMEOUT, mDefaultMotionInactiveTimeout);
break;
case KEY_MOTION_INACTIVE_TIMEOUT_FLEX:
MOTION_INACTIVE_TIMEOUT_FLEX = properties.getLong(
KEY_MOTION_INACTIVE_TIMEOUT_FLEX,
- DEFAULT_MOTION_INACTIVE_TIMEOUT_FLEX);
+ mDefaultMotionInactiveTimeoutFlex);
break;
case KEY_IDLE_AFTER_INACTIVE_TIMEOUT:
final long defaultIdleAfterInactiveTimeout = mSmallBatteryDevice
? DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT_SMALL_BATTERY
- : DEFAULT_IDLE_AFTER_INACTIVE_TIMEOUT;
+ : mDefaultIdleAfterInactiveTimeout;
IDLE_AFTER_INACTIVE_TIMEOUT = properties.getLong(
KEY_IDLE_AFTER_INACTIVE_TIMEOUT,
defaultIdleAfterInactiveTimeout);
break;
case KEY_IDLE_PENDING_TIMEOUT:
IDLE_PENDING_TIMEOUT = properties.getLong(
- KEY_IDLE_PENDING_TIMEOUT, DEFAULT_IDLE_PENDING_TIMEOUT);
+ KEY_IDLE_PENDING_TIMEOUT, mDefaultIdlePendingTimeout);
break;
case KEY_MAX_IDLE_PENDING_TIMEOUT:
MAX_IDLE_PENDING_TIMEOUT = properties.getLong(
- KEY_MAX_IDLE_PENDING_TIMEOUT, DEFAULT_MAX_IDLE_PENDING_TIMEOUT);
+ KEY_MAX_IDLE_PENDING_TIMEOUT, mDefaultMaxIdlePendingTimeout);
break;
case KEY_IDLE_PENDING_FACTOR:
IDLE_PENDING_FACTOR = properties.getFloat(
- KEY_IDLE_PENDING_FACTOR, DEFAULT_IDLE_PENDING_FACTOR);
+ KEY_IDLE_PENDING_FACTOR, mDefaultIdlePendingFactor);
break;
case KEY_QUICK_DOZE_DELAY_TIMEOUT:
QUICK_DOZE_DELAY_TIMEOUT = properties.getLong(
- KEY_QUICK_DOZE_DELAY_TIMEOUT, DEFAULT_QUICK_DOZE_DELAY_TIMEOUT);
+ KEY_QUICK_DOZE_DELAY_TIMEOUT, mDefaultQuickDozeDelayTimeout);
break;
case KEY_IDLE_TIMEOUT:
IDLE_TIMEOUT = properties.getLong(
- KEY_IDLE_TIMEOUT, DEFAULT_IDLE_TIMEOUT);
+ KEY_IDLE_TIMEOUT, mDefaultIdleTimeout);
break;
case KEY_MAX_IDLE_TIMEOUT:
MAX_IDLE_TIMEOUT = properties.getLong(
- KEY_MAX_IDLE_TIMEOUT, DEFAULT_MAX_IDLE_TIMEOUT);
+ KEY_MAX_IDLE_TIMEOUT, mDefaultMaxIdleTimeout);
break;
case KEY_IDLE_FACTOR:
- IDLE_FACTOR = properties.getFloat(KEY_IDLE_FACTOR, DEFAULT_IDLE_FACTOR);
+ IDLE_FACTOR = properties.getFloat(KEY_IDLE_FACTOR, mDefaultIdleFactor);
break;
case KEY_MIN_TIME_TO_ALARM:
MIN_TIME_TO_ALARM = properties.getLong(
- KEY_MIN_TIME_TO_ALARM, DEFAULT_MIN_TIME_TO_ALARM);
+ KEY_MIN_TIME_TO_ALARM, mDefaultMinTimeToAlarm);
break;
case KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS:
MAX_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
KEY_MAX_TEMP_APP_ALLOWLIST_DURATION_MS,
- DEFAULT_MAX_TEMP_APP_ALLOWLIST_DURATION_MS);
+ mDefaultMaxTempAppAllowlistDurationMs);
break;
case KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS:
MMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
KEY_MMS_TEMP_APP_ALLOWLIST_DURATION_MS,
- DEFAULT_MMS_TEMP_APP_ALLOWLIST_DURATION_MS);
+ mDefaultMmsTempAppAllowlistDurationMs);
break;
case KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS:
SMS_TEMP_APP_ALLOWLIST_DURATION_MS = properties.getLong(
KEY_SMS_TEMP_APP_ALLOWLIST_DURATION_MS,
- DEFAULT_SMS_TEMP_APP_ALLOWLIST_DURATION_MS);
+ mDefaultSmsTempAppAllowlistDurationMs);
break;
case KEY_NOTIFICATION_ALLOWLIST_DURATION_MS:
NOTIFICATION_ALLOWLIST_DURATION_MS = properties.getLong(
KEY_NOTIFICATION_ALLOWLIST_DURATION_MS,
- DEFAULT_NOTIFICATION_ALLOWLIST_DURATION_MS);
+ mDefaultNotificationAllowlistDurationMs);
break;
case KEY_WAIT_FOR_UNLOCK:
WAIT_FOR_UNLOCK = properties.getBoolean(
- KEY_WAIT_FOR_UNLOCK, DEFAULT_WAIT_FOR_UNLOCK);
+ KEY_WAIT_FOR_UNLOCK, mDefaultWaitForUnlock);
break;
case KEY_PRE_IDLE_FACTOR_LONG:
PRE_IDLE_FACTOR_LONG = properties.getFloat(
- KEY_PRE_IDLE_FACTOR_LONG, DEFAULT_PRE_IDLE_FACTOR_LONG);
+ KEY_PRE_IDLE_FACTOR_LONG, mDefaultPreIdleFactorLong);
break;
case KEY_PRE_IDLE_FACTOR_SHORT:
PRE_IDLE_FACTOR_SHORT = properties.getFloat(
- KEY_PRE_IDLE_FACTOR_SHORT, DEFAULT_PRE_IDLE_FACTOR_SHORT);
+ KEY_PRE_IDLE_FACTOR_SHORT, mDefaultPreIdleFactorShort);
break;
case KEY_USE_WINDOW_ALARMS:
USE_WINDOW_ALARMS = properties.getBoolean(
- KEY_USE_WINDOW_ALARMS, DEFAULT_USE_WINDOW_ALARMS);
+ KEY_USE_WINDOW_ALARMS, mDefaultUseWindowAlarms);
break;
default:
Slog.e(TAG, "Unknown configuration key: " + name);
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index b6d29aa5641a..22f70ce8e0e6 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -57,6 +57,8 @@ import static com.android.server.alarm.AlarmManagerService.RemovedAlarm.REMOVE_R
import android.Manifest;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.app.ActivityManagerInternal;
@@ -289,6 +291,7 @@ public class AlarmManagerService extends SystemService {
final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
IBinder.DeathRecipient mListenerDeathRecipient;
Intent mTimeTickIntent;
+ Bundle mTimeTickOptions;
IAlarmListener mTimeTickTrigger;
PendingIntent mDateChangeSender;
boolean mInteractive = true;
@@ -676,6 +679,9 @@ public class AlarmManagerService extends SystemService {
private static final String KEY_TIME_TICK_ALLOWED_WHILE_IDLE =
"time_tick_allowed_while_idle";
+ private static final String KEY_DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF =
+ "delay_nonwakeup_alarms_while_screen_off";
+
@VisibleForTesting
static final String KEY_ALLOW_WHILE_IDLE_QUOTA = "allow_while_idle_quota";
@@ -743,6 +749,8 @@ public class AlarmManagerService extends SystemService {
private static final int DEFAULT_TEMPORARY_QUOTA_BUMP = 0;
+ private static final boolean DEFAULT_DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF = true;
+
// Minimum futurity of a new alarm
public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY;
@@ -835,6 +843,9 @@ public class AlarmManagerService extends SystemService {
*/
public int TEMPORARY_QUOTA_BUMP = DEFAULT_TEMPORARY_QUOTA_BUMP;
+ public boolean DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF =
+ DEFAULT_DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF;
+
private long mLastAllowWhileIdleWhitelistDuration = -1;
private int mVersion = 0;
@@ -1011,6 +1022,11 @@ public class AlarmManagerService extends SystemService {
TEMPORARY_QUOTA_BUMP = properties.getInt(KEY_TEMPORARY_QUOTA_BUMP,
DEFAULT_TEMPORARY_QUOTA_BUMP);
break;
+ case KEY_DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF:
+ DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF = properties.getBoolean(
+ KEY_DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF,
+ DEFAULT_DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF);
+ break;
default:
if (name.startsWith(KEY_PREFIX_STANDBY_QUOTA) && !standbyQuotaUpdated) {
// The quotas need to be updated in order, so we can't just rely
@@ -1249,6 +1265,10 @@ public class AlarmManagerService extends SystemService {
pw.print(KEY_TEMPORARY_QUOTA_BUMP, TEMPORARY_QUOTA_BUMP);
pw.println();
+ pw.print(KEY_DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF,
+ DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF);
+ pw.println();
+
pw.decreaseIndent();
}
@@ -1892,7 +1912,9 @@ public class AlarmManagerService extends SystemService {
Intent.FLAG_RECEIVER_REGISTERED_ONLY
| Intent.FLAG_RECEIVER_FOREGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
-
+ mTimeTickOptions = BroadcastOptions
+ .makeRemovingMatchingFilter(new IntentFilter(Intent.ACTION_TIME_TICK))
+ .toBundle();
mTimeTickTrigger = new IAlarmListener.Stub() {
@Override
public void doAlarm(final IAlarmCompleteListener callback) throws RemoteException {
@@ -1904,8 +1926,8 @@ public class AlarmManagerService extends SystemService {
// takes care of this automatically, but we're using the direct internal
// interface here rather than that client-side wrapper infrastructure.
mHandler.post(() -> {
- getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL);
-
+ getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL, null,
+ mTimeTickOptions);
try {
callback.alarmComplete(this);
} catch (RemoteException e) { /* local method call */ }
@@ -4360,7 +4382,11 @@ public class AlarmManagerService extends SystemService {
}
}
+ @GuardedBy("mLock")
boolean checkAllowNonWakeupDelayLocked(long nowELAPSED) {
+ if (!mConstants.DELAY_NONWAKEUP_ALARMS_WHILE_SCREEN_OFF) {
+ return false;
+ }
if (mInteractive) {
return false;
}
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 4d5eef2f65f5..1775d908e21b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -882,6 +882,8 @@ public class JobSchedulerService extends com.android.server.SystemService
// a user-initiated action, it should be fine to just
// put USER instead of UNINSTALL or DISABLED.
cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
+ /* includeSchedulingApp */ true,
+ /* includeSourceApp */ true,
JobParameters.STOP_REASON_USER,
JobParameters.INTERNAL_STOP_REASON_UNINSTALL,
"app disabled");
@@ -932,6 +934,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// 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, pkgUid,
+ /* includeSchedulingApp */ true, /* includeSourceApp */ true,
JobParameters.STOP_REASON_USER,
JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "app uninstalled");
for (int c = 0; c < mControllers.size(); ++c) {
@@ -986,7 +989,12 @@ public class JobSchedulerService extends com.android.server.SystemService
Slog.d(TAG, "Removing jobs for pkg " + pkgName + " at uid " + pkgUid);
}
synchronized (mLock) {
+ // Exclude jobs scheduled on behalf of this app for now because SyncManager
+ // and other job proxy agents may not know to reschedule the job properly
+ // after force stop.
+ // TODO(209852664): determine how to best handle syncs & other proxied jobs
cancelJobsForPackageAndUidLocked(pkgName, pkgUid,
+ /* includeSchedulingApp */ true, /* includeSourceApp */ false,
JobParameters.STOP_REASON_USER,
JobParameters.INTERNAL_STOP_REASON_CANCELED,
"app force stopped");
@@ -1304,16 +1312,18 @@ public class JobSchedulerService extends com.android.server.SystemService
}
}
+ private final Consumer<JobStatus> mCancelJobDueToUserRemovalConsumer = (toRemove) -> {
+ // There's no guarantee that the process has been stopped by the time we get
+ // here, but since this is a user-initiated action, it should be fine to just
+ // put USER instead of UNINSTALL or DISABLED.
+ cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
+ JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed");
+ };
+
private void cancelJobsForUserLocked(int userHandle) {
- final List<JobStatus> jobsForUser = mJobs.getJobsByUser(userHandle);
- for (int i = 0; i < jobsForUser.size(); i++) {
- JobStatus toRemove = jobsForUser.get(i);
- // There's no guarantee that the process has been stopped by the time we get here,
- // but since this is a user-initiated action, it should be fine to just put USER
- // instead of UNINSTALL or DISABLED.
- cancelJobImplLocked(toRemove, null, JobParameters.STOP_REASON_USER,
- JobParameters.INTERNAL_STOP_REASON_UNINSTALL, "user removed");
- }
+ mJobs.forEachJob(
+ (job) -> job.getUserId() == userHandle || job.getSourceUserId() == userHandle,
+ mCancelJobDueToUserRemovalConsumer);
}
private void cancelJobsForNonExistentUsers() {
@@ -1324,15 +1334,31 @@ public class JobSchedulerService extends com.android.server.SystemService
}
private void cancelJobsForPackageAndUidLocked(String pkgName, int uid,
+ boolean includeSchedulingApp, boolean includeSourceApp,
@JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
+ if (!includeSchedulingApp && !includeSourceApp) {
+ Slog.wtfStack(TAG,
+ "Didn't indicate whether to cancel jobs for scheduling and/or source app");
+ includeSourceApp = true;
+ }
if ("android".equals(pkgName)) {
Slog.wtfStack(TAG, "Can't cancel all jobs for system package");
return;
}
- final List<JobStatus> jobsForUid = mJobs.getJobsByUid(uid);
+ final List<JobStatus> jobsForUid = new ArrayList<>();
+ if (includeSchedulingApp) {
+ mJobs.getJobsByUid(uid, jobsForUid);
+ }
+ if (includeSourceApp) {
+ mJobs.getJobsBySourceUid(uid, jobsForUid);
+ }
for (int i = jobsForUid.size() - 1; i >= 0; i--) {
final JobStatus job = jobsForUid.get(i);
- if (job.getSourcePackageName().equals(pkgName)) {
+ final boolean shouldCancel =
+ (includeSchedulingApp
+ && job.getServiceComponent().getPackageName().equals(pkgName))
+ || (includeSourceApp && job.getSourcePackageName().equals(pkgName));
+ if (shouldCancel) {
cancelJobImplLocked(job, null, reason, internalReasonCode, debugReason);
}
}
@@ -1404,8 +1430,31 @@ public class JobSchedulerService extends com.android.server.SystemService
}
mChangedJobList.remove(cancelled);
// Cancel if running.
- mConcurrencyManager.stopJobOnServiceContextLocked(
+ final boolean wasRunning = mConcurrencyManager.stopJobOnServiceContextLocked(
cancelled, reason, internalReasonCode, debugReason);
+ // If the job was running, the JobServiceContext should log with state FINISHED.
+ if (!wasRunning) {
+ FrameworkStatsLog.write_non_chained(FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED,
+ cancelled.getSourceUid(), null, cancelled.getBatteryName(),
+ FrameworkStatsLog.SCHEDULED_JOB_STATE_CHANGED__STATE__CANCELLED,
+ internalReasonCode, cancelled.getStandbyBucket(),
+ cancelled.getJobId(),
+ cancelled.hasChargingConstraint(),
+ cancelled.hasBatteryNotLowConstraint(),
+ cancelled.hasStorageNotLowConstraint(),
+ cancelled.hasTimingDelayConstraint(),
+ cancelled.hasDeadlineConstraint(),
+ cancelled.hasIdleConstraint(),
+ cancelled.hasConnectivityConstraint(),
+ cancelled.hasContentTriggerConstraint(),
+ cancelled.isRequestedExpeditedJob(),
+ /* isRunningAsExpeditedJob */ false,
+ reason,
+ cancelled.getJob().isPrefetch(),
+ cancelled.getJob().getPriority(),
+ cancelled.getEffectivePriority(),
+ cancelled.getNumFailures());
+ }
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
if (DEBUG) Slog.i(TAG, "Tracking replacement job " + incomingJob.toShortString());
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 72553ed7a0a5..90ce8bf27322 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -366,7 +366,7 @@ public final class JobServiceContext implements ServiceConnection {
job.getNumFailures());
// Use the context's ID to distinguish traces since there'll only be one job running
// per context.
- Trace.asyncTraceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, job.getBatteryName(), getId());
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, job.getTag(), getId());
try {
mBatteryStats.noteJobStart(job.getBatteryName(), job.getSourceUid());
} catch (RemoteException e) {
@@ -1030,7 +1030,7 @@ public final class JobServiceContext implements ServiceConnection {
completedJob.getJob().getPriority(),
completedJob.getEffectivePriority(),
completedJob.getNumFailures());
- Trace.asyncTraceEnd(Trace.TRACE_TAG_SYSTEM_SERVER, completedJob.getBatteryName(), getId());
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_SYSTEM_SERVER, completedJob.getTag(), getId());
try {
mBatteryStats.noteJobFinish(mRunningJob.getBatteryName(), mRunningJob.getSourceUid(),
internalStopReason);
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 dfa1442a3192..78ab06c9e332 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java
@@ -22,6 +22,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
import static com.android.server.job.JobSchedulerService.sSystemClock;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.job.JobInfo;
import android.content.ComponentName;
@@ -32,7 +33,6 @@ import android.os.Handler;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.util.ArraySet;
@@ -287,26 +287,37 @@ public final class JobStore {
}
/**
- * @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.
+ * @param sourceUid Uid of the source app.
+ * @return A list of all the jobs scheduled for the source app. Never null.
*/
- public List<JobStatus> getJobsByUser(int userHandle) {
- return mJobSet.getJobsByUser(userHandle);
+ @NonNull
+ public List<JobStatus> getJobsBySourceUid(int sourceUid) {
+ return mJobSet.getJobsBySourceUid(sourceUid);
+ }
+
+ public void getJobsBySourceUid(int sourceUid, @NonNull List<JobStatus> insertInto) {
+ mJobSet.getJobsBySourceUid(sourceUid, insertInto);
}
/**
* @param uid Uid of the requesting app.
* @return All JobStatus objects for a given uid from the master list. Never null.
*/
+ @NonNull
public List<JobStatus> getJobsByUid(int uid) {
return mJobSet.getJobsByUid(uid);
}
+ public void getJobsByUid(int uid, @NonNull List<JobStatus> insertInto) {
+ mJobSet.getJobsByUid(uid, insertInto);
+ }
+
/**
* @param uid Uid of the requesting app.
* @param jobId Job id, specified at schedule-time.
* @return the JobStatus that matches the provided uId and jobId, or null if none found.
*/
+ @Nullable
public JobStatus getJobByUidAndJobId(int uid, int jobId) {
return mJobSet.get(uid, jobId);
}
@@ -742,6 +753,10 @@ public final class JobStore {
}
} catch (XmlPullParserException | IOException e) {
Slog.wtf(TAG, "Error jobstore xml.", e);
+ } catch (Exception e) {
+ // Crashing at this point would result in a boot loop, so live with a general
+ // Exception for system stability's sake.
+ Slog.wtf(TAG, "Unexpected exception", e);
} finally {
if (mPersistInfo.countAllJobsLoaded < 0) { // Only set them once.
mPersistInfo.countAllJobsLoaded = numJobs;
@@ -890,6 +905,9 @@ public final class JobStore {
} catch (IOException e) {
Slog.d(TAG, "Error I/O Exception.", e);
return null;
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Constraints contained invalid data", e);
+ return null;
}
parser.next(); // Consume </constraints>
@@ -986,8 +1004,14 @@ public final class JobStore {
return null;
}
- PersistableBundle extras = PersistableBundle.restoreFromXml(parser);
- jobBuilder.setExtras(extras);
+ final PersistableBundle extras;
+ try {
+ extras = PersistableBundle.restoreFromXml(parser);
+ jobBuilder.setExtras(extras);
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "Persisted extras contained invalid data", e);
+ return null;
+ }
parser.nextTag(); // Consume </extras>
final JobInfo builtJob;
@@ -1207,27 +1231,31 @@ public final class JobStore {
public List<JobStatus> getJobsByUid(int uid) {
ArrayList<JobStatus> matchingJobs = new ArrayList<JobStatus>();
+ getJobsByUid(uid, matchingJobs);
+ return matchingJobs;
+ }
+
+ public void getJobsByUid(int uid, List<JobStatus> insertInto) {
ArraySet<JobStatus> jobs = mJobs.get(uid);
if (jobs != null) {
- matchingJobs.addAll(jobs);
+ insertInto.addAll(jobs);
}
- return matchingJobs;
}
- // By user, not by uid, so we need to traverse by key and check
- public List<JobStatus> getJobsByUser(int userId) {
+ @NonNull
+ public List<JobStatus> getJobsBySourceUid(int sourceUid) {
final ArrayList<JobStatus> result = new ArrayList<JobStatus>();
- for (int i = mJobsPerSourceUid.size() - 1; i >= 0; i--) {
- if (UserHandle.getUserId(mJobsPerSourceUid.keyAt(i)) == userId) {
- final ArraySet<JobStatus> jobs = mJobsPerSourceUid.valueAt(i);
- if (jobs != null) {
- result.addAll(jobs);
- }
- }
- }
+ getJobsBySourceUid(sourceUid, result);
return result;
}
+ public void getJobsBySourceUid(int sourceUid, List<JobStatus> insertInto) {
+ final ArraySet<JobStatus> jobs = mJobsPerSourceUid.get(sourceUid);
+ if (jobs != null) {
+ insertInto.addAll(jobs);
+ }
+ }
+
public boolean add(JobStatus job) {
final int uid = job.getUid();
final int sourceUid = job.getSourceUid();
@@ -1369,7 +1397,7 @@ public final class JobStore {
}
public void forEachJob(@Nullable Predicate<JobStatus> filterPredicate,
- Consumer<JobStatus> functor) {
+ @NonNull Consumer<JobStatus> functor) {
for (int uidIndex = mJobs.size() - 1; uidIndex >= 0; uidIndex--) {
ArraySet<JobStatus> jobs = mJobs.valueAt(uidIndex);
if (jobs != null) {
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 3bbc5a369684..d284a99a4559 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
@@ -69,6 +69,11 @@ public final class BatteryController extends RestrictingController {
*/
private final ArraySet<JobStatus> mChangedJobs = new ArraySet<>();
+ @GuardedBy("mLock")
+ private Boolean mLastReportedStatsdBatteryNotLow = null;
+ @GuardedBy("mLock")
+ private Boolean mLastReportedStatsdStablePower = null;
+
public BatteryController(JobSchedulerService service,
FlexibilityController flexibilityController) {
super(service);
@@ -176,12 +181,25 @@ public final class BatteryController extends RestrictingController {
Slog.d(TAG, "maybeReportNewChargingStateLocked: "
+ powerConnected + "/" + stablePower + "/" + batteryNotLow);
}
+
+ if (mLastReportedStatsdStablePower == null
+ || mLastReportedStatsdStablePower != stablePower) {
+ logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_CHARGING, stablePower);
+ mLastReportedStatsdStablePower = stablePower;
+ }
+ if (mLastReportedStatsdBatteryNotLow == null
+ || mLastReportedStatsdBatteryNotLow != stablePower) {
+ logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_BATTERY_NOT_LOW,
+ batteryNotLow);
+ mLastReportedStatsdBatteryNotLow = batteryNotLow;
+ }
+
final long nowElapsed = sElapsedRealtimeClock.millis();
mFlexibilityController.setConstraintSatisfied(
JobStatus.CONSTRAINT_CHARGING, mService.isBatteryCharging(), nowElapsed);
mFlexibilityController.setConstraintSatisfied(
- JobStatus.CONSTRAINT_BATTERY_NOT_LOW, batteryNotLow, nowElapsed);
+ JobStatus.CONSTRAINT_BATTERY_NOT_LOW, batteryNotLow, nowElapsed);
for (int i = mTrackedTasks.size() - 1; i >= 0; i--) {
final JobStatus ts = mTrackedTasks.valueAt(i);
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 f6de109d7ec9..abbe177c5d49 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
@@ -153,6 +153,8 @@ public final class DeviceIdleJobsController extends StateController {
changed = true;
}
mDeviceIdleMode = enabled;
+ logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_DEVICE_NOT_DOZING,
+ !mDeviceIdleMode);
if (DEBUG) Slog.d(TAG, "mDeviceIdleMode=" + mDeviceIdleMode);
mDeviceIdleUpdateFunctor.prepare();
if (enabled) {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index f1e13dfcab5c..9c167728cff9 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -644,7 +644,7 @@ public final class FlexibilityController extends StateController {
static final String KEY_FALLBACK_FLEXIBILITY_DEADLINE =
FC_CONFIG_PREFIX + "fallback_flexibility_deadline_ms";
static final String KEY_MIN_TIME_BETWEEN_FLEXIBILITY_ALARMS_MS =
- FC_CONFIG_PREFIX + "min_alarm_time_flexibility_ms";
+ FC_CONFIG_PREFIX + "min_time_between_flexibility_alarms_ms";
static final String KEY_PERCENTS_TO_DROP_NUM_FLEXIBLE_CONSTRAINTS =
FC_CONFIG_PREFIX + "percents_to_drop_num_flexible_constraints";
static final String KEY_MAX_RESCHEDULED_DEADLINE_MS =
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 dd0621728724..926cfc192cd1 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
@@ -96,6 +96,8 @@ public final class IdleController extends RestrictingController implements Idlen
@Override
public void reportNewIdleState(boolean isIdle) {
synchronized (mLock) {
+ logDeviceWideConstraintStateToStatsd(JobStatus.CONSTRAINT_IDLE, isIdle);
+
final long nowElapsed = sElapsedRealtimeClock.millis();
mFlexibilityController.setConstraintSatisfied(
JobStatus.CONSTRAINT_IDLE, isIdle, nowElapsed);
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 4320db09aef5..999a3c02b18c 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
@@ -170,13 +170,12 @@ public final class JobStatus {
*/
private static final int STATSD_CONSTRAINTS_TO_LOG = CONSTRAINT_CONTENT_TRIGGER
| CONSTRAINT_DEADLINE
- | CONSTRAINT_IDLE
| CONSTRAINT_PREFETCH
| CONSTRAINT_TARE_WEALTH
| CONSTRAINT_TIMING_DELAY
| CONSTRAINT_WITHIN_QUOTA;
- // TODO(b/129954980)
+ // TODO(b/129954980): ensure this doesn't spam statsd, especially at boot
private static final boolean STATS_LOG_ENABLED = false;
// No override.
@@ -1982,7 +1981,7 @@ public final class JobStatus {
}
/** Returns a {@link JobServerProtoEnums.Constraint} enum value for the given constraint. */
- private int getProtoConstraint(int constraint) {
+ static int getProtoConstraint(int constraint) {
switch (constraint) {
case CONSTRAINT_BACKGROUND_NOT_RESTRICTED:
return JobServerProtoEnums.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
@@ -1998,10 +1997,16 @@ public final class JobStatus {
return JobServerProtoEnums.CONSTRAINT_DEADLINE;
case CONSTRAINT_DEVICE_NOT_DOZING:
return JobServerProtoEnums.CONSTRAINT_DEVICE_NOT_DOZING;
+ case CONSTRAINT_FLEXIBLE:
+ return JobServerProtoEnums.CONSTRAINT_FLEXIBILITY;
case CONSTRAINT_IDLE:
return JobServerProtoEnums.CONSTRAINT_IDLE;
+ case CONSTRAINT_PREFETCH:
+ return JobServerProtoEnums.CONSTRAINT_PREFETCH;
case CONSTRAINT_STORAGE_NOT_LOW:
return JobServerProtoEnums.CONSTRAINT_STORAGE_NOT_LOW;
+ case CONSTRAINT_TARE_WEALTH:
+ return JobServerProtoEnums.CONSTRAINT_TARE_WEALTH;
case CONSTRAINT_TIMING_DELAY:
return JobServerProtoEnums.CONSTRAINT_TIMING_DELAY;
case CONSTRAINT_WITHIN_QUOTA:
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 2a2d602b24bf..8453e53782ca 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
@@ -26,6 +26,7 @@ import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.job.JobSchedulerService;
import com.android.server.job.JobSchedulerService.Constants;
import com.android.server.job.StateChangedListener;
@@ -165,6 +166,15 @@ public abstract class StateController {
return mService.areComponentsInPlaceLocked(jobStatus);
}
+ protected void logDeviceWideConstraintStateToStatsd(int constraint, boolean satisfied) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED,
+ JobStatus.getProtoConstraint(constraint),
+ satisfied
+ ? FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED__STATE__SATISFIED
+ : FrameworkStatsLog.DEVICE_WIDE_JOB_CONSTRAINT_CHANGED__STATE__UNSATISFIED);
+ }
+
public abstract void dumpControllerStateLocked(IndentingPrintWriter pw,
Predicate<JobStatus> predicate);
public void dumpControllerStateLocked(ProtoOutputStream proto, long fieldId,
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
index b79cc5e4bb60..7391bcfa8d88 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/EconomicPolicy.java
@@ -120,6 +120,7 @@ public abstract class EconomicPolicy {
REWARD_NOTIFICATION_INTERACTION,
REWARD_WIDGET_INTERACTION,
REWARD_OTHER_USER_INTERACTION,
+ JobSchedulerEconomicPolicy.REWARD_APP_INSTALL,
})
@Retention(RetentionPolicy.SOURCE)
public @interface UtilityReward {
@@ -430,6 +431,8 @@ public abstract class EconomicPolicy {
return "REWARD_WIDGET_INTERACTION";
case REWARD_OTHER_USER_INTERACTION:
return "REWARD_OTHER_USER_INTERACTION";
+ case JobSchedulerEconomicPolicy.REWARD_APP_INSTALL:
+ return "REWARD_JOB_APP_INSTALL";
}
return "UNKNOWN_REWARD:" + Integer.toHexString(eventId);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
index da544bb6a2eb..fcb3e6759412 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InstalledPackageInfo.java
@@ -17,9 +17,12 @@
package com.android.server.tare;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
import android.content.pm.ApplicationInfo;
+import android.content.pm.InstallSourceInfo;
import android.content.pm.PackageInfo;
-import android.os.UserHandle;
+import android.os.RemoteException;
/** POJO to cache only the information about installed packages that TARE cares about. */
class InstalledPackageInfo {
@@ -28,11 +31,21 @@ class InstalledPackageInfo {
public final int uid;
public final String packageName;
public final boolean hasCode;
+ @Nullable
+ public final String installerPackageName;
InstalledPackageInfo(@NonNull PackageInfo packageInfo) {
final ApplicationInfo applicationInfo = packageInfo.applicationInfo;
- this.uid = applicationInfo == null ? NO_UID : applicationInfo.uid;
- this.packageName = packageInfo.packageName;
- this.hasCode = applicationInfo != null && applicationInfo.hasCode();
+ uid = applicationInfo == null ? NO_UID : applicationInfo.uid;
+ packageName = packageInfo.packageName;
+ hasCode = applicationInfo != null && applicationInfo.hasCode();
+ InstallSourceInfo installSourceInfo = null;
+ try {
+ installSourceInfo = AppGlobals.getPackageManager().getInstallSourceInfo(packageName);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+ installerPackageName =
+ installSourceInfo == null ? null : installSourceInfo.getInstallingPackageName();
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 59d4ded8b1d2..4a26d213a48a 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -81,6 +81,7 @@ import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.CopyOnWriteArraySet;
/**
@@ -164,6 +165,10 @@ public class InternalResourceService extends SystemService {
@GuardedBy("mLock")
private final SparseArrayMap<String, Boolean> mVipOverrides = new SparseArrayMap<>();
+ /** Set of apps each installer is responsible for installing. */
+ @GuardedBy("mLock")
+ private final SparseArrayMap<String, ArraySet<String>> mInstallers = new SparseArrayMap<>();
+
private volatile boolean mHasBattery = true;
private volatile boolean mIsEnabled;
private volatile int mBootPhase;
@@ -353,6 +358,14 @@ public class InternalResourceService extends SystemService {
return mCompleteEconomicPolicy;
}
+ /** Returns the number of apps that this app is expected to update at some point. */
+ int getAppUpdateResponsibilityCount(final int userId, @NonNull final String pkgName) {
+ synchronized (mLock) {
+ // TODO(248274798): return 0 if the app has lost the install permission
+ return ArrayUtils.size(mInstallers.get(userId, pkgName));
+ }
+ }
+
@NonNull
SparseArrayMap<String, InstalledPackageInfo> getInstalledPackages() {
synchronized (mLock) {
@@ -524,10 +537,16 @@ public class InternalResourceService extends SystemService {
mPackageToUidCache.add(userId, pkgName, uid);
}
synchronized (mLock) {
- mPkgCache.add(userId, pkgName, new InstalledPackageInfo(packageInfo));
+ final InstalledPackageInfo ipo = new InstalledPackageInfo(packageInfo);
+ final InstalledPackageInfo oldIpo = mPkgCache.add(userId, pkgName, ipo);
+ maybeUpdateInstallerStatusLocked(oldIpo, ipo);
mUidToPackageCache.add(uid, pkgName);
// TODO: only do this when the user first launches the app (app leaves stopped state)
mAgent.grantBirthrightLocked(userId, pkgName);
+ if (ipo.installerPackageName != null) {
+ mAgent.noteInstantaneousEventLocked(userId, ipo.installerPackageName,
+ JobSchedulerEconomicPolicy.REWARD_APP_INSTALL, null);
+ }
}
}
@@ -547,7 +566,14 @@ public class InternalResourceService extends SystemService {
synchronized (mLock) {
mUidToPackageCache.remove(uid, pkgName);
mVipOverrides.delete(userId, pkgName);
- mPkgCache.delete(userId, pkgName);
+ final InstalledPackageInfo ipo = mPkgCache.delete(userId, pkgName);
+ mInstallers.delete(userId, pkgName);
+ if (ipo != null && ipo.installerPackageName != null) {
+ final ArraySet<String> list = mInstallers.get(userId, ipo.installerPackageName);
+ if (list != null) {
+ list.remove(pkgName);
+ }
+ }
mAgent.onPackageRemovedLocked(userId, pkgName);
}
}
@@ -569,7 +595,8 @@ public class InternalResourceService extends SystemService {
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
- mPkgCache.add(userId, ipo.packageName, ipo);
+ final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
+ maybeUpdateInstallerStatusLocked(oldIpo, ipo);
}
mAgent.grantBirthrightsLocked(userId);
}
@@ -585,6 +612,7 @@ public class InternalResourceService extends SystemService {
mUidToPackageCache.remove(pkgInfo.uid);
}
}
+ mInstallers.delete(userId);
mPkgCache.delete(userId);
mAgent.onUserRemovedLocked(userId);
}
@@ -741,11 +769,49 @@ public class InternalResourceService extends SystemService {
mPackageManager.getInstalledPackagesAsUser(PACKAGE_QUERY_FLAGS, userId);
for (int i = pkgs.size() - 1; i >= 0; --i) {
final InstalledPackageInfo ipo = new InstalledPackageInfo(pkgs.get(i));
- mPkgCache.add(userId, ipo.packageName, ipo);
+ final InstalledPackageInfo oldIpo = mPkgCache.add(userId, ipo.packageName, ipo);
+ maybeUpdateInstallerStatusLocked(oldIpo, ipo);
}
}
}
+ /**
+ * Used to update the set of installed apps for each installer. This only has an effect if the
+ * installer package name is different between {@code oldIpo} and {@code newIpo}.
+ */
+ @GuardedBy("mLock")
+ private void maybeUpdateInstallerStatusLocked(@Nullable InstalledPackageInfo oldIpo,
+ @NonNull InstalledPackageInfo newIpo) {
+ final boolean changed;
+ if (oldIpo == null) {
+ changed = newIpo.installerPackageName != null;
+ } else {
+ changed = !Objects.equals(oldIpo.installerPackageName, newIpo.installerPackageName);
+ }
+ if (!changed) {
+ return;
+ }
+ // InstallSourceInfo doesn't track userId, so for now, assume the installer on the package's
+ // user profile did the installation.
+ // TODO(246640162): use the actual installer's user ID
+ final int userId = UserHandle.getUserId(newIpo.uid);
+ final String pkgName = newIpo.packageName;
+ if (oldIpo != null) {
+ final ArraySet<String> oldList = mInstallers.get(userId, oldIpo.installerPackageName);
+ if (oldList != null) {
+ oldList.remove(pkgName);
+ }
+ }
+ if (newIpo.installerPackageName != null) {
+ ArraySet<String> newList = mInstallers.get(userId, newIpo.installerPackageName);
+ if (newList == null) {
+ newList = new ArraySet<>();
+ mInstallers.add(userId, newIpo.installerPackageName, newList);
+ }
+ newList.add(pkgName);
+ }
+ }
+
private void registerListeners() {
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_BATTERY_CHANGED);
@@ -1355,6 +1421,23 @@ public class InternalResourceService extends SystemService {
pw.println();
pw.println();
+ pw.println("Installers:");
+ pw.increaseIndent();
+ for (int u = 0; u < mInstallers.numMaps(); ++u) {
+ final int userId = mInstallers.keyAt(u);
+
+ for (int p = 0; p < mInstallers.numElementsForKeyAt(u); ++p) {
+ final String pkgName = mInstallers.keyAt(u, p);
+
+ pw.print(appToString(userId, pkgName));
+ pw.print(": ");
+ pw.print(mInstallers.valueAt(u, p).size());
+ pw.println(" apps");
+ }
+ }
+ pw.decreaseIndent();
+
+ pw.println();
mCompleteEconomicPolicy.dump(pw);
pw.println();
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
index cbb88c0f5e31..322e824286d5 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/JobSchedulerEconomicPolicy.java
@@ -42,7 +42,11 @@ import static android.app.tare.EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_
import static android.app.tare.EconomyManager.DEFAULT_JS_INITIAL_CONSUMPTION_LIMIT_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES;
+import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_INSTANT_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_MAX_CAKES;
import static android.app.tare.EconomyManager.DEFAULT_JS_REWARD_NOTIFICATION_INTERACTION_ONGOING_CAKES;
@@ -84,7 +88,11 @@ import static android.app.tare.EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT;
import static android.app.tare.EconomyManager.KEY_JS_MAX_SATIATED_BALANCE;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED;
+import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER;
import static android.app.tare.EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_INSTANT;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_MAX;
+import static android.app.tare.EconomyManager.KEY_JS_REWARD_APP_INSTALL_ONGOING;
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;
@@ -137,6 +145,8 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
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;
+ public static final int REWARD_APP_INSTALL = TYPE_REWARD | POLICY_JS | 0;
+
private static final int[] COST_MODIFIERS = new int[]{
COST_MODIFIER_CHARGING,
COST_MODIFIER_DEVICE_IDLE,
@@ -146,6 +156,7 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
private long mMinSatiatedBalanceExempted;
private long mMinSatiatedBalanceOther;
+ private long mMinSatiatedBalanceIncrementalAppUpdater;
private long mMaxSatiatedBalance;
private long mInitialSatiatedConsumptionLimit;
private long mHardSatiatedConsumptionLimit;
@@ -175,11 +186,20 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
if (mIrs.isPackageRestricted(userId, pkgName)) {
return 0;
}
+
+ final long baseBalance;
if (mIrs.isPackageExempted(userId, pkgName)) {
- return mMinSatiatedBalanceExempted;
+ baseBalance = mMinSatiatedBalanceExempted;
+ } else {
+ baseBalance = mMinSatiatedBalanceOther;
}
- // TODO: take other exemptions into account
- return mMinSatiatedBalanceOther;
+
+ long minBalance = baseBalance;
+
+ final int updateResponsibilityCount = mIrs.getAppUpdateResponsibilityCount(userId, pkgName);
+ minBalance += updateResponsibilityCount * mMinSatiatedBalanceIncrementalAppUpdater;
+
+ return Math.min(minBalance, mMaxSatiatedBalance);
}
@Override
@@ -234,6 +254,9 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
mMinSatiatedBalanceExempted = getConstantAsCake(mParser, properties,
KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mMinSatiatedBalanceOther);
+ mMinSatiatedBalanceIncrementalAppUpdater = getConstantAsCake(mParser, properties,
+ KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
+ DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
mMaxSatiatedBalance = getConstantAsCake(mParser, properties,
KEY_JS_MAX_SATIATED_BALANCE, DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
Math.max(arcToCake(1), mMinSatiatedBalanceExempted));
@@ -374,14 +397,26 @@ public class JobSchedulerEconomicPolicy extends EconomicPolicy {
getConstantAsCake(mParser, properties,
KEY_JS_REWARD_OTHER_USER_INTERACTION_MAX,
DEFAULT_JS_REWARD_OTHER_USER_INTERACTION_MAX_CAKES)));
+ mRewards.put(REWARD_APP_INSTALL,
+ new Reward(REWARD_APP_INSTALL,
+ getConstantAsCake(mParser, properties,
+ KEY_JS_REWARD_APP_INSTALL_INSTANT,
+ DEFAULT_JS_REWARD_APP_INSTALL_INSTANT_CAKES),
+ getConstantAsCake(mParser, properties,
+ KEY_JS_REWARD_APP_INSTALL_ONGOING,
+ DEFAULT_JS_REWARD_APP_INSTALL_ONGOING_CAKES),
+ getConstantAsCake(mParser, properties,
+ KEY_JS_REWARD_APP_INSTALL_MAX,
+ DEFAULT_JS_REWARD_APP_INSTALL_MAX_CAKES)));
}
@Override
void dump(IndentingPrintWriter pw) {
- pw.println("Min satiated balances:");
+ pw.println("Min satiated balance:");
pw.increaseIndent();
pw.print("Exempted", cakeToString(mMinSatiatedBalanceExempted)).println();
pw.print("Other", cakeToString(mMinSatiatedBalanceOther)).println();
+ pw.print("+App Updater", cakeToString(mMinSatiatedBalanceIncrementalAppUpdater)).println();
pw.decreaseIndent();
pw.print("Max satiated balance", cakeToString(mMaxSatiatedBalance)).println();
pw.print("Consumption limits: [");
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
index 620d1a0da76f..a68170c9bac7 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/Ledger.java
@@ -286,7 +286,7 @@ class Ledger {
final int idx = (mRewardBucketIndex - b + NUM_REWARD_BUCKET_WINDOWS)
% NUM_REWARD_BUCKET_WINDOWS;
final RewardBucket rewardBucket = mRewardBuckets[idx];
- if (rewardBucket == null) {
+ if (rewardBucket == null || rewardBucket.startTimeMs == 0) {
continue;
}
diff --git a/api/Android.bp b/api/Android.bp
index e9930e3a3b5b..29eaec6ffbd2 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -38,23 +38,9 @@ bootstrap_go_package {
pluginFor: ["soong_build"],
}
-python_defaults {
- name: "python3_version_defaults",
- version: {
- py2: {
- enabled: false,
- },
- py3: {
- enabled: true,
- embedded_launcher: false,
- },
- },
-}
-
python_binary_host {
name: "api_versions_trimmer",
srcs: ["api_versions_trimmer.py"],
- defaults: ["python3_version_defaults"],
}
python_test_host {
@@ -64,7 +50,6 @@ python_test_host {
"api_versions_trimmer_unittests.py",
"api_versions_trimmer.py",
],
- defaults: ["python3_version_defaults"],
test_options: {
unit_test: true,
},
@@ -73,7 +58,6 @@ python_test_host {
python_binary_host {
name: "merge_annotation_zips",
srcs: ["merge_annotation_zips.py"],
- defaults: ["python3_version_defaults"],
}
python_test_host {
@@ -83,7 +67,6 @@ python_test_host {
"merge_annotation_zips.py",
"merge_annotation_zips_test.py",
],
- defaults: ["python3_version_defaults"],
test_options: {
unit_test: true,
},
diff --git a/api/Android.mk b/api/Android.mk
new file mode 100644
index 000000000000..ce5f995033c5
--- /dev/null
+++ b/api/Android.mk
@@ -0,0 +1,2 @@
+.PHONY: checkapi
+checkapi: frameworks-base-api-current-compat frameworks-base-api-system-current-compat frameworks-base-api-module-lib-current-compat
diff --git a/cmds/idmap2/Android.bp b/cmds/idmap2/Android.bp
index 18ec5a407b24..4f8faca59e4c 100644
--- a/cmds/idmap2/Android.bp
+++ b/cmds/idmap2/Android.bp
@@ -43,6 +43,7 @@ cc_defaults {
"-modernize-return-braced-init-list",
"-modernize-use-default-member-init",
"-modernize-use-equals-default",
+ "-modernize-use-emplace",
"-modernize-use-nodiscard",
"-modernize-use-override",
"-modernize-use-trailing-return-type",
diff --git a/cmds/idmap2/include/idmap2/Idmap.h b/cmds/idmap2/include/idmap2/Idmap.h
index 58aff42b1e98..03e714a3847e 100644
--- a/cmds/idmap2/include/idmap2/Idmap.h
+++ b/cmds/idmap2/include/idmap2/Idmap.h
@@ -21,42 +21,51 @@
* header := magic version target_crc overlay_crc fulfilled_policies
* enforce_overlayable target_path overlay_path overlay_name
* debug_info
- * data := data_header target_entry* target_inline_entry* overlay_entry*
- * string_pool
- * data_header := target_entry_count target_inline_entry_count overlay_entry_count
+ * data := data_header target_entry* target_inline_entry*
+ target_inline_entry_value* config* overlay_entry* string_pool
+ * data_header := target_entry_count target_inline_entry_count
+ target_inline_entry_value_count config_count overlay_entry_count
* string_pool_index
* target_entry := target_id overlay_id
- * target_inline_entry := target_id Res_value::size padding(1) Res_value::type
+ * target_inline_entry := target_id start_value_index value_count
+ * target_inline_entry_value := config_index Res_value::size padding(1) Res_value::type
+ * Res_value::value
+ * config := target_id Res_value::size padding(1) Res_value::type
* Res_value::value
* overlay_entry := overlay_id target_id
*
- * debug_info := string
- * enforce_overlayable := <uint32_t>
- * fulfilled_policies := <uint32_t>
- * magic := <uint32_t>
- * overlay_crc := <uint32_t>
- * overlay_entry_count := <uint32_t>
- * overlay_id := <uint32_t>
- * overlay_package_id := <uint8_t>
- * overlay_name := string
- * overlay_path := string
- * padding(n) := <uint8_t>[n]
- * Res_value::size := <uint16_t>
- * Res_value::type := <uint8_t>
- * Res_value::value := <uint32_t>
- * string := <uint32_t> <uint8_t>+ padding(n)
- * string_pool := string
- * string_pool_index := <uint32_t>
- * string_pool_length := <uint32_t>
- * target_crc := <uint32_t>
- * target_entry_count := <uint32_t>
- * target_inline_entry_count := <uint32_t>
- * target_id := <uint32_t>
- * target_package_id := <uint8_t>
- * target_path := string
- * value_type := <uint8_t>
- * value_data := <uint32_t>
- * version := <uint32_t>
+ * debug_info := string
+ * enforce_overlayable := <uint32_t>
+ * fulfilled_policies := <uint32_t>
+ * magic := <uint32_t>
+ * overlay_crc := <uint32_t>
+ * overlay_entry_count := <uint32_t>
+ * overlay_id := <uint32_t>
+ * overlay_package_id := <uint8_t>
+ * overlay_name := string
+ * overlay_path := string
+ * padding(n) := <uint8_t>[n]
+ * Res_value::size := <uint16_t>
+ * Res_value::type := <uint8_t>
+ * Res_value::value := <uint32_t>
+ * string := <uint32_t> <uint8_t>+ padding(n)
+ * string_pool := string
+ * string_pool_index := <uint32_t>
+ * string_pool_length := <uint32_t>
+ * target_crc := <uint32_t>
+ * target_entry_count := <uint32_t>
+ * target_inline_entry_count := <uint32_t>
+ * target_inline_entry_value_count := <uint32_t>
+ * config_count := <uint32_t>
+ * config_index := <uint32_t>
+ * start_value_index := <uint32_t>
+ * value_count := <uint32_t>
+ * target_id := <uint32_t>
+ * target_package_id := <uint8_t>
+ * target_path := string
+ * value_type := <uint8_t>
+ * value_data := <uint32_t>
+ * version := <uint32_t>
*/
#ifndef IDMAP2_INCLUDE_IDMAP2_IDMAP_H_
@@ -70,6 +79,7 @@
#include "android-base/macros.h"
#include "androidfw/ResourceTypes.h"
#include "androidfw/StringPiece.h"
+#include "androidfw/ConfigDescription.h"
#include "idmap2/ResourceContainer.h"
#include "idmap2/ResourceMapping.h"
@@ -165,19 +175,27 @@ class IdmapData {
public:
static std::unique_ptr<const Header> FromBinaryStream(std::istream& stream);
- inline uint32_t GetTargetEntryCount() const {
+ [[nodiscard]] inline uint32_t GetTargetEntryCount() const {
return target_entry_count;
}
- inline uint32_t GetTargetInlineEntryCount() const {
+ [[nodiscard]] inline uint32_t GetTargetInlineEntryCount() const {
return target_entry_inline_count;
}
- inline uint32_t GetOverlayEntryCount() const {
+ [[nodiscard]] inline uint32_t GetTargetInlineEntryValueCount() const {
+ return target_entry_inline_value_count;
+ }
+
+ [[nodiscard]] inline uint32_t GetConfigCount() const {
+ return config_count;
+ }
+
+ [[nodiscard]] inline uint32_t GetOverlayEntryCount() const {
return overlay_entry_count;
}
- inline uint32_t GetStringPoolIndexOffset() const {
+ [[nodiscard]] inline uint32_t GetStringPoolIndexOffset() const {
return string_pool_index_offset;
}
@@ -186,6 +204,8 @@ class IdmapData {
private:
uint32_t target_entry_count;
uint32_t target_entry_inline_count;
+ uint32_t target_entry_inline_value_count;
+ uint32_t config_count;
uint32_t overlay_entry_count;
uint32_t string_pool_index_offset;
Header() = default;
@@ -202,7 +222,7 @@ class IdmapData {
struct TargetInlineEntry {
ResourceId target_id;
- TargetValue value;
+ std::map<ConfigDescription, TargetValue> values;
};
struct OverlayEntry {
@@ -227,11 +247,11 @@ class IdmapData {
return target_inline_entries_;
}
- const std::vector<OverlayEntry>& GetOverlayEntries() const {
+ [[nodiscard]] const std::vector<OverlayEntry>& GetOverlayEntries() const {
return overlay_entries_;
}
- const std::string& GetStringPoolData() const {
+ [[nodiscard]] const std::string& GetStringPoolData() const {
return string_pool_data_;
}
diff --git a/cmds/idmap2/include/idmap2/ResourceMapping.h b/cmds/idmap2/include/idmap2/ResourceMapping.h
index 21862a3635d5..4bad2fa392a2 100644
--- a/cmds/idmap2/include/idmap2/ResourceMapping.h
+++ b/cmds/idmap2/include/idmap2/ResourceMapping.h
@@ -33,7 +33,8 @@
using PolicyBitmask = android::ResTable_overlayable_policy_header::PolicyBitmask;
namespace android::idmap2 {
-using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, TargetValue>>;
+using ConfigMap = std::unordered_map<std::string, TargetValue>;
+using TargetResourceMap = std::map<ResourceId, std::variant<ResourceId, ConfigMap>>;
using OverlayResourceMap = std::map<ResourceId, ResourceId>;
class ResourceMapping {
diff --git a/cmds/idmap2/include/idmap2/ResourceUtils.h b/cmds/idmap2/include/idmap2/ResourceUtils.h
index 8ec749602c4a..af4dd8960cc3 100644
--- a/cmds/idmap2/include/idmap2/ResourceUtils.h
+++ b/cmds/idmap2/include/idmap2/ResourceUtils.h
@@ -46,6 +46,10 @@ struct TargetValue {
struct TargetValueWithConfig {
TargetValue value;
std::string config;
+
+ [[nodiscard]] std::pair<std::string, TargetValue> to_pair() const {
+ return std::make_pair(config, value);
+ }
};
namespace utils {
diff --git a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
index 3bbe9d91deb6..4b271a1ff96f 100644
--- a/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
+++ b/cmds/idmap2/libidmap2/BinaryStreamVisitor.cpp
@@ -70,12 +70,35 @@ void BinaryStreamVisitor::visit(const IdmapData& data) {
}
static constexpr uint16_t kValueSize = 8U;
+ std::vector<std::pair<ConfigDescription, TargetValue>> target_values;
+ target_values.reserve(data.GetHeader()->GetTargetInlineEntryValueCount());
for (const auto& target_entry : data.GetTargetInlineEntries()) {
Write32(target_entry.target_id);
+ Write32(target_values.size());
+ Write32(target_entry.values.size());
+ target_values.insert(
+ target_values.end(), target_entry.values.begin(), target_entry.values.end());
+ }
+
+ std::vector<ConfigDescription> configs;
+ configs.reserve(data.GetHeader()->GetConfigCount());
+ for (const auto& target_entry_value : target_values) {
+ auto config_it = find(configs.begin(), configs.end(), target_entry_value.first);
+ if (config_it != configs.end()) {
+ Write32(config_it - configs.begin());
+ } else {
+ Write32(configs.size());
+ configs.push_back(target_entry_value.first);
+ }
Write16(kValueSize);
Write8(0U); // padding
- Write8(target_entry.value.data_type);
- Write32(target_entry.value.data_value);
+ Write8(target_entry_value.second.data_type);
+ Write32(target_entry_value.second.data_value);
+ }
+
+ for( auto& cd : configs) {
+ cd.swapHtoD();
+ stream_.write(reinterpret_cast<char*>(&cd), sizeof(cd));
}
for (const auto& overlay_entry : data.GetOverlayEntries()) {
@@ -89,6 +112,8 @@ void BinaryStreamVisitor::visit(const IdmapData& data) {
void BinaryStreamVisitor::visit(const IdmapData::Header& header) {
Write32(header.GetTargetEntryCount());
Write32(header.GetTargetInlineEntryCount());
+ Write32(header.GetTargetInlineEntryValueCount());
+ Write32(header.GetConfigCount());
Write32(header.GetOverlayEntryCount());
Write32(header.GetStringPoolIndexOffset());
}
diff --git a/cmds/idmap2/libidmap2/Idmap.cpp b/cmds/idmap2/libidmap2/Idmap.cpp
index 06650f681b24..444f91d144a2 100644
--- a/cmds/idmap2/libidmap2/Idmap.cpp
+++ b/cmds/idmap2/libidmap2/Idmap.cpp
@@ -186,6 +186,8 @@ std::unique_ptr<const IdmapData::Header> IdmapData::Header::FromBinaryStream(std
std::unique_ptr<IdmapData::Header> idmap_data_header(new IdmapData::Header());
if (!Read32(stream, &idmap_data_header->target_entry_count) ||
!Read32(stream, &idmap_data_header->target_entry_inline_count) ||
+ !Read32(stream, &idmap_data_header->target_entry_inline_value_count) ||
+ !Read32(stream, &idmap_data_header->config_count) ||
!Read32(stream, &idmap_data_header->overlay_entry_count) ||
!Read32(stream, &idmap_data_header->string_pool_index_offset)) {
return nullptr;
@@ -207,20 +209,59 @@ std::unique_ptr<const IdmapData> IdmapData::FromBinaryStream(std::istream& strea
if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &target_entry.overlay_id)) {
return nullptr;
}
- data->target_entries_.push_back(target_entry);
+ data->target_entries_.emplace_back(target_entry);
}
// Read the mapping of target resource id to inline overlay values.
- uint8_t unused1;
- uint16_t unused2;
+ std::vector<std::tuple<TargetInlineEntry, uint32_t, uint32_t>> target_inline_entries;
for (size_t i = 0; i < data->header_->GetTargetInlineEntryCount(); i++) {
TargetInlineEntry target_entry{};
- if (!Read32(stream, &target_entry.target_id) || !Read16(stream, &unused2) ||
- !Read8(stream, &unused1) || !Read8(stream, &target_entry.value.data_type) ||
- !Read32(stream, &target_entry.value.data_value)) {
+ uint32_t entry_offset;
+ uint32_t entry_count;
+ if (!Read32(stream, &target_entry.target_id) || !Read32(stream, &entry_offset)
+ || !Read32(stream, &entry_count)) {
return nullptr;
}
- data->target_inline_entries_.push_back(target_entry);
+ target_inline_entries.emplace_back(target_entry, entry_offset, entry_count);
+ }
+
+ // Read the inline overlay resource values
+ std::vector<std::pair<uint32_t, TargetValue>> target_values;
+ uint8_t unused1;
+ uint16_t unused2;
+ for (size_t i = 0; i < data->header_->GetTargetInlineEntryValueCount(); i++) {
+ uint32_t config_index;
+ if (!Read32(stream, &config_index)) {
+ return nullptr;
+ }
+ TargetValue value;
+ if (!Read16(stream, &unused2)
+ || !Read8(stream, &unused1)
+ || !Read8(stream, &value.data_type)
+ || !Read32(stream, &value.data_value)) {
+ return nullptr;
+ }
+ target_values.emplace_back(config_index, value);
+ }
+
+ // Read the configurations
+ std::vector<ConfigDescription> configurations;
+ for (size_t i = 0; i < data->header_->GetConfigCount(); i++) {
+ ConfigDescription cd;
+ if (!stream.read(reinterpret_cast<char*>(&cd), sizeof(ConfigDescription))) {
+ return nullptr;
+ }
+ configurations.emplace_back(cd);
+ }
+
+ // Construct complete target inline entries
+ for (auto [target_entry, entry_offset, entry_count] : target_inline_entries) {
+ for(size_t i = 0; i < entry_count; i++) {
+ const auto& target_value = target_values[entry_offset + i];
+ const auto& config = configurations[target_value.first];
+ target_entry.values[config] = target_value.second;
+ }
+ data->target_inline_entries_.emplace_back(target_entry);
}
// Read the mapping of overlay resource id to target resource id.
@@ -278,12 +319,21 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
std::unique_ptr<IdmapData> data(new IdmapData());
data->string_pool_data_ = resource_mapping.GetStringPoolData().to_string();
+ uint32_t inline_value_count = 0;
+ std::set<std::string> config_set;
for (const auto& mapping : resource_mapping.GetTargetToOverlayMap()) {
if (auto overlay_resource = std::get_if<ResourceId>(&mapping.second)) {
data->target_entries_.push_back({mapping.first, *overlay_resource});
} else {
- data->target_inline_entries_.push_back(
- {mapping.first, std::get<TargetValue>(mapping.second)});
+ std::map<ConfigDescription, TargetValue> values;
+ for (const auto& [config, value] : std::get<ConfigMap>(mapping.second)) {
+ config_set.insert(config);
+ ConfigDescription cd;
+ ConfigDescription::Parse(config, &cd);
+ values[cd] = value;
+ inline_value_count++;
+ }
+ data->target_inline_entries_.push_back({mapping.first, values});
}
}
@@ -295,6 +345,8 @@ Result<std::unique_ptr<const IdmapData>> IdmapData::FromResourceMapping(
data_header->target_entry_count = static_cast<uint32_t>(data->target_entries_.size());
data_header->target_entry_inline_count =
static_cast<uint32_t>(data->target_inline_entries_.size());
+ data_header->target_entry_inline_value_count = inline_value_count;
+ data_header->config_count = config_set.size();
data_header->overlay_entry_count = static_cast<uint32_t>(data->overlay_entries_.size());
data_header->string_pool_index_offset = resource_mapping.GetStringPoolOffset();
data->header_ = std::move(data_header);
diff --git a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
index d10a2785aaba..a44fa756aa1c 100644
--- a/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/PrettyPrintVisitor.cpp
@@ -94,14 +94,17 @@ void PrettyPrintVisitor::visit(const IdmapData& data) {
}
for (auto& target_entry : data.GetTargetInlineEntries()) {
- stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id)
- << utils::DataTypeToString(target_entry.value.data_type);
-
- if (target_entry.value.data_type == Res_value::TYPE_STRING) {
- auto str = string_pool.stringAt(target_entry.value.data_value - string_pool_offset);
- stream_ << " \"" << str.value_or(StringPiece16(u"")) << "\"";
- } else {
- stream_ << " " << base::StringPrintf("0x%08x", target_entry.value.data_value);
+ for(auto iter = target_entry.values.begin(); iter != target_entry.values.end(); ++iter) {
+ auto value = iter->second;
+ stream_ << TAB << base::StringPrintf("0x%08x -> ", target_entry.target_id)
+ << utils::DataTypeToString(value.data_type);
+
+ if (value.data_type == Res_value::TYPE_STRING) {
+ auto str = string_pool.stringAt(value.data_value - string_pool_offset);
+ stream_ << " \"" << str.value_or(StringPiece16(u"")) << "\"";
+ } else {
+ stream_ << " " << base::StringPrintf("0x%08x", value.data_value);
+ }
}
std::string target_name = kUnknownResourceName;
diff --git a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
index 779538c617f4..3531cd7c2f36 100644
--- a/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
+++ b/cmds/idmap2/libidmap2/RawPrintVisitor.cpp
@@ -89,22 +89,30 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
print(target_entry.target_id, "target id");
}
+
pad(sizeof(Res_value::size) + sizeof(Res_value::res0));
- print(target_entry.value.data_type, "type: %s",
- utils::DataTypeToString(target_entry.value.data_type).data());
+ for (auto& target_entry_value : target_entry.values) {
+ auto value = target_entry_value.second;
- Result<std::string> overlay_name(Error(""));
- if (overlay_ != nullptr &&
- (target_entry.value.data_value == Res_value::TYPE_REFERENCE ||
- target_entry.value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
- overlay_name = overlay_->GetResourceName(target_entry.value.data_value);
- }
+ print(target_entry_value.first.to_string(), false, "config: %s",
+ target_entry_value.first.toString().string());
- if (overlay_name) {
- print(target_entry.value.data_value, "data: %s", overlay_name->c_str());
- } else {
- print(target_entry.value.data_value, "data");
+ print(value.data_type, "type: %s",
+ utils::DataTypeToString(value.data_type).data());
+
+ Result<std::string> overlay_name(Error(""));
+ if (overlay_ != nullptr &&
+ (value.data_value == Res_value::TYPE_REFERENCE ||
+ value.data_value == Res_value::TYPE_DYNAMIC_REFERENCE)) {
+ overlay_name = overlay_->GetResourceName(value.data_value);
+ }
+
+ if (overlay_name) {
+ print(value.data_value, "data: %s", overlay_name->c_str());
+ } else {
+ print(value.data_value, "data");
+ }
}
}
@@ -138,6 +146,8 @@ void RawPrintVisitor::visit(const IdmapData& data ATTRIBUTE_UNUSED) {
void RawPrintVisitor::visit(const IdmapData::Header& header) {
print(header.GetTargetEntryCount(), "target entry count");
print(header.GetTargetInlineEntryCount(), "target inline entry count");
+ print(header.GetTargetInlineEntryValueCount(), "target inline entry value count");
+ print(header.GetConfigCount(), "config count");
print(header.GetOverlayEntryCount(), "overlay entry count");
print(header.GetStringPoolIndexOffset(), "string pool index offset");
}
diff --git a/cmds/idmap2/libidmap2/ResourceMapping.cpp b/cmds/idmap2/libidmap2/ResourceMapping.cpp
index 8ebe5aa46e73..bb31c11d629c 100644
--- a/cmds/idmap2/libidmap2/ResourceMapping.cpp
+++ b/cmds/idmap2/libidmap2/ResourceMapping.cpp
@@ -161,14 +161,13 @@ Result<ResourceMapping> ResourceMapping::FromContainers(const TargetResourceCont
Result<Unit> ResourceMapping::AddMapping(
ResourceId target_resource,
const std::variant<OverlayData::ResourceIdValue, TargetValueWithConfig>& value) {
- if (target_map_.find(target_resource) != target_map_.end()) {
- return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
- }
-
// TODO(141485591): Ensure that the overlay type is compatible with the target type. If the
// runtime types are not compatible, it could cause runtime crashes when the resource is resolved.
if (auto overlay_resource = std::get_if<OverlayData::ResourceIdValue>(&value)) {
+ if (target_map_.find(target_resource) != target_map_.end()) {
+ return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
+ }
target_map_.insert(std::make_pair(target_resource, overlay_resource->overlay_id));
if (overlay_resource->rewrite_id) {
// An overlay resource can override multiple target resources at once. Rewrite the overlay
@@ -176,8 +175,18 @@ Result<Unit> ResourceMapping::AddMapping(
overlay_map_.insert(std::make_pair(overlay_resource->overlay_id, target_resource));
}
} else {
- auto overlay_value = std::get<TargetValueWithConfig>(value);
- target_map_.insert(std::make_pair(target_resource, overlay_value.value));
+ auto[iter, inserted] = target_map_.try_emplace(target_resource, ConfigMap());
+ auto& resource_value = iter->second;
+ if (!inserted && std::holds_alternative<ResourceId>(resource_value)) {
+ return Error(R"(target resource id "0x%08x" mapped to multiple values)", target_resource);
+ }
+ auto& config_map = std::get<ConfigMap>(resource_value);
+ const auto& config_value = std::get<TargetValueWithConfig>(value);
+ if (config_map.find(config_value.config) != config_map.end()) {
+ return Error(R"(target resource id "0x%08x" mapped to multiple values with the same config)",
+ target_resource);
+ }
+ config_map.insert(config_value.to_pair());
}
return Unit{};
diff --git a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
index bf6332742787..f1eeab9c803b 100644
--- a/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
+++ b/cmds/idmap2/tests/BinaryStreamVisitorTests.cpp
@@ -84,8 +84,10 @@ TEST(BinaryStreamVisitorTests, CreateBinaryStreamViaBinaryStreamVisitor) {
const auto& target_inline_entries2 = data2->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries1.size(), target_inline_entries2.size());
ASSERT_EQ(target_inline_entries1[0].target_id, target_inline_entries2[0].target_id);
- ASSERT_EQ(target_inline_entries1[0].value.data_type, target_inline_entries2[0].value.data_type);
- ASSERT_EQ(target_inline_entries1[0].value.data_value, target_inline_entries2[0].value.data_value);
+ ASSERT_EQ(target_inline_entries1[0].values.begin()->second.data_type,
+ target_inline_entries2[0].values.begin()->second.data_type);
+ ASSERT_EQ(target_inline_entries1[0].values.begin()->second.data_value,
+ target_inline_entries2[0].values.begin()->second.data_value);
const auto& overlay_entries1 = data1->GetOverlayEntries();
const auto& overlay_entries2 = data2->GetOverlayEntries();
diff --git a/cmds/idmap2/tests/IdmapTests.cpp b/cmds/idmap2/tests/IdmapTests.cpp
index ee9a424b3050..7b7dc17deea4 100644
--- a/cmds/idmap2/tests/IdmapTests.cpp
+++ b/cmds/idmap2/tests/IdmapTests.cpp
@@ -47,10 +47,11 @@ namespace android::idmap2 {
ASSERT_EQ((entry).target_id, (target_resid)); \
ASSERT_EQ((entry).overlay_id, (overlay_resid))
-#define ASSERT_TARGET_INLINE_ENTRY(entry, target_resid, expected_type, expected_value) \
- ASSERT_EQ((entry).target_id, target_resid); \
- ASSERT_EQ((entry).value.data_type, (expected_type)); \
- ASSERT_EQ((entry).value.data_value, (expected_value))
+#define ASSERT_TARGET_INLINE_ENTRY(entry, target_resid, ex_config, expected_type, expected_value) \
+ ASSERT_EQ((entry).target_id, target_resid); \
+ ASSERT_EQ((entry).values.begin()->first.to_string(), (ex_config)); \
+ ASSERT_EQ((entry).values.begin()->second.data_type, (expected_type)); \
+ ASSERT_EQ((entry).values.begin()->second.data_value, (expected_value))
#define ASSERT_OVERLAY_ENTRY(entry, overlay_resid, target_resid) \
ASSERT_EQ((entry).overlay_id, (overlay_resid)); \
@@ -67,7 +68,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromBinaryStream) {
std::unique_ptr<const IdmapHeader> header = IdmapHeader::FromBinaryStream(stream);
ASSERT_THAT(header, NotNull());
ASSERT_EQ(header->GetMagic(), 0x504d4449U);
- ASSERT_EQ(header->GetVersion(), 0x08U);
+ ASSERT_EQ(header->GetVersion(), 0x09U);
ASSERT_EQ(header->GetTargetCrc(), 0x1234U);
ASSERT_EQ(header->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(header->GetFulfilledPolicies(), 0x11);
@@ -122,8 +123,8 @@ TEST(IdmapTests, CreateIdmapDataFromBinaryStream) {
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 1U);
- ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
- 0x12345678);
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, "land-xxhdpi-v7",
+ Res_value::TYPE_INT_HEX, 0x12345678);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 3U);
@@ -142,7 +143,7 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x09U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), 0x1234U);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), 0x5678U);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), kIdmapRawDataPolicies);
@@ -165,8 +166,8 @@ TEST(IdmapTests, CreateIdmapFromBinaryStream) {
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 1U);
- ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, Res_value::TYPE_INT_HEX,
- 0x12345678);
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], 0x7f040000, "land-xxhdpi-v7",
+ Res_value::TYPE_INT_HEX, 0x12345678);
const auto& overlay_entries = data->GetOverlayEntries();
ASSERT_EQ(target_entries.size(), 3U);
@@ -203,7 +204,7 @@ TEST(IdmapTests, CreateIdmapHeaderFromApkAssets) {
ASSERT_THAT(idmap->GetHeader(), NotNull());
ASSERT_EQ(idmap->GetHeader()->GetMagic(), 0x504d4449U);
- ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x08U);
+ ASSERT_EQ(idmap->GetHeader()->GetVersion(), 0x09U);
ASSERT_EQ(idmap->GetHeader()->GetTargetCrc(), android::idmap2::TestConstants::TARGET_CRC);
ASSERT_EQ(idmap->GetHeader()->GetOverlayCrc(), android::idmap2::TestConstants::OVERLAY_CRC);
ASSERT_EQ(idmap->GetHeader()->GetFulfilledPolicies(), PolicyFlags::PUBLIC);
@@ -261,9 +262,9 @@ TEST(IdmapTests, FabricatedOverlay) {
auto frro = FabricatedOverlay::Builder("com.example.overlay", "SandTheme", "test.target")
.SetOverlayable("TestResources")
- .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "")
- .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "")
- .SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "")
+ .SetResourceValue("integer/int1", Res_value::TYPE_INT_DEC, 2U, "land-xxhdpi-v7")
+ .SetResourceValue("string/str1", Res_value::TYPE_REFERENCE, 0x7f010000, "land")
+ .SetResourceValue("string/str2", Res_value::TYPE_STRING, "foobar", "xxhdpi-v7")
.Build();
ASSERT_TRUE(frro);
@@ -295,11 +296,11 @@ TEST(IdmapTests, FabricatedOverlay) {
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 3U);
- ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, "land-xxhdpi-v7",
Res_value::TYPE_INT_DEC, 2U);
- ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, "land",
Res_value::TYPE_REFERENCE, 0x7f010000);
- ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str2,
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[2], R::target::string::str2, "xxhdpi-v7",
Res_value::TYPE_STRING,
(uint32_t) (string_pool.indexOfString(u"foobar", 6)).value_or(-1));
}
@@ -442,9 +443,9 @@ TEST(IdmapTests, CreateIdmapDataInlineResources) {
constexpr size_t overlay_string_pool_size = 10U;
const auto& target_inline_entries = data->GetTargetInlineEntries();
ASSERT_EQ(target_inline_entries.size(), 2U);
- ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1,
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[0], R::target::integer::int1, std::string(),
Res_value::TYPE_INT_DEC, 73U); // -> 73
- ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1,
+ ASSERT_TARGET_INLINE_ENTRY(target_inline_entries[1], R::target::string::str1, std::string(),
Res_value::TYPE_STRING,
overlay_string_pool_size + 0U); // -> "Hello World"
diff --git a/cmds/idmap2/tests/RawPrintVisitorTests.cpp b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
index a6371cb74f2e..7112eeb9ea0c 100644
--- a/cmds/idmap2/tests/RawPrintVisitorTests.cpp
+++ b/cmds/idmap2/tests/RawPrintVisitorTests.cpp
@@ -64,7 +64,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000009 version\n", stream.str());
ASSERT_CONTAINS_REGEX(
StringPrintf(ADDRESS "%s target crc\n", android::idmap2::TestConstants::TARGET_CRC_STRING),
stream.str());
@@ -75,6 +75,8 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitor) {
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 enforce overlayable\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 target entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 target inline entry count", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000000 target inline entry value count", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000000 config count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 overlay entry count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "0000000a string pool index offset", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f010000 target id: integer/int1", stream.str());
@@ -111,7 +113,7 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
(*idmap)->accept(&visitor);
ASSERT_CONTAINS_REGEX(ADDRESS "504d4449 magic\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "00000008 version\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000009 version\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00001234 target crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00005678 overlay crc\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000011 fulfilled policies: public|signature\n", stream.str());
@@ -124,17 +126,25 @@ TEST(RawPrintVisitorTests, CreateRawPrintVisitorWithoutAccessToApks) {
ASSERT_CONTAINS_REGEX(ADDRESS "........ overlay name: OverlayName\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 target entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry count\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000001 target inline entry value count", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "00000001 config count", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000003 overlay entry count\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000000 string pool index offset\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
- ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 target id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f030000 target id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f030000 overlay id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f030001 overlay id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "7f040000 target id\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "0000000e config: land-xxhdpi-v7 size\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "........ config: land-xxhdpi-v7\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS " 11 type: integer\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "12345678 data\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f020000 overlay id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "7f030002 target id\n", stream.str());
ASSERT_CONTAINS_REGEX(ADDRESS "00000004 string pool size\n", stream.str());
- ASSERT_CONTAINS_REGEX("000000a4: ........ string pool\n", stream.str());
+ ASSERT_CONTAINS_REGEX(ADDRESS "........ string pool\n", stream.str());
}
} // namespace android::idmap2
diff --git a/cmds/idmap2/tests/ResourceMappingTests.cpp b/cmds/idmap2/tests/ResourceMappingTests.cpp
index ca9a444bcb05..016d427e7452 100644
--- a/cmds/idmap2/tests/ResourceMappingTests.cpp
+++ b/cmds/idmap2/tests/ResourceMappingTests.cpp
@@ -111,19 +111,20 @@ Result<Unit> MappingExists(const ResourceMapping& mapping, const ResourceId& tar
return Error("Failed to find mapping for target resource");
}
- auto actual_overlay_value = std::get_if<TargetValue>(&entry_map->second);
- if (actual_overlay_value == nullptr) {
+ auto config_map = std::get_if<ConfigMap>(&entry_map->second);
+ if (config_map == nullptr || config_map->empty()) {
return Error("Target resource is not mapped to an inline value");
}
+ auto actual_overlay_value = config_map->begin()->second;
- if (actual_overlay_value->data_type != type) {
+ if (actual_overlay_value.data_type != type) {
return Error(R"(Expected type: "0x%02x" Actual type: "0x%02x")", type,
- actual_overlay_value->data_type);
+ actual_overlay_value.data_type);
}
- if (actual_overlay_value->data_value != value) {
+ if (actual_overlay_value.data_value != value) {
return Error(R"(Expected value: "0x%08x" Actual value: "0x%08x")", type,
- actual_overlay_value->data_value);
+ actual_overlay_value.data_value);
}
return Result<Unit>({});
diff --git a/cmds/idmap2/tests/TestHelpers.h b/cmds/idmap2/tests/TestHelpers.h
index 6b5f3a8a98eb..45740e2d5a9e 100644
--- a/cmds/idmap2/tests/TestHelpers.h
+++ b/cmds/idmap2/tests/TestHelpers.h
@@ -30,7 +30,7 @@ const unsigned char kIdmapRawData[] = {
0x49, 0x44, 0x4d, 0x50,
// 0x4: version
- 0x08, 0x00, 0x00, 0x00,
+ 0x09, 0x00, 0x00, 0x00,
// 0x8: target crc
0x34, 0x12, 0x00, 0x00,
@@ -76,66 +76,123 @@ const unsigned char kIdmapRawData[] = {
// 0x58: target_inline_entry_count
0x01, 0x00, 0x00, 0x00,
- // 0x5c: overlay_entry_count
+ // 0x5c: target_inline_entry_value_count
+ 0x01, 0x00, 0x00, 0x00,
+
+ // 0x60: config_count
+ 0x01, 0x00, 0x00, 0x00,
+
+ // 0x64: overlay_entry_count
0x03, 0x00, 0x00, 0x00,
- // 0x60: string_pool_offset
+ // 0x68: string_pool_offset
0x00, 0x00, 0x00, 0x00,
// TARGET ENTRIES
- // 0x64: target id (0x7f020000)
+ // 0x6c: target id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
- // 0x68: overlay_id (0x7f020000)
+ // 0x70: overlay_id (0x7f020000)
0x00, 0x00, 0x02, 0x7f,
- // 0x6c: target id (0x7f030000)
+ // 0x74: target id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
- // 0x70: overlay_id (0x7f030000)
+ // 0x78: overlay_id (0x7f030000)
0x00, 0x00, 0x03, 0x7f,
- // 0x74: target id (0x7f030002)
+ // 0x7c: target id (0x7f030002)
0x02, 0x00, 0x03, 0x7f,
- // 0x78: overlay_id (0x7f030001)
+ // 0x80: overlay_id (0x7f030001)
0x01, 0x00, 0x03, 0x7f,
// INLINE TARGET ENTRIES
- // 0x7c: target_id
+ // 0x84: target_id
0x00, 0x00, 0x04, 0x7f,
- // 0x80: Res_value::size (value ignored by idmap)
+ // 0x88: start value index
+ 0x00, 0x00, 0x00, 0x00,
+
+ // 0x8c: value count
+ 0x01, 0x00, 0x00, 0x00,
+
+ // INLINE TARGET ENTRY VALUES
+
+ // 0x90: config index
+ 0x00, 0x00, 0x00, 0x00,
+
+ // 0x94: Res_value::size (value ignored by idmap)
0x08, 0x00,
- // 0x82: Res_value::res0 (value ignored by idmap)
+ // 0x98: Res_value::res0 (value ignored by idmap)
0x00,
- // 0x83: Res_value::dataType (TYPE_INT_HEX)
+ // 0x9c: Res_value::dataType (TYPE_INT_HEX)
0x11,
- // 0x84: Res_value::data
+ // 0xa0: Res_value::data
0x78, 0x56, 0x34, 0x12,
+ // CONFIGURATIONS
+
+ // 0xa4: ConfigDescription
+ // size
+ 0x40, 0x00, 0x00, 0x00,
+ // 0xa8: imsi
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xac: locale
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xb0: screenType
+ 0x02, 0x00, 0xe0, 0x01,
+ // 0xb4: input
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xb8: screenSize
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xbc: version
+ 0x07, 0x00, 0x00, 0x00,
+ // 0xc0: screenConfig
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xc4: screenSizeDp
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xc8: localeScript
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xcc: localVariant(1)
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xd0: localVariant(2)
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xd4: screenConfig2
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xd8: localeScriptWasComputed
+ 0x00,
+ // 0xd9: localeNumberingSystem(1)
+ 0x00, 0x00, 0x00, 0x00,
+ // 0xdd: localeNumberingSystem(2)
+ 0x00, 0x00, 0x00, 0x00,
+
+ // 0xe1: padding
+ 0x00, 0x00, 0x00,
+
+
// OVERLAY ENTRIES
- // 0x88: 0x7f020000 -> 0x7f020000
+ // 0xe4: 0x7f020000 -> 0x7f020000
0x00, 0x00, 0x02, 0x7f, 0x00, 0x00, 0x02, 0x7f,
- // 0x90: 0x7f030000 -> 0x7f030000
+ // 0xec: 0x7f030000 -> 0x7f030000
0x00, 0x00, 0x03, 0x7f, 0x00, 0x00, 0x03, 0x7f,
- // 0x98: 0x7f030001 -> 0x7f030002
+ // 0xf4: 0x7f030001 -> 0x7f030002
0x01, 0x00, 0x03, 0x7f, 0x02, 0x00, 0x03, 0x7f,
- // 0xa0: string pool
+ // 0xfc: string pool
// string length,
0x04, 0x00, 0x00, 0x00,
- // 0xa4 string contents "test"
+ // 0x100 string contents "test"
0x74, 0x65, 0x73, 0x74};
-const unsigned int kIdmapRawDataLen = 0xa8;
+const unsigned int kIdmapRawDataLen = 0x104;
const unsigned int kIdmapRawDataOffset = 0x54;
const unsigned int kIdmapRawDataTargetCrc = 0x1234;
const unsigned int kIdmapRawOverlayCrc = 0x5678;
diff --git a/core/api/current.txt b/core/api/current.txt
index 4d25ad7d8b37..e30761f82992 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -4426,6 +4426,7 @@ package android.app {
method public void readFromParcel(android.os.Parcel);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ActivityManager.MemoryInfo> CREATOR;
+ field public long advertisedMem;
field public long availMem;
field public boolean lowMemory;
field public long threshold;
@@ -5933,6 +5934,7 @@ package android.app {
field public static final String EXTRA_BIG_TEXT = "android.bigText";
field public static final String EXTRA_CALL_IS_VIDEO = "android.callIsVideo";
field public static final String EXTRA_CALL_PERSON = "android.callPerson";
+ field public static final String EXTRA_CALL_TYPE = "android.callType";
field public static final String EXTRA_CHANNEL_GROUP_ID = "android.intent.extra.CHANNEL_GROUP_ID";
field public static final String EXTRA_CHANNEL_ID = "android.intent.extra.CHANNEL_ID";
field public static final String EXTRA_CHRONOMETER_COUNT_DOWN = "android.chronometerCountDown";
@@ -6240,6 +6242,10 @@ package android.app {
method @NonNull public android.app.Notification.CallStyle setIsVideo(boolean);
method @NonNull public android.app.Notification.CallStyle setVerificationIcon(@Nullable android.graphics.drawable.Icon);
method @NonNull public android.app.Notification.CallStyle setVerificationText(@Nullable CharSequence);
+ field public static final int CALL_TYPE_INCOMING = 1; // 0x1
+ field public static final int CALL_TYPE_ONGOING = 2; // 0x2
+ field public static final int CALL_TYPE_SCREENING = 3; // 0x3
+ field public static final int CALL_TYPE_UNKNOWN = 0; // 0x0
}
public static final class Notification.CarExtender implements android.app.Notification.Extender {
@@ -11936,6 +11942,7 @@ package android.content.pm {
field @Deprecated public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
field public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
field public static final String FEATURE_CONTROLS = "android.software.controls";
+ field public static final String FEATURE_CREDENTIALS = "android.software.credentials";
field public static final String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
field public static final String FEATURE_EMBEDDED = "android.hardware.type.embedded";
field public static final String FEATURE_ETHERNET = "android.hardware.ethernet";
@@ -15679,6 +15686,7 @@ package android.graphics {
method public static android.graphics.Typeface createFromFile(@Nullable String);
method public static android.graphics.Typeface defaultFromStyle(int);
method public int getStyle();
+ method @Nullable public final String getSystemFontFamilyName();
method @IntRange(from=0, to=1000) public int getWeight();
method public final boolean isBold();
method public final boolean isItalic();
@@ -18638,8 +18646,10 @@ package android.hardware.lights {
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.lights.Light> CREATOR;
field public static final int LIGHT_CAPABILITY_BRIGHTNESS = 1; // 0x1
- field public static final int LIGHT_CAPABILITY_RGB = 0; // 0x0
+ field public static final int LIGHT_CAPABILITY_COLOR_RGB = 2; // 0x2
+ field @Deprecated public static final int LIGHT_CAPABILITY_RGB = 0; // 0x0
field public static final int LIGHT_TYPE_INPUT = 10001; // 0x2711
+ field public static final int LIGHT_TYPE_KEYBOARD_BACKLIGHT = 10003; // 0x2713
field public static final int LIGHT_TYPE_MICROPHONE = 8; // 0x8
field public static final int LIGHT_TYPE_PLAYER_ID = 10002; // 0x2712
}
@@ -18919,8 +18929,8 @@ package android.inputmethodservice {
method public android.view.View onCreateCandidatesView();
method public android.view.View onCreateExtractTextView();
method @Nullable public android.view.inputmethod.InlineSuggestionsRequest onCreateInlineSuggestionsRequest(@NonNull android.os.Bundle);
- method public android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface();
- method public android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
+ method @Deprecated public android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodImpl onCreateInputMethodInterface();
+ method @Deprecated public android.inputmethodservice.AbstractInputMethodService.AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface();
method public android.view.View onCreateInputView();
method protected void onCurrentInputMethodSubtypeChanged(android.view.inputmethod.InputMethodSubtype);
method public void onDisplayCompletions(android.view.inputmethod.CompletionInfo[]);
@@ -20057,6 +20067,7 @@ package android.media {
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 public static final int CHANNEL_OUT_6POINT1 = 1276; // 0x4fc
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
@@ -22427,6 +22438,7 @@ package android.media {
field public static final String MIMETYPE_AUDIO_SCRAMBLED = "audio/scrambled";
field public static final String MIMETYPE_AUDIO_VORBIS = "audio/vorbis";
field public static final String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
+ field public static final String MIMETYPE_IMAGE_AVIF = "image/avif";
field public static final String MIMETYPE_TEXT_CEA_608 = "text/cea-608";
field public static final String MIMETYPE_TEXT_CEA_708 = "text/cea-708";
field public static final String MIMETYPE_TEXT_SUBRIP = "application/x-subrip";
@@ -41545,6 +41557,10 @@ package android.telephony {
field public static final String KEY_OPPORTUNISTIC_NETWORK_PING_PONG_TIME_LONG = "opportunistic_network_ping_pong_time_long";
field public static final String KEY_PING_TEST_BEFORE_DATA_SWITCH_BOOL = "ping_test_before_data_switch_bool";
field public static final String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
+ field public static final String KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG = "premium_capability_notification_backoff_hysteresis_time_millis_long";
+ field public static final String KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG = "premium_capability_notification_display_timeout_millis_long";
+ field public static final String KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG = "premium_capability_purchase_condition_backoff_hysteresis_time_millis_long";
+ field public static final String KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING = "premium_capability_purchase_url_string";
field public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL = "prevent_clir_activation_and_deactivation_code_bool";
field public static final String KEY_RADIO_RESTART_FAILURE_CAUSES_INT_ARRAY = "radio_restart_failure_causes_int_array";
field public static final String KEY_RCS_CONFIG_SERVER_URL_STRING = "rcs_config_server_url_string";
@@ -41572,11 +41588,12 @@ package android.telephony {
field public static final String KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL = "show_signal_strength_in_sim_status_bool";
field public static final String KEY_SHOW_VIDEO_CALL_CHARGES_ALERT_DIALOG_BOOL = "show_video_call_charges_alert_dialog_bool";
field public static final String KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL = "show_wfc_location_privacy_policy_bool";
- field public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
+ field @Deprecated public static final String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
field public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING = "subscription_group_uuid_string";
+ field public static final String KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY = "supported_premium_capabilities_int_array";
field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL = "supports_device_to_device_communication_using_rtp_bool";
@@ -43068,6 +43085,7 @@ package android.telephony {
method @NonNull public int[] getThresholds();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SignalThresholdInfo> CREATOR;
+ field public static final int SIGNAL_MEASUREMENT_TYPE_ECNO = 9; // 0x9
field public static final int SIGNAL_MEASUREMENT_TYPE_RSCP = 2; // 0x2
field public static final int SIGNAL_MEASUREMENT_TYPE_RSRP = 3; // 0x3
field public static final int SIGNAL_MEASUREMENT_TYPE_RSRQ = 4; // 0x4
@@ -43640,6 +43658,7 @@ package android.telephony {
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, "android.permission.READ_PRIVILEGED_PHONE_STATE"}) public boolean isModemEnabledForSlot(int);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int isMultiSimSupported();
method public boolean isNetworkRoaming();
+ method @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE) public boolean isPremiumCapabilityAvailableForPurchase(int);
method public boolean isRadioInterfaceCapabilitySupported(@NonNull String);
method public boolean isRttSupported();
method public boolean isSmsCapable();
@@ -43648,6 +43667,7 @@ package android.telephony {
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
method @Deprecated public void listen(android.telephony.PhoneStateListener, int);
+ method @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE) public void purchasePremiumCapability(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void rebootModem();
method public void registerTelephonyCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback);
method public void registerTelephonyCallback(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyCallback);
@@ -43817,6 +43837,20 @@ package android.telephony {
field public static final int PHONE_TYPE_GSM = 1; // 0x1
field public static final int PHONE_TYPE_NONE = 0; // 0x0
field public static final int PHONE_TYPE_SIP = 3; // 0x3
+ field public static final int PREMIUM_CAPABILITY_REALTIME_INTERACTIVE_TRAFFIC = 1; // 0x1
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS = 4; // 0x4
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED = 3; // 0x3
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED = 7; // 0x7
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR = 8; // 0x8
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED = 10; // 0xa
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED = 13; // 0xd
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE = 12; // 0xc
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED = 11; // 0xb
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS = 1; // 0x1
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED = 2; // 0x2
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT = 9; // 0x9
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED = 6; // 0x6
+ field public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED = 5; // 0x5
field public static final int SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION = 2; // 0x2
field public static final int SET_OPPORTUNISTIC_SUB_NO_OPPORTUNISTIC_SUB_AVAILABLE = 3; // 0x3
field public static final int SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION = 4; // 0x4
@@ -45165,6 +45199,7 @@ package android.text {
method public final int getLineAscent(int);
method public final int getLineBaseline(int);
method public final int getLineBottom(int);
+ method public int getLineBottom(int, boolean);
method public int getLineBounds(int, android.graphics.Rect);
method public abstract boolean getLineContainsTab(int);
method public abstract int getLineCount();
@@ -48771,6 +48806,10 @@ package android.view {
field public static final int KEYCODE_STEM_2 = 266; // 0x10a
field public static final int KEYCODE_STEM_3 = 267; // 0x10b
field public static final int KEYCODE_STEM_PRIMARY = 264; // 0x108
+ field public static final int KEYCODE_STYLUS_BUTTON_PRIMARY = 308; // 0x134
+ field public static final int KEYCODE_STYLUS_BUTTON_SECONDARY = 309; // 0x135
+ field public static final int KEYCODE_STYLUS_BUTTON_TAIL = 311; // 0x137
+ field public static final int KEYCODE_STYLUS_BUTTON_TERTIARY = 310; // 0x136
field public static final int KEYCODE_SWITCH_CHARSET = 95; // 0x5f
field public static final int KEYCODE_SYM = 63; // 0x3f
field public static final int KEYCODE_SYSRQ = 120; // 0x78
@@ -49222,6 +49261,7 @@ package android.view {
field public static final int CLASSIFICATION_AMBIGUOUS_GESTURE = 1; // 0x1
field public static final int CLASSIFICATION_DEEP_PRESS = 2; // 0x2
field public static final int CLASSIFICATION_NONE = 0; // 0x0
+ field public static final int CLASSIFICATION_TWO_FINGER_SWIPE = 3; // 0x3
field @NonNull public static final android.os.Parcelable.Creator<android.view.MotionEvent> CREATOR;
field public static final int EDGE_BOTTOM = 2; // 0x2
field public static final int EDGE_LEFT = 4; // 0x4
@@ -51846,10 +51886,11 @@ package android.view.accessibility {
method public void setSpeechStateChangeTypes(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
+ field public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 1024; // 0x400
field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
field public static final int CONTENT_CHANGE_TYPE_DRAG_DROPPED = 256; // 0x100
field public static final int CONTENT_CHANGE_TYPE_DRAG_STARTED = 128; // 0x80
- field public static final int CONTENT_CHANGE_TYPE_INVALID = 1024; // 0x400
+ field public static final int CONTENT_CHANGE_TYPE_ERROR = 2048; // 0x800
field public static final int CONTENT_CHANGE_TYPE_PANE_APPEARED = 16; // 0x10
field public static final int CONTENT_CHANGE_TYPE_PANE_DISAPPEARED = 32; // 0x20
field public static final int CONTENT_CHANGE_TYPE_PANE_TITLE = 8; // 0x8
@@ -52951,7 +52992,7 @@ package android.view.displayhash {
package android.view.inputmethod {
public class BaseInputConnection implements android.view.inputmethod.InputConnection {
- ctor public BaseInputConnection(android.view.View, boolean);
+ ctor public BaseInputConnection(@NonNull android.view.View, boolean);
method public boolean beginBatchEdit();
method public boolean clearMetaKeyStates(int);
method @CallSuper public void closeConnection();
@@ -52963,24 +53004,24 @@ package android.view.inputmethod {
method public boolean deleteSurroundingTextInCodePoints(int, int);
method public boolean endBatchEdit();
method public boolean finishComposingText();
- method public static int getComposingSpanEnd(android.text.Spannable);
- method public static int getComposingSpanStart(android.text.Spannable);
+ method public static int getComposingSpanEnd(@NonNull android.text.Spannable);
+ method public static int getComposingSpanStart(@NonNull android.text.Spannable);
method public int getCursorCapsMode(int);
- method public android.text.Editable getEditable();
- method public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
- method public android.os.Handler getHandler();
- method public CharSequence getSelectedText(int);
+ method @Nullable public android.text.Editable getEditable();
+ method @Nullable public android.view.inputmethod.ExtractedText getExtractedText(android.view.inputmethod.ExtractedTextRequest, int);
+ method @Nullable public android.os.Handler getHandler();
+ method @Nullable public CharSequence getSelectedText(int);
method @Nullable public CharSequence getTextAfterCursor(@IntRange(from=0) int, int);
method @Nullable public CharSequence getTextBeforeCursor(@IntRange(from=0) int, int);
method public boolean performContextMenuAction(int);
method public boolean performEditorAction(int);
method public boolean performPrivateCommand(String, android.os.Bundle);
- method public static final void removeComposingSpans(android.text.Spannable);
+ method public static final void removeComposingSpans(@NonNull android.text.Spannable);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
method public boolean sendKeyEvent(android.view.KeyEvent);
method public boolean setComposingRegion(int, int);
- method public static void setComposingSpans(android.text.Spannable);
+ method public static void setComposingSpans(@NonNull android.text.Spannable);
method public boolean setComposingText(CharSequence, int);
method public boolean setSelection(int, int);
}
@@ -53061,6 +53102,24 @@ package android.view.inputmethod {
method @NonNull public android.view.inputmethod.DeleteGesture.Builder setGranularity(int);
}
+ public final class DeleteRangeGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.graphics.RectF getDeletionEndArea();
+ method @NonNull public android.graphics.RectF getDeletionStartArea();
+ method public int getGranularity();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.DeleteRangeGesture> CREATOR;
+ }
+
+ public static final class DeleteRangeGesture.Builder {
+ ctor public DeleteRangeGesture.Builder();
+ method @NonNull public android.view.inputmethod.DeleteRangeGesture build();
+ method @NonNull public android.view.inputmethod.DeleteRangeGesture.Builder setDeletionEndArea(@NonNull android.graphics.RectF);
+ method @NonNull public android.view.inputmethod.DeleteRangeGesture.Builder setDeletionStartArea(@NonNull android.graphics.RectF);
+ method @NonNull public android.view.inputmethod.DeleteRangeGesture.Builder setFallbackText(@Nullable String);
+ method @NonNull public android.view.inputmethod.DeleteRangeGesture.Builder setGranularity(int);
+ }
+
public final class EditorBoundsInfo implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.graphics.RectF getEditorBounds();
@@ -53160,11 +53219,13 @@ package android.view.inputmethod {
public abstract class HandwritingGesture {
method @Nullable public String getFallbackText();
field public static final int GESTURE_TYPE_DELETE = 4; // 0x4
+ field public static final int GESTURE_TYPE_DELETE_RANGE = 64; // 0x40
field public static final int GESTURE_TYPE_INSERT = 2; // 0x2
field public static final int GESTURE_TYPE_JOIN_OR_SPLIT = 16; // 0x10
field public static final int GESTURE_TYPE_NONE = 0; // 0x0
field public static final int GESTURE_TYPE_REMOVE_SPACE = 8; // 0x8
field public static final int GESTURE_TYPE_SELECT = 1; // 0x1
+ field public static final int GESTURE_TYPE_SELECT_RANGE = 32; // 0x20
field public static final int GRANULARITY_CHARACTER = 2; // 0x2
field public static final int GRANULARITY_WORD = 1; // 0x1
}
@@ -53262,6 +53323,7 @@ package android.view.inputmethod {
method public default void performHandwritingGesture(@NonNull android.view.inputmethod.HandwritingGesture, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.IntConsumer);
method public boolean performPrivateCommand(String, android.os.Bundle);
method public default boolean performSpellCheck();
+ method public default boolean replaceText(@IntRange(from=0) int, @IntRange(from=0) int, @NonNull CharSequence, int, @Nullable android.view.inputmethod.TextAttribute);
method public boolean reportFullscreenMode(boolean);
method public boolean requestCursorUpdates(int);
method public default boolean requestCursorUpdates(int, int);
@@ -53490,8 +53552,8 @@ package android.view.inputmethod {
public final class InsertGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
method public int describeContents();
- method @Nullable public android.graphics.PointF getInsertionPoint();
- method @Nullable public String getTextToInsert();
+ method @NonNull public android.graphics.PointF getInsertionPoint();
+ method @NonNull public String getTextToInsert();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.InsertGesture> CREATOR;
}
@@ -53549,6 +53611,24 @@ package android.view.inputmethod {
method @NonNull public android.view.inputmethod.SelectGesture.Builder setSelectionArea(@NonNull android.graphics.RectF);
}
+ public final class SelectRangeGesture extends android.view.inputmethod.HandwritingGesture implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getGranularity();
+ method @NonNull public android.graphics.RectF getSelectionEndArea();
+ method @NonNull public android.graphics.RectF getSelectionStartArea();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.view.inputmethod.SelectRangeGesture> CREATOR;
+ }
+
+ public static final class SelectRangeGesture.Builder {
+ ctor public SelectRangeGesture.Builder();
+ method @NonNull public android.view.inputmethod.SelectRangeGesture build();
+ method @NonNull public android.view.inputmethod.SelectRangeGesture.Builder setFallbackText(@Nullable String);
+ method @NonNull public android.view.inputmethod.SelectRangeGesture.Builder setGranularity(int);
+ method @NonNull public android.view.inputmethod.SelectRangeGesture.Builder setSelectionEndArea(@NonNull android.graphics.RectF);
+ method @NonNull public android.view.inputmethod.SelectRangeGesture.Builder setSelectionStartArea(@NonNull android.graphics.RectF);
+ }
+
public final class SurroundingText implements android.os.Parcelable {
ctor public SurroundingText(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0xffffffff) int);
method public int describeContents();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f09244ad7859..7dcfab4b5503 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1318,6 +1318,7 @@ package android.app.ambientcontext {
method @NonNull public java.time.Instant getStartTime();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.ambientcontext.AmbientContextEvent> CREATOR;
+ field public static final int EVENT_BACK_DOUBLE_TAP = 3; // 0x3
field public static final int EVENT_COUGH = 1; // 0x1
field public static final int EVENT_SNORE = 2; // 0x2
field public static final int EVENT_UNKNOWN = 0; // 0x0
@@ -2803,6 +2804,7 @@ package android.companion.virtual {
method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.companion.virtual.audio.VirtualAudioDevice createVirtualAudioDevice(@NonNull android.hardware.display.VirtualDisplay, @Nullable java.util.concurrent.Executor, @Nullable android.companion.virtual.audio.VirtualAudioDevice.AudioConfigurationChangeCallback);
method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@IntRange(from=1) int, @IntRange(from=1) int, @IntRange(from=1) int, @Nullable android.view.Surface, int, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.display.VirtualDisplay.Callback);
+ method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualDpad createVirtualDpad(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualKeyboard createVirtualKeyboard(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualMouse createVirtualMouse(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
@@ -4372,6 +4374,11 @@ package android.hardware.hdmi {
package android.hardware.input {
+ public class VirtualDpad implements java.io.Closeable {
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void close();
+ method @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void sendKeyEvent(@NonNull android.hardware.input.VirtualKeyEvent);
+ }
+
public final class VirtualKeyEvent implements android.os.Parcelable {
method public int describeContents();
method public int getAction();
@@ -6665,6 +6672,15 @@ package android.media.musicrecognition {
}
+package android.media.projection {
+
+ public class MediaProjectionGlobal {
+ method @Nullable public android.hardware.display.VirtualDisplay createVirtualDisplay(@NonNull String, int, int, int, @Nullable android.view.Surface);
+ method @NonNull public static android.media.projection.MediaProjectionGlobal getInstance();
+ }
+
+}
+
package android.media.session {
public final class MediaSessionManager {
@@ -10040,7 +10056,7 @@ package android.os.storage {
method @WorkerThread public long getAllocatableBytes(@NonNull java.util.UUID, @RequiresPermission int) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.WRITE_MEDIA_STORAGE) public int getExternalStorageMountMode(int, @NonNull String);
method public static boolean hasIsolatedStorage();
- method public void updateExternalStorageFileQuotaType(@NonNull java.io.File, int) throws java.io.IOException;
+ method @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) public void updateExternalStorageFileQuotaType(@NonNull java.io.File, int) throws java.io.IOException;
field @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; // 0x1
field public static final int MOUNT_MODE_EXTERNAL_ANDROID_WRITABLE = 4; // 0x4
field public static final int MOUNT_MODE_EXTERNAL_DEFAULT = 1; // 0x1
@@ -10347,6 +10363,7 @@ package android.provider {
field public static final String NAMESPACE_APP_HIBERNATION = "app_hibernation";
field public static final String NAMESPACE_ATTENTION_MANAGER_SERVICE = "attention_manager_service";
field public static final String NAMESPACE_AUTOFILL = "autofill";
+ field public static final String NAMESPACE_BACKUP_AND_RESTORE = "backup_and_restore";
field public static final String NAMESPACE_BATTERY_SAVER = "battery_saver";
field public static final String NAMESPACE_BIOMETRICS = "biometrics";
field public static final String NAMESPACE_BLOBSTORE = "blobstore";
@@ -10364,6 +10381,7 @@ package android.provider {
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";
+ field public static final String NAMESPACE_NEARBY = "nearby";
field public static final String NAMESPACE_NETD_NATIVE = "netd_native";
field public static final String NAMESPACE_NNAPI_NATIVE = "nnapi_native";
field public static final String NAMESPACE_ON_DEVICE_PERSONALIZATION = "on_device_personalization";
@@ -10393,6 +10411,7 @@ package android.provider {
field public static final String NAMESPACE_TELEPHONY = "telephony";
field public static final String NAMESPACE_TETHERING = "tethering";
field public static final String NAMESPACE_TEXTCLASSIFIER = "textclassifier";
+ field public static final String NAMESPACE_TRANSPARENCY_METADATA = "transparency_metadata";
field public static final String NAMESPACE_UWB = "uwb";
field public static final String NAMESPACE_WINDOW_MANAGER_NATIVE_BOOT = "window_manager_native_boot";
}
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index deb5aca53b78..e08cef92fc6c 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -121,6 +121,7 @@ package android.app {
public class ActivityManager {
method @RequiresPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER) public void addHomeVisibilityListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.HomeVisibilityListener);
method public void alwaysShowUnsupportedCompileSdkWarning(android.content.ComponentName);
+ method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int[] getSecondaryDisplayIdsForStartingBackgroundUsers();
method public long getTotalRam();
method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessCapabilities(int);
method @RequiresPermission(allOf={android.Manifest.permission.PACKAGE_USAGE_STATS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}, conditional=true) public int getUidProcessState(int);
@@ -1168,9 +1169,11 @@ package android.hardware.camera2 {
package android.hardware.devicestate {
public final class DeviceStateManager {
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelBaseStateOverride();
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest();
method @NonNull public int[] getSupportedStates();
method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+ method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
@@ -1221,6 +1224,7 @@ package android.hardware.display {
method @Nullable public android.view.Display.Mode getGlobalUserPreferredDisplayMode();
method @NonNull public int[] getUserDisabledHdrTypes();
method public boolean isMinimalPostProcessingRequested(int);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_SURFACE_FLINGER) public void overrideHdrTypes(int, @NonNull int[]);
method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setAreUserDisabledHdrTypesAllowed(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_USER_PREFERRED_DISPLAY_MODE) public void setGlobalUserPreferredDisplayMode(@NonNull android.view.Display.Mode);
method @RequiresPermission(android.Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE) public void setRefreshRateSwitchingType(int);
@@ -1874,7 +1878,7 @@ package android.os {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public java.util.List<android.content.pm.UserInfo> getUsers(boolean, boolean, boolean);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public boolean hasBaseUserRestriction(@NonNull String, @NonNull android.os.UserHandle);
method public static boolean isSplitSystemUser();
- method public static boolean isUsersOnSecondaryDisplaysEnabled();
+ method public boolean isUsersOnSecondaryDisplaysSupported();
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public android.content.pm.UserInfo preCreateUser(@NonNull String) throws android.os.UserManager.UserOperationException;
}
@@ -2209,6 +2213,7 @@ package android.provider {
field public static final String ACCESSIBILITY_SHORTCUT_TARGET_SERVICE = "accessibility_shortcut_target_service";
field public static final String ANR_SHOW_BACKGROUND = "anr_show_background";
field public static final String AUTOFILL_SERVICE = "autofill_service";
+ field public static final String BIOMETRIC_VIRTUAL_ENABLED = "biometric_virtual_enabled";
field public static final String CONTENT_CAPTURE_ENABLED = "content_capture_enabled";
field public static final String DISABLED_PRINT_SERVICES = "disabled_print_services";
field @Deprecated public static final String ENABLED_NOTIFICATION_POLICY_ACCESS_PACKAGES = "enabled_notification_policy_access_packages";
@@ -2709,7 +2714,7 @@ package android.util {
public class SparseArrayMap<K, V> {
ctor public SparseArrayMap();
- method public void add(int, @NonNull K, @Nullable V);
+ method public V add(int, @NonNull K, @Nullable V);
method public void clear();
method public boolean contains(int, @NonNull K);
method public void delete(int);
@@ -2854,7 +2859,7 @@ package android.view {
method public static String actionToString(int);
method public final void setDisplayId(int);
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
- field public static final int LAST_KEYCODE = 307; // 0x133
+ field public static final int LAST_KEYCODE = 311; // 0x137
}
public final class KeyboardShortcutGroup implements android.os.Parcelable {
@@ -2882,7 +2887,6 @@ package android.view {
ctor public SurfaceControl(@NonNull android.view.SurfaceControl, @NonNull String);
method @NonNull public static android.os.IBinder getInternalDisplayToken();
method public boolean isSameSurface(@NonNull android.view.SurfaceControl);
- method public static void overrideHdrTypes(@NonNull android.os.IBinder, @NonNull int[]);
}
public class SurfaceControlViewHost {
@@ -3431,6 +3435,7 @@ package android.window {
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 finishActivity(@NonNull android.os.IBinder);
method @NonNull public android.window.WindowContainerTransaction removeTask(@NonNull android.window.WindowContainerToken);
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);
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 0a906bee6fad..f9b8a3006880 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -873,6 +873,8 @@ NoSettingsProvider: android.provider.Settings.Secure#ACCESSIBILITY_SHORTCUT_TARG
New setting keys are not allowed (Field: ACCESSIBILITY_SHORTCUT_TARGET_SERVICE); use getters/setters in relevant manager class
NoSettingsProvider: android.provider.Settings.Secure#AUTOFILL_SERVICE:
New setting keys are not allowed (Field: AUTOFILL_SERVICE); use getters/setters in relevant manager class
+NoSettingsProvider: android.provider.Settings.Secure#BIOMETRIC_VIRTUAL_ENABLED:
+ New setting keys are not allowed (Field: BIOMETRIC_VIRTUAL_ENABLED); use getters/setters in relevant manager class
NoSettingsProvider: android.provider.Settings.Secure#CONTENT_CAPTURE_ENABLED:
New setting keys are not allowed (Field: CONTENT_CAPTURE_ENABLED); use getters/setters in relevant manager class
NoSettingsProvider: android.provider.Settings.Secure#DISABLED_PRINT_SERVICES:
@@ -1011,7 +1013,7 @@ UserHandleName: android.content.ContentCaptureOptions:
Classes holding a set of parameters should be called `FooParams`, was `ContentCaptureOptions`
-VisiblySynchronized: PsiThisExpression:this:
+VisiblySynchronized: PsiThisExpression:
Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getApkPaths()
VisiblySynchronized: android.content.res.AssetManager#getApkPaths():
Internal locks must not be exposed (synchronizing on this or class is still externally observable): method android.content.res.AssetManager.getApkPaths()
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c2b315f7088d..212e3581269a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -89,6 +89,7 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StrictMode;
+import android.os.SystemClock;
import android.os.Trace;
import android.os.UserHandle;
import android.text.Selection;
@@ -8304,13 +8305,15 @@ public class Activity extends ContextThemeWrapper
mIsInPictureInPictureMode = windowingMode == WINDOWING_MODE_PINNED;
mShouldDockBigOverlays = getResources().getBoolean(R.bool.config_dockBigOverlayWindows);
restoreHasCurrentPermissionRequest(icicle);
+ final long startTime = SystemClock.uptimeMillis();
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
+ final long duration = SystemClock.uptimeMillis() - startTime;
EventLogTags.writeWmOnCreateCalled(mIdent, getComponentName().getClassName(),
- "performCreate");
+ "performCreate", duration);
mActivityTransitionState.readState(icicle);
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
@@ -8332,8 +8335,11 @@ public class Activity extends ContextThemeWrapper
mFragments.noteStateNotSaved();
mCalled = false;
mFragments.execPendingActions();
+ final long startTime = SystemClock.uptimeMillis();
mInstrumentation.callActivityOnStart(this);
- EventLogTags.writeWmOnStartCalled(mIdent, getComponentName().getClassName(), reason);
+ final long duration = SystemClock.uptimeMillis() - startTime;
+ EventLogTags.writeWmOnStartCalled(mIdent, getComponentName().getClassName(), reason,
+ duration);
if (!mCalled) {
throw new SuperNotCalledException(
@@ -8410,8 +8416,11 @@ public class Activity extends ContextThemeWrapper
}
mCalled = false;
+ final long startTime = SystemClock.uptimeMillis();
mInstrumentation.callActivityOnRestart(this);
- EventLogTags.writeWmOnRestartCalled(mIdent, getComponentName().getClassName(), reason);
+ final long duration = SystemClock.uptimeMillis() - startTime;
+ EventLogTags.writeWmOnRestartCalled(mIdent, getComponentName().getClassName(), reason,
+ duration);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
@@ -8438,9 +8447,12 @@ public class Activity extends ContextThemeWrapper
getAutofillClientController().onActivityPerformResume(followedByPause);
mCalled = false;
+ final long startTime = SystemClock.uptimeMillis();
// mResumed is set by the instrumentation
mInstrumentation.callActivityOnResume(this);
- EventLogTags.writeWmOnResumeCalled(mIdent, getComponentName().getClassName(), reason);
+ final long duration = SystemClock.uptimeMillis() - startTime;
+ EventLogTags.writeWmOnResumeCalled(mIdent, getComponentName().getClassName(), reason,
+ duration);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
@@ -8483,9 +8495,11 @@ public class Activity extends ContextThemeWrapper
mDoReportFullyDrawn = false;
mFragments.dispatchPause();
mCalled = false;
+ final long startTime = SystemClock.uptimeMillis();
onPause();
+ final long duration = SystemClock.uptimeMillis() - startTime;
EventLogTags.writeWmOnPausedCalled(mIdent, getComponentName().getClassName(),
- "performPause");
+ "performPause", duration);
mResumed = false;
if (!mCalled && getApplicationInfo().targetSdkVersion
>= android.os.Build.VERSION_CODES.GINGERBREAD) {
@@ -8529,8 +8543,11 @@ public class Activity extends ContextThemeWrapper
mFragments.dispatchStop();
mCalled = false;
+ final long startTime = SystemClock.uptimeMillis();
mInstrumentation.callActivityOnStop(this);
- EventLogTags.writeWmOnStopCalled(mIdent, getComponentName().getClassName(), reason);
+ final long duration = SystemClock.uptimeMillis() - startTime;
+ EventLogTags.writeWmOnStopCalled(mIdent, getComponentName().getClassName(), reason,
+ duration);
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
@@ -8564,9 +8581,11 @@ public class Activity extends ContextThemeWrapper
mDestroyed = true;
mWindow.destroy();
mFragments.dispatchDestroy();
+ final long startTime = SystemClock.uptimeMillis();
onDestroy();
+ final long duration = SystemClock.uptimeMillis() - startTime;
EventLogTags.writeWmOnDestroyCalled(mIdent, getComponentName().getClassName(),
- "performDestroy");
+ "performDestroy", duration);
mFragments.doLoaderDestroy();
if (mVoiceInteractor != null) {
mVoiceInteractor.detachActivity();
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index d899dab8a61d..576b572dcc9a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -29,6 +29,7 @@ import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -2812,6 +2813,15 @@ public class ActivityManager {
*/
public static class MemoryInfo implements Parcelable {
/**
+ * The advertised memory of the system, as the end user would encounter in a retail display
+ * environment. This value might be different from {@code totalMem}. This could be due to
+ * many reasons. For example, the ODM could reserve part of the memory for the Trusted
+ * Execution Environment (TEE) which the kernel doesn't have access or knowledge about it.
+ */
+ @SuppressLint("MutableBareField")
+ public long advertisedMem;
+
+ /**
* The available memory on the system. This number should not
* be considered absolute: due to the nature of the kernel, a significant
* portion of this memory is actually in use and needed for the overall
@@ -2860,6 +2870,7 @@ public class ActivityManager {
}
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(advertisedMem);
dest.writeLong(availMem);
dest.writeLong(totalMem);
dest.writeLong(threshold);
@@ -2871,6 +2882,7 @@ public class ActivityManager {
}
public void readFromParcel(Parcel source) {
+ advertisedMem = source.readLong();
availMem = source.readLong();
totalMem = source.readLong();
threshold = source.readLong();
@@ -4216,13 +4228,23 @@ public class ActivityManager {
}
}*/
+ /** @hide
+ * Determines whether the given UID can access unexported components
+ * @param uid the calling UID
+ * @return true if the calling UID is ROOT or SYSTEM
+ */
+ public static boolean canAccessUnexportedComponents(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ return (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID);
+ }
+
/** @hide */
@UnsupportedAppUsage
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
// Root, system server get to do everything.
final int appId = UserHandle.getAppId(uid);
- if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
+ if (canAccessUnexportedComponents(uid)) {
return PackageManager.PERMISSION_GRANTED;
}
// Isolated processes don't get any permissions.
@@ -4350,20 +4372,30 @@ public class ActivityManager {
}
/**
- * Starts the given user in background and associate the user with the given display.
+ * Starts the given user in background and assign the user to the given display.
*
* <p>This method will allow the user to launch activities on that display, and it's typically
* used only on automotive builds when the vehicle has multiple displays (you can verify if it's
- * supported by calling {@link UserManager#isBackgroundUsersOnSecondaryDisplaysSupported()}).
+ * supported by calling {@link UserManager#isUsersOnSecondaryDisplaysSupported()}).
+ *
+ * <p><b>NOTE:</b> differently from {@link #switchUser(int)}, which stops the current foreground
+ * user before starting a new one, this method does not stop the previous user running in
+ * background in the display, and it will return {@code false} in this case. It's up to the
+ * caller to call {@link #stopUser(int, boolean)} before starting a new user.
*
- * @return whether the user was started.
+ * @param userId user to be started in the display. It will return {@code false} if the user is
+ * a profile, the {@link #getCurrentUser()}, the {@link UserHandle#SYSTEM system user}, or
+ * does not exist.
+ *
+ * @param displayId id of the display.
+ *
+ * @return whether the operation succeeded. Notice that if the user was already started in such
+ * display before, it will return {@code false}.
*
* @throws UnsupportedOperationException if the device does not support background users on
* secondary displays.
- * @throws IllegalArgumentException if the display does not exist.
- * @throws IllegalStateException if the user cannot be started on that display (for example, if
- * there's already a user using that display or if the user is already associated with other
- * display).
+ * @throws IllegalArgumentException if the display doesn't exist or is not a valid display to
+ * start secondary users on.
*
* @hide
*/
@@ -4372,6 +4404,10 @@ public class ActivityManager {
android.Manifest.permission.CREATE_USERS})
public boolean startUserInBackgroundOnSecondaryDisplay(@UserIdInt int userId,
int displayId) {
+ if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) {
+ throw new UnsupportedOperationException(
+ "device does not support users on secondary displays");
+ }
try {
return getService().startUserInBackgroundOnSecondaryDisplay(userId, displayId);
} catch (RemoteException e) {
@@ -4380,6 +4416,24 @@ public class ActivityManager {
}
/**
+ * Gets the id of displays that can be used by
+ * {@link #startUserInBackgroundOnSecondaryDisplay(int, int)}.
+ *
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
+ public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
+ try {
+ return getService().getSecondaryDisplayIdsForStartingBackgroundUsers();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Gets the message that is shown when a user is switched from.
*
* @hide
@@ -4532,6 +4586,10 @@ public class ActivityManager {
/**
* Stops the given {@code userId}.
*
+ * <p><b>NOTE:</b> on systems that support
+ * {@link UserManager#isUsersOnSecondaryDisplaysSupported() background users on secondary
+ * displays}, this method will also unassign the user from the display it was started on.
+ *
* @hide
*/
@TestApi
@@ -4641,7 +4699,7 @@ public class ActivityManager {
* @hide
*/
public static void broadcastStickyIntent(Intent intent, int userId) {
- broadcastStickyIntent(intent, AppOpsManager.OP_NONE, userId);
+ broadcastStickyIntent(intent, AppOpsManager.OP_NONE, null, userId);
}
/**
@@ -4650,11 +4708,20 @@ public class ActivityManager {
* @hide
*/
public static void broadcastStickyIntent(Intent intent, int appOp, int userId) {
+ broadcastStickyIntent(intent, appOp, null, userId);
+ }
+
+ /**
+ * Convenience for sending a sticky broadcast. For internal use only.
+ *
+ * @hide
+ */
+ public static void broadcastStickyIntent(Intent intent, int appOp, Bundle options, int userId) {
try {
getService().broadcastIntentWithFeature(
null, null, intent, null, null, Activity.RESULT_OK, null, null,
null /*requiredPermissions*/, null /*excludedPermissions*/,
- null /*excludedPackages*/, appOp, null, false, true, userId);
+ null /*excludedPackages*/, appOp, options, false, true, userId);
} catch (RemoteException ex) {
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 419b8e1ef016..626b7d3cd5b1 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -30,6 +30,7 @@ import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ActivityPresentationInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PermissionMethod;
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Bundle;
@@ -292,6 +293,7 @@ public abstract class ActivityManagerInternal {
boolean allowAll, int allowMode, String name, String callerPackage);
/** Checks if the calling binder pid as the permission. */
+ @PermissionMethod
public abstract void enforceCallingPermission(String permission, String func);
/** Returns the current user id. */
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cf5f10ba109e..7a9f3c1c4c76 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6662,6 +6662,9 @@ public final class ActivityThread extends ClientTransactionHandler
// Pass the current context to HardwareRenderer
HardwareRenderer.setContextForInit(getSystemContext());
+ if (data.persistent) {
+ HardwareRenderer.setIsSystemOrPersistent();
+ }
// Instrumentation info affects the class loader, so load it before
// setting up the app context.
diff --git a/core/java/android/app/AppOpInfo.java b/core/java/android/app/AppOpInfo.java
index 979c910ff5f4..5268ec42e21c 100644
--- a/core/java/android/app/AppOpInfo.java
+++ b/core/java/android/app/AppOpInfo.java
@@ -26,6 +26,7 @@ import java.util.Objects;
* Information about a particular app op.
*/
class AppOpInfo {
+
/**
* A unique constant identifying this app op.
*/
@@ -91,6 +92,11 @@ class AppOpInfo {
*/
public final boolean restrictRead;
+ /**
+ * Whether to collect noteOp instances, and send them to callbacks.
+ */
+ public final boolean forceCollectNotes;
+
AppOpInfo(int code,
int switchCode,
@NonNull String name,
@@ -100,7 +106,8 @@ class AppOpInfo {
AppOpsManager.RestrictionBypass allowSystemRestrictionBypass,
int defaultMode,
boolean disableReset,
- boolean restrictRead) {
+ boolean restrictRead,
+ boolean forceCollectNotes) {
if (code < OP_NONE) throw new IllegalArgumentException();
if (switchCode < OP_NONE) throw new IllegalArgumentException();
Objects.requireNonNull(name);
@@ -115,6 +122,7 @@ class AppOpInfo {
this.defaultMode = defaultMode;
this.disableReset = disableReset;
this.restrictRead = restrictRead;
+ this.forceCollectNotes = forceCollectNotes;
}
static class Builder {
@@ -128,6 +136,7 @@ class AppOpInfo {
private int mDefaultMode = AppOpsManager.MODE_DEFAULT;
private boolean mDisableReset = false;
private boolean mRestrictRead = false;
+ private boolean mForceCollectNotes = false;
Builder(int code, @NonNull String name, @NonNull String simpleName) {
if (code < OP_NONE) throw new IllegalArgumentException();
@@ -190,9 +199,15 @@ class AppOpInfo {
return this;
}
+ public Builder setForceCollectNotes(boolean value) {
+ this.mForceCollectNotes = value;
+ return this;
+ }
+
public AppOpInfo build() {
return new AppOpInfo(mCode, mSwitchCode, mName, mSimpleName, mPermission, mRestriction,
- mAllowSystemRestrictionBypass, mDefaultMode, mDisableReset, mRestrictRead);
+ mAllowSystemRestrictionBypass, mDefaultMode, mDisableReset, mRestrictRead,
+ mForceCollectNotes);
}
}
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index bc59d60bebd1..03c1e072092d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -554,7 +554,7 @@ public class AppOpsManager {
* @hide
*/
public static int resolveFirstUnrestrictedUidState(int op) {
- return UID_STATE_FOREGROUND;
+ return UID_STATE_MAX_LAST_NON_RESTRICTED;
}
/**
@@ -1831,6 +1831,9 @@ public class AppOpsManager {
})
private @interface ShouldCollectNoteOp {}
+ /** Whether noting for an appop should be collected */
+ private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
+
private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
// RUNTIME PERMISSIONS
// Contacts
@@ -1907,6 +1910,7 @@ public class AppOpsManager {
OP_SCHEDULE_EXACT_ALARM,
OP_MANAGE_MEDIA,
OP_TURN_SCREEN_ON,
+ OP_GET_USAGE_STATS,
};
static final AppOpInfo[] sAppOpInfos = new AppOpInfo[]{
@@ -2281,10 +2285,19 @@ public class AppOpsManager {
"ACCESS_RESTRICTED_SETTINGS").setDefaultMode(AppOpsManager.MODE_ALLOWED)
.setDisableReset(true).setRestrictRead(true).build(),
new AppOpInfo.Builder(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO,
- "RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED).build()
+ "RECEIVE_SOUNDTRIGGER_AUDIO").setDefaultMode(AppOpsManager.MODE_ALLOWED)
+ .setForceCollectNotes(true).build()
};
/**
+ * @hide
+ */
+ public static boolean shouldForceCollectNoteForOp(int op) {
+ Preconditions.checkArgumentInRange(op, 0, _NUM_OP - 1, "opCode");
+ return sAppOpInfos[op].forceCollectNotes;
+ }
+
+ /**
* Mapping from an app op name to the app op code.
*/
private static HashMap<String, Integer> sOpStrToOp = new HashMap<>();
@@ -2313,9 +2326,6 @@ public class AppOpsManager {
private static final ThreadLocal<ArrayMap<String, long[]>> sAppOpsNotedInThisBinderTransaction =
new ThreadLocal<>();
- /** Whether noting for an appop should be collected */
- private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
-
static {
if (sAppOpInfos.length != _NUM_OP) {
throw new IllegalStateException("mAppOpInfos length " + sAppOpInfos.length
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index aa5fa5b19117..c2df8022af0f 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -27,12 +27,15 @@ import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
import android.content.Intent;
+import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerExemptionManager;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.PowerExemptionManager.TempAllowListType;
+import java.util.Objects;
+
/**
* Helper class for building an options Bundle that can be used with
* {@link android.content.Context#sendBroadcast(android.content.Intent)
@@ -55,6 +58,7 @@ public class BroadcastOptions extends ComponentOptions {
private boolean mRequireCompatChangeEnabled = true;
private boolean mIsAlarmBroadcast = false;
private long mIdForResponseEvent;
+ private @Nullable IntentFilter mRemoveMatchingFilter;
/**
* Change ID which is invalid.
@@ -180,11 +184,25 @@ public class BroadcastOptions extends ComponentOptions {
private static final String KEY_ID_FOR_RESPONSE_EVENT =
"android:broadcast.idForResponseEvent";
+ /**
+ * Corresponds to {@link #setRemoveMatchingFilter}.
+ */
+ private static final String KEY_REMOVE_MATCHING_FILTER =
+ "android:broadcast.removeMatchingFilter";
+
public static BroadcastOptions makeBasic() {
BroadcastOptions opts = new BroadcastOptions();
return opts;
}
+ /** {@hide} */
+ public static @NonNull BroadcastOptions makeRemovingMatchingFilter(
+ @NonNull IntentFilter removeMatchingFilter) {
+ BroadcastOptions opts = new BroadcastOptions();
+ opts.setRemoveMatchingFilter(removeMatchingFilter);
+ return opts;
+ }
+
private BroadcastOptions() {
super();
resetTemporaryAppAllowlist();
@@ -216,6 +234,8 @@ public class BroadcastOptions extends ComponentOptions {
mRequireCompatChangeEnabled = opts.getBoolean(KEY_REQUIRE_COMPAT_CHANGE_ENABLED, true);
mIdForResponseEvent = opts.getLong(KEY_ID_FOR_RESPONSE_EVENT);
mIsAlarmBroadcast = opts.getBoolean(KEY_ALARM_BROADCAST, false);
+ mRemoveMatchingFilter = opts.getParcelable(KEY_REMOVE_MATCHING_FILTER,
+ IntentFilter.class);
}
/**
@@ -596,6 +616,29 @@ public class BroadcastOptions extends ComponentOptions {
}
/**
+ * When enqueuing this broadcast, remove all pending broadcasts previously
+ * sent by this app which match the given filter.
+ * <p>
+ * For example, sending {@link Intent#ACTION_SCREEN_ON} would typically want
+ * to remove any pending {@link Intent#ACTION_SCREEN_OFF} broadcasts.
+ *
+ * @hide
+ */
+ public void setRemoveMatchingFilter(@NonNull IntentFilter removeMatchingFilter) {
+ mRemoveMatchingFilter = Objects.requireNonNull(removeMatchingFilter);
+ }
+
+ /** @hide */
+ public void clearRemoveMatchingFilter() {
+ mRemoveMatchingFilter = null;
+ }
+
+ /** @hide */
+ public @Nullable IntentFilter getRemoveMatchingFilter() {
+ return mRemoveMatchingFilter;
+ }
+
+ /**
* Returns the created options as a Bundle, which can be passed to
* {@link android.content.Context#sendBroadcast(android.content.Intent)
* Context.sendBroadcast(Intent)} and related methods.
@@ -640,6 +683,9 @@ public class BroadcastOptions extends ComponentOptions {
if (mIdForResponseEvent != 0) {
b.putLong(KEY_ID_FOR_RESPONSE_EVENT, mIdForResponseEvent);
}
+ if (mRemoveMatchingFilter != null) {
+ b.putParcelable(KEY_REMOVE_MATCHING_FILTER, mRemoveMatchingFilter);
+ }
return b.isEmpty() ? null : b;
}
}
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 80121b729779..0e1b47f65561 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1094,7 +1094,7 @@ class ContextImpl extends Context {
&& (options == null
|| ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
throw new AndroidRuntimeException(
- "Calling startActivity() from outside of an Activity "
+ "Calling startActivity() from outside of an Activity"
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag."
+ " Is this really what you want?");
}
@@ -1128,7 +1128,7 @@ class ContextImpl extends Context {
public int startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
if ((intents[0].getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
- "Calling startActivities() from outside of an Activity "
+ "Calling startActivities() from outside of an Activity"
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag on first Intent."
+ " Is this really what you want?");
}
@@ -1142,7 +1142,7 @@ class ContextImpl extends Context {
warnIfCallingFromSystemProcess();
if ((intents[0].getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
throw new AndroidRuntimeException(
- "Calling startActivities() from outside of an Activity "
+ "Calling startActivities() from outside of an Activity"
+ " context requires the FLAG_ACTIVITY_NEW_TASK flag on first Intent."
+ " Is this really what you want?");
}
diff --git a/core/java/android/app/EventLogTags.logtags b/core/java/android/app/EventLogTags.logtags
index d0856f44d9a9..479f5a6722db 100644
--- a/core/java/android/app/EventLogTags.logtags
+++ b/core/java/android/app/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package android.app
@@ -6,21 +6,21 @@ option java_package android.app
# google3/googledata/wireless/android/provisioning/gservices.config !!
#
# The activity's onPause has been called.
-30021 wm_on_paused_called (Token|1|5),(Component Name|3),(Reason|3)
+30021 wm_on_paused_called (Token|1|5),(Component Name|3),(Reason|3),(time|2|3)
# The activity's onResume has been called.
-30022 wm_on_resume_called (Token|1|5),(Component Name|3),(Reason|3)
+30022 wm_on_resume_called (Token|1|5),(Component Name|3),(Reason|3),(time|2|3)
# The activity's onStop has been called.
-30049 wm_on_stop_called (Token|1|5),(Component Name|3),(Reason|3)
+30049 wm_on_stop_called (Token|1|5),(Component Name|3),(Reason|3),(time|2|3)
# The activity's onCreate has been called.
-30057 wm_on_create_called (Token|1|5),(Component Name|3),(Reason|3)
+30057 wm_on_create_called (Token|1|5),(Component Name|3),(Reason|3),(time|2|3)
# The activity's onRestart has been called.
-30058 wm_on_restart_called (Token|1|5),(Component Name|3),(Reason|3)
+30058 wm_on_restart_called (Token|1|5),(Component Name|3),(Reason|3),(time|2|3)
# The activity's onStart has been called.
-30059 wm_on_start_called (Token|1|5),(Component Name|3),(Reason|3)
+30059 wm_on_start_called (Token|1|5),(Component Name|3),(Reason|3),(time|2|3)
# The activity's onDestroy has been called.
-30060 wm_on_destroy_called (Token|1|5),(Component Name|3),(Reason|3)
+30060 wm_on_destroy_called (Token|1|5),(Component Name|3),(Reason|3),(time|2|3)
# The activity's onActivityResult has been called.
30062 wm_on_activity_result_called (Token|1|5),(Component Name|3),(Reason|3)
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 93381cf82764..81f61430560d 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -258,6 +258,11 @@ import java.lang.reflect.InvocationTargetException;
* pressing back will pop it to return the user to whatever previous state
* the activity UI was in.
*
+ * <p>
+ * Fragments appearing or disappearing do not generate system events for accessibility, so set a
+ * title on your fragments with {@link View#setAccessibilityPaneTitle(CharSequence)} to notify
+ * accessibility users of these UI transitions.
+ *
* @deprecated Use the <a href="{@docRoot}jetpack">Jetpack Fragment Library</a>
* {@link androidx.fragment.app.Fragment} for consistent behavior across all devices
* and access to <a href="{@docRoot}topic/libraries/architecture/lifecycle.html">Lifecycle</a>.
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index bd999fc04844..b4abd3c69482 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -522,9 +522,6 @@ interface IActivityManager {
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
boolean stopBinderTrackingAndDump(in ParcelFileDescriptor fd);
- /** Enables server-side binder tracing for the calling uid. */
- void enableBinderTracing();
-
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
void suppressResizeConfigChanges(boolean suppress);
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
@@ -769,6 +766,14 @@ interface IActivityManager {
*
* <p>Typically used only by automotive builds when the vehicle has multiple displays.
*/
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}, conditional = true)")
boolean startUserInBackgroundOnSecondaryDisplay(int userid, int displayId);
+ /**
+ * Gets the ids of displays that can be used on {@link #startUserInBackgroundOnSecondaryDisplay(int userId, int displayId)}.
+ *
+ * <p>Typically used only by automotive builds when the vehicle has multiple displays.
+ */
+ @nullable int[] getSecondaryDisplayIdsForStartingBackgroundUsers();
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 343adb82c8eb..9bbebc85255a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -1421,7 +1421,6 @@ public class Notification implements Parcelable
/**
* {@link #extras} key: the type of call represented by the
* {@link android.app.Notification.CallStyle} notification. This extra is an int.
- * @hide
*/
public static final String EXTRA_CALL_TYPE = "android.callType";
@@ -9301,11 +9300,43 @@ public class Notification implements Parcelable
* </pre>
*/
public static class CallStyle extends Style {
- /** @hide */
+
+ /**
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ CALL_TYPE_UNKNOWN,
+ CALL_TYPE_INCOMING,
+ CALL_TYPE_ONGOING,
+ CALL_TYPE_SCREENING
+ })
+ public @interface CallType {};
+
+ /**
+ * Unknown call type.
+ *
+ * See {@link #EXTRA_CALL_TYPE}.
+ */
+ public static final int CALL_TYPE_UNKNOWN = 0;
+
+ /**
+ * Call type for incoming calls.
+ *
+ * See {@link #EXTRA_CALL_TYPE}.
+ */
public static final int CALL_TYPE_INCOMING = 1;
- /** @hide */
+ /**
+ * Call type for ongoing calls.
+ *
+ * See {@link #EXTRA_CALL_TYPE}.
+ */
public static final int CALL_TYPE_ONGOING = 2;
- /** @hide */
+ /**
+ * Call type for calls that are being screened.
+ *
+ * See {@link #EXTRA_CALL_TYPE}.
+ */
public static final int CALL_TYPE_SCREENING = 3;
/**
@@ -9392,13 +9423,14 @@ public class Notification implements Parcelable
}
/**
+ * @param callType The type of the call
* @param person The person displayed for the incoming call.
* The user also needs to have a non-empty name associated with it.
* @param hangUpIntent The intent to be sent when the user taps the hang up action
* @param declineIntent The intent to be sent when the user taps the decline action
* @param answerIntent The intent to be sent when the user taps the answer action
*/
- private CallStyle(int callType, @NonNull Person person,
+ private CallStyle(@CallType int callType, @NonNull Person person,
@Nullable PendingIntent hangUpIntent, @Nullable PendingIntent declineIntent,
@Nullable PendingIntent answerIntent) {
if (person == null || TextUtils.isEmpty(person.getName())) {
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 13934e592d40..a51b9d3956df 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -391,7 +391,12 @@ public class PropertyInvalidatedCache<Query, Result> {
private static final boolean DEBUG = false;
private static final boolean VERIFY = false;
- // Per-Cache performance counters. As some cache instances are declared static,
+ /**
+ * The object-private lock.
+ */
+ private final Object mLock = new Object();
+
+ // Per-Cache performance counters.
@GuardedBy("mLock")
private long mHits = 0;
@@ -410,25 +415,19 @@ public class PropertyInvalidatedCache<Query, Result> {
@GuardedBy("mLock")
private long mClears = 0;
- // Most invalidation is done in a static context, so the counters need to be accessible.
- @GuardedBy("sCorkLock")
- private static final HashMap<String, Long> sInvalidates = new HashMap<>();
-
/**
- * Record the number of invalidate or cork calls that were nops because
- * the cache was already corked. This is static because invalidation is
- * done in a static context.
+ * Protect objects that support corking. mLock and sGlobalLock must never be taken while this
+ * is held.
*/
- @GuardedBy("sCorkLock")
- private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>();
+ private static final Object sCorkLock = new Object();
/**
- * If sEnabled is false then all cache operations are stubbed out. Set
- * it to false inside test processes.
+ * Record the number of invalidate or cork calls that were nops because the cache was already
+ * corked. This is static because invalidation is done in a static context. Entries are
+ * indexed by the cache property.
*/
- private static boolean sEnabled = true;
-
- private static final Object sCorkLock = new Object();
+ @GuardedBy("sCorkLock")
+ private static final HashMap<String, Long> sCorkedInvalidates = new HashMap<>();
/**
* A map of cache keys that we've "corked". (The values are counts.) When a cache key is
@@ -440,21 +439,39 @@ public class PropertyInvalidatedCache<Query, Result> {
private static final HashMap<String, Integer> sCorks = new HashMap<>();
/**
+ * A lock for the global list of caches and cache keys. This must never be taken inside mLock
+ * or sCorkLock.
+ */
+ private static final Object sGlobalLock = new Object();
+
+ /**
* A map of cache keys that have been disabled in the local process. When a key is
* disabled locally, existing caches are disabled and the key is saved in this map.
* Future cache instances that use the same key will be disabled in their constructor.
*/
- @GuardedBy("sCorkLock")
+ @GuardedBy("sGlobalLock")
private static final HashSet<String> sDisabledKeys = new HashSet<>();
/**
* Weakly references all cache objects in the current process, allowing us to iterate over
* them all for purposes like issuing debug dumps and reacting to memory pressure.
*/
- @GuardedBy("sCorkLock")
+ @GuardedBy("sGlobalLock")
private static final WeakHashMap<PropertyInvalidatedCache, Void> sCaches = new WeakHashMap<>();
- private final Object mLock = new Object();
+ /**
+ * Counts of the number of times a cache key was invalidated. Invalidation occurs in a static
+ * context with no cache object available, so this is a static map. Entries are indexed by
+ * the cache property.
+ */
+ @GuardedBy("sGlobalLock")
+ private static final HashMap<String, Long> sInvalidates = new HashMap<>();
+
+ /**
+ * If sEnabled is false then all cache operations are stubbed out. Set
+ * it to false inside test processes.
+ */
+ private static boolean sEnabled = true;
/**
* Name of the property that holds the unique value that we use to invalidate the cache.
@@ -595,14 +612,17 @@ public class PropertyInvalidatedCache<Query, Result> {
};
}
- // Register the map in the global list. If the cache is disabled globally, disable it
- // now.
+ /**
+ * Register the map in the global list. If the cache is disabled globally, disable it
+ * now. This method is only ever called from the constructor, which means no other thread has
+ * access to the object yet. It can safely be modified outside any lock.
+ */
private void registerCache() {
- synchronized (sCorkLock) {
- sCaches.put(this, null);
+ synchronized (sGlobalLock) {
if (sDisabledKeys.contains(mCacheName)) {
disableInstance();
}
+ sCaches.put(this, null);
}
}
@@ -797,8 +817,9 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
- * Disable the use of this cache in this process. This method is using during
- * testing. To disable a cache in normal code, use disableLocal().
+ * Disable the use of this cache in this process. This method is using internally and during
+ * testing. To disable a cache in normal code, use disableLocal(). A disabled cache cannot
+ * be re-enabled.
* @hide
*/
@TestApi
@@ -811,17 +832,23 @@ public class PropertyInvalidatedCache<Query, Result> {
/**
* Disable the local use of all caches with the same name. All currently registered caches
- * using the key will be disabled now, and all future cache instances that use the key will be
- * disabled in their constructor.
+ * with the name will be disabled now, and all future cache instances that use the name will
+ * be disabled in their constructor.
*/
private static final void disableLocal(@NonNull String name) {
- synchronized (sCorkLock) {
- sDisabledKeys.add(name);
+ synchronized (sGlobalLock) {
+ if (sDisabledKeys.contains(name)) {
+ // The key is already in recorded so there is no further work to be done.
+ return;
+ }
for (PropertyInvalidatedCache cache : sCaches.keySet()) {
if (name.equals(cache.mCacheName)) {
cache.disableInstance();
}
}
+ // Record the disabled key after the iteration. If an exception occurs during the
+ // iteration above, and the code is retried, the function should not exit early.
+ sDisabledKeys.add(name);
}
}
@@ -834,7 +861,7 @@ public class PropertyInvalidatedCache<Query, Result> {
*/
@TestApi
public final void forgetDisableLocal() {
- synchronized (sCorkLock) {
+ synchronized (sGlobalLock) {
sDisabledKeys.remove(mCacheName);
}
}
@@ -851,9 +878,9 @@ public class PropertyInvalidatedCache<Query, Result> {
}
/**
- * Disable this cache in the current process, and all other caches that use the same
- * name. This does not affect caches that have a different name but use the same
- * property.
+ * Disable this cache in the current process, and all other present and future caches that use
+ * the same name. This does not affect caches that have a different name but use the same
+ * property. Once disabled, a cache cannot be reenabled.
* @hide
*/
@TestApi
@@ -1381,10 +1408,9 @@ public class PropertyInvalidatedCache<Query, Result> {
/**
* Returns a list of caches alive at the current time.
*/
+ @GuardedBy("sGlobalLock")
private static @NonNull ArrayList<PropertyInvalidatedCache> getActiveCaches() {
- synchronized (sCorkLock) {
- return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet());
- }
+ return new ArrayList<PropertyInvalidatedCache>(sCaches.keySet());
}
/**
@@ -1540,7 +1566,7 @@ public class PropertyInvalidatedCache<Query, Result> {
boolean detail = anyDetailed(args);
ArrayList<PropertyInvalidatedCache> activeCaches;
- synchronized (sCorkLock) {
+ synchronized (sGlobalLock) {
activeCaches = getActiveCaches();
if (!detail) {
dumpCorkInfo(pw);
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 4da957c17a56..e5c080acbedb 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -29,8 +29,6 @@ import android.app.ambientcontext.AmbientContextManager;
import android.app.ambientcontext.IAmbientContextManager;
import android.app.appsearch.AppSearchManagerFrameworkInitializer;
import android.app.blob.BlobStoreManagerFrameworkInitializer;
-import android.app.cloudsearch.CloudSearchManager;
-import android.app.cloudsearch.ICloudSearchManager;
import android.app.contentsuggestions.ContentSuggestionsManager;
import android.app.contentsuggestions.IContentSuggestionsManager;
import android.app.job.JobSchedulerFrameworkInitializer;
@@ -1220,17 +1218,6 @@ public final class SystemServiceRegistry {
}
});
- registerService(Context.CLOUDSEARCH_SERVICE, CloudSearchManager.class,
- new CachedServiceFetcher<CloudSearchManager>() {
- @Override
- public CloudSearchManager createService(ContextImpl ctx)
- throws ServiceNotFoundException {
- IBinder b = ServiceManager.getService(Context.CLOUDSEARCH_SERVICE);
- return b == null ? null :
- new CloudSearchManager(ICloudSearchManager.Stub.asInterface(b));
- }
- });
-
registerService(Context.APP_PREDICTION_SERVICE, AppPredictionManager.class,
new CachedServiceFetcher<AppPredictionManager>() {
@Override
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 8d57e32a763c..d17f2ba1b401 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -45,6 +45,7 @@ import android.view.WindowAnimationFrameStats;
import android.view.WindowContentFrameStats;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.IAccessibilityManager;
+import android.window.ScreenCapture;
import libcore.io.IoUtils;
@@ -220,13 +221,13 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
int width = crop.width();
int height = crop.height();
final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
- final SurfaceControl.DisplayCaptureArgs captureArgs =
- new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+ final ScreenCapture.DisplayCaptureArgs captureArgs =
+ new ScreenCapture.DisplayCaptureArgs.Builder(displayToken)
.setSourceCrop(crop)
.setSize(width, height)
.build();
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureDisplay(captureArgs);
+ final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+ ScreenCapture.captureDisplay(captureArgs);
return screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
} finally {
Binder.restoreCallingIdentity(identity);
@@ -242,11 +243,11 @@ public final class UiAutomationConnection extends IUiAutomationConnection.Stub {
throwIfNotConnectedLocked();
}
- SurfaceControl.ScreenshotHardwareBuffer captureBuffer;
+ ScreenCapture.ScreenshotHardwareBuffer captureBuffer;
final long identity = Binder.clearCallingIdentity();
try {
- captureBuffer = SurfaceControl.captureLayers(
- new SurfaceControl.LayerCaptureArgs.Builder(surfaceControl)
+ captureBuffer = ScreenCapture.captureLayers(
+ new ScreenCapture.LayerCaptureArgs.Builder(surfaceControl)
.setChildrenOnly(false)
.build());
} finally {
diff --git a/core/java/android/app/UidObserver.java b/core/java/android/app/UidObserver.java
new file mode 100644
index 000000000000..9e928073ac5c
--- /dev/null
+++ b/core/java/android/app/UidObserver.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+/**
+ * Default implementation of {@link IUidObserver} for which all methods are
+ * no-op. Subclasses can override the select methods they're interested in
+ * handling.
+ *
+ * @hide
+ */
+public class UidObserver extends IUidObserver.Stub {
+ @Override
+ public void onUidActive(int uid) {
+ }
+
+ @Override
+ public void onUidCachedChanged(int uid, boolean cached) {
+ }
+
+ @Override
+ public void onUidGone(int uid, boolean disabled) {
+ }
+
+ @Override
+ public void onUidIdle(int uid, boolean disabled) {
+ }
+
+ @Override
+ public void onUidProcAdjChanged(int uid) {
+ }
+
+ @Override
+ public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
+ }
+}
diff --git a/core/java/android/app/ambientcontext/AmbientContextEvent.java b/core/java/android/app/ambientcontext/AmbientContextEvent.java
index af48fde6423a..865e1fbaf74e 100644
--- a/core/java/android/app/ambientcontext/AmbientContextEvent.java
+++ b/core/java/android/app/ambientcontext/AmbientContextEvent.java
@@ -63,8 +63,6 @@ public final class AmbientContextEvent implements Parcelable {
* The integer indicating a double-tap event was detected.
* For detecting this event type, there's no specific consent activity to request access, but
* the consent is implied through the double tap toggle in the Settings app.
- *
- * @hide
*/
public static final int EVENT_BACK_DOUBLE_TAP = 3;
diff --git a/core/java/android/app/cloudsearch/CloudSearchManager.java b/core/java/android/app/cloudsearch/CloudSearchManager.java
index 471e423db458..b7bbf4712842 100644
--- a/core/java/android/app/cloudsearch/CloudSearchManager.java
+++ b/core/java/android/app/cloudsearch/CloudSearchManager.java
@@ -15,17 +15,15 @@
*/
package android.app.cloudsearch;
-import static java.util.Objects.requireNonNull;
-
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
-import android.os.RemoteException;
import java.util.concurrent.Executor;
+
/**
* A {@link CloudSearchManager} is the class having all the information passed to search providers.
*
@@ -41,7 +39,7 @@ public class CloudSearchManager {
/**
* Invoked by receiving app with the result of the search.
*
- * @param request original request for the search.
+ * @param request original request for the search.
* @param response search result.
*/
void onSearchSucceeded(@NonNull SearchRequest request, @NonNull SearchResponse response);
@@ -51,17 +49,15 @@ public class CloudSearchManager {
* Each failure is recorded. The client may receive a failure from one provider and
* subsequently receive successful searches from other providers
*
- * @param request original request for the search.
+ * @param request original request for the search.
* @param response search result.
*/
void onSearchFailed(@NonNull SearchRequest request, @NonNull SearchResponse response);
}
- private final ICloudSearchManager mService;
-
/** @hide **/
- public CloudSearchManager(@NonNull ICloudSearchManager service) {
- mService = service;
+ public CloudSearchManager() {
+
}
/**
@@ -69,10 +65,9 @@ public class CloudSearchManager {
* to the designated cloud lookup services. After the lookup is done, the given
* callback will be invoked by the system with the result or lack thereof.
*
- * @param request request to be searched.
+ * @param request request to be searched.
* @param callbackExecutor where the callback is invoked.
- * @param callback invoked when the result is available.
- *
+ * @param callback invoked when the result is available.
* @hide
*/
@SystemApi
@@ -80,49 +75,8 @@ public class CloudSearchManager {
public void search(@NonNull SearchRequest request,
@NonNull @CallbackExecutor Executor callbackExecutor,
@NonNull CallBack callback) {
- try {
- mService.search(
- requireNonNull(request),
- new CallBackWrapper(
- requireNonNull(request),
- requireNonNull(callback),
- requireNonNull(callbackExecutor)));
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- private final class CallBackWrapper extends
- ICloudSearchManagerCallback.Stub {
- @NonNull
- private final SearchRequest mSearchRequest;
-
- @NonNull
- private final CallBack mCallback;
-
- @NonNull
- private final Executor mCallbackExecutor;
-
- CallBackWrapper(
- SearchRequest searchRequest,
- CallBack callback,
- Executor callbackExecutor) {
- mSearchRequest = searchRequest;
- mCallback = callback;
- mCallbackExecutor = callbackExecutor;
- }
-
-
- @Override
- public void onSearchSucceeded(SearchResponse searchResponse) {
- mCallbackExecutor.execute(
- () -> mCallback.onSearchSucceeded(mSearchRequest, searchResponse));
- }
-
- @Override
- public void onSearchFailed(SearchResponse searchResponse) {
- mCallbackExecutor.execute(
- () -> mCallback.onSearchFailed(mSearchRequest, searchResponse));
- }
+ callbackExecutor.execute(
+ () -> callback.onSearchFailed(request,
+ new SearchResponse.Builder(SearchResponse.SEARCH_STATUS_UNKNOWN).build()));
}
}
diff --git a/core/java/android/app/cloudsearch/ICloudSearchManager.aidl b/core/java/android/app/cloudsearch/ICloudSearchManager.aidl
deleted file mode 100644
index 18f8fc476191..000000000000
--- a/core/java/android/app/cloudsearch/ICloudSearchManager.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Copyright (c) 2022, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.cloudsearch;
-
-import android.app.cloudsearch.SearchRequest;
-import android.app.cloudsearch.SearchResponse;
-import android.app.cloudsearch.ICloudSearchManagerCallback;
-
-/**
- * Used by {@link CloudSearchManager} to tell system server to do search.
- *
- * @hide
- */
-oneway interface ICloudSearchManager {
- void search(in SearchRequest request, in ICloudSearchManagerCallback callBack);
-
- void returnResults(in IBinder token, in String requestId,
- in SearchResponse response);
-}
diff --git a/core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl b/core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl
deleted file mode 100644
index 84771dd4a19b..000000000000
--- a/core/java/android/app/cloudsearch/ICloudSearchManagerCallback.aidl
+++ /dev/null
@@ -1,31 +0,0 @@
-/**
- * Copyright (c) 2022, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.cloudsearch;
-
-import android.app.cloudsearch.SearchResponse;
-
-
-/**
- * Callback used by system server to notify invoker of {@link CloudSearchManager} of the result
- *
- * @hide
- */
-oneway interface ICloudSearchManagerCallback {
- void onSearchSucceeded(in SearchResponse response);
-
- void onSearchFailed(in SearchResponse response);
-}
diff --git a/core/java/android/app/cloudsearch/SearchRequest.java b/core/java/android/app/cloudsearch/SearchRequest.java
index bf783255b3d9..3725b36a1c7f 100644
--- a/core/java/android/app/cloudsearch/SearchRequest.java
+++ b/core/java/android/app/cloudsearch/SearchRequest.java
@@ -15,8 +15,6 @@
*/
package android.app.cloudsearch;
-import static java.util.Objects.requireNonNull;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
@@ -28,7 +26,6 @@ import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
/**
* A {@link SearchRequest} is the data class having all the information passed to search providers.
@@ -39,36 +36,6 @@ import java.util.Objects;
public final class SearchRequest implements Parcelable {
/**
- * Query for search.
- */
- @NonNull
- private final String mQuery;
-
- /**
- * Expected result offset for pagination.
- *
- * The default value is 0.
- */
- private final int mResultOffset;
-
- /**
- * Expected search result number.
- *
- * The default value is 10.
- */
- private final int mResultNumber;
-
- /**
- * The max acceptable latency.
- *
- * The default value is 200 milliseconds.
- */
- private final float mMaxLatencyMillis;
-
- @Nullable
- private String mId = null;
-
- /**
* List of public static KEYS for the Bundle to mSearchConstraints. mSearchConstraints
* contains various constraints specifying the search intent.
*
@@ -76,121 +43,83 @@ public final class SearchRequest implements Parcelable {
*/
@Retention(RetentionPolicy.SOURCE)
@StringDef(prefix = {"CONSTRAINT_"},
- value = {CONSTRAINT_IS_PRESUBMIT_SUGGESTION,
- CONSTRAINT_SEARCH_PROVIDER_FILTER})
- public @interface SearchConstraintKey {}
- /** If this is a presubmit suggestion, Boolean value expected.
- * presubmit is the input before the user finishes the entire query, i.e. push "ENTER" or
- * "SEARCH" button. After the user finishes the entire query, the behavior is postsubmit.
+ value = {CONSTRAINT_IS_PRESUBMIT_SUGGESTION,
+ CONSTRAINT_SEARCH_PROVIDER_FILTER})
+ public @interface SearchConstraintKey {
+ }
+
+ /**
+ * If this is a presubmit suggestion, Boolean value expected.
+ * presubmit is the input before the user finishes the entire query, i.e. push "ENTER" or
+ * "SEARCH" button. After the user finishes the entire query, the behavior is postsubmit.
*/
public static final String CONSTRAINT_IS_PRESUBMIT_SUGGESTION =
"android.app.cloudsearch.IS_PRESUBMIT_SUGGESTION";
- /** The target search provider list of package names(separated by ;), String value expected.
+ /**
+ * The target search provider list of package names(separated by ;), String value expected.
* If this is not provided or its value is empty, then no filter will be applied.
*/
public static final String CONSTRAINT_SEARCH_PROVIDER_FILTER =
"android.app.cloudsearch.SEARCH_PROVIDER_FILTER";
- @NonNull
- private Bundle mSearchConstraints;
-
- /** Auto set by system servier, and the caller cannot set it.
- *
- * The caller's package name.
- *
- */
- @NonNull
- private String mCallerPackageName;
-
- private SearchRequest(Parcel in) {
- this.mQuery = in.readString();
- this.mResultOffset = in.readInt();
- this.mResultNumber = in.readInt();
- this.mMaxLatencyMillis = in.readFloat();
- this.mSearchConstraints = in.readBundle();
- this.mId = in.readString();
- this.mCallerPackageName = in.readString();
- }
-
- private SearchRequest(String query, int resultOffset, int resultNumber, float maxLatencyMillis,
- Bundle searchConstraints, String callerPackageName) {
- mQuery = query;
- mResultOffset = resultOffset;
- mResultNumber = resultNumber;
- mMaxLatencyMillis = maxLatencyMillis;
- mSearchConstraints = searchConstraints;
- mCallerPackageName = callerPackageName;
+ private SearchRequest() {
}
/** Returns the original query. */
@NonNull
public String getQuery() {
- return mQuery;
+ return "";
}
/** Returns the result offset. */
public int getResultOffset() {
- return mResultOffset;
+ return 0;
}
/** Returns the expected number of search results. */
public int getResultNumber() {
- return mResultNumber;
+ return 0;
}
/** Returns the maximum latency requirement. */
public float getMaxLatencyMillis() {
- return mMaxLatencyMillis;
+ return 0;
}
/** Returns the search constraints. */
@NonNull
public Bundle getSearchConstraints() {
- return mSearchConstraints;
+ return Bundle.EMPTY;
}
/** Gets the caller's package name. */
@NonNull
public String getCallerPackageName() {
- return mCallerPackageName;
+ return "";
}
/** Returns the search request id, which is used to identify the request. */
@NonNull
public String getRequestId() {
- if (mId == null || mId.length() == 0) {
- mId = String.valueOf(toString().hashCode());
- }
-
- return mId;
+ return "";
}
- /** Sets the caller, and this will be set by the system server.
+ /**
+ * Sets the caller, and this will be set by the system server.
*
* @hide
*/
public void setCallerPackageName(@NonNull String callerPackageName) {
- this.mCallerPackageName = callerPackageName;
- }
-
- private SearchRequest(Builder b) {
- mQuery = requireNonNull(b.mQuery);
- mResultOffset = b.mResultOffset;
- mResultNumber = b.mResultNumber;
- mMaxLatencyMillis = b.mMaxLatencyMillis;
- mSearchConstraints = requireNonNull(b.mSearchConstraints);
- mCallerPackageName = requireNonNull(b.mCallerPackageName);
}
/**
* @see Creator
- *
*/
@NonNull
public static final Creator<SearchRequest> CREATOR = new Creator<SearchRequest>() {
@Override
public SearchRequest createFromParcel(Parcel p) {
- return new SearchRequest(p);
+ return new SearchRequest();
}
@Override
@@ -201,13 +130,6 @@ public final class SearchRequest implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(this.mQuery);
- dest.writeInt(this.mResultOffset);
- dest.writeInt(this.mResultNumber);
- dest.writeFloat(this.mMaxLatencyMillis);
- dest.writeBundle(this.mSearchConstraints);
- dest.writeString(getRequestId());
- dest.writeString(this.mCallerPackageName);
}
@Override
@@ -217,44 +139,17 @@ public final class SearchRequest implements Parcelable {
@Override
public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
-
- SearchRequest that = (SearchRequest) obj;
- return Objects.equals(mQuery, that.mQuery)
- && mResultOffset == that.mResultOffset
- && mResultNumber == that.mResultNumber
- && mMaxLatencyMillis == that.mMaxLatencyMillis
- && Objects.equals(mSearchConstraints, that.mSearchConstraints)
- && Objects.equals(mCallerPackageName, that.mCallerPackageName);
+ return false;
}
@Override
public String toString() {
- boolean isPresubmit =
- mSearchConstraints.containsKey(CONSTRAINT_IS_PRESUBMIT_SUGGESTION)
- && mSearchConstraints.getBoolean(CONSTRAINT_IS_PRESUBMIT_SUGGESTION);
-
- String searchProvider = "EMPTY";
- if (mSearchConstraints.containsKey(CONSTRAINT_SEARCH_PROVIDER_FILTER)) {
- searchProvider = mSearchConstraints.getString(CONSTRAINT_SEARCH_PROVIDER_FILTER);
- }
-
- return String.format("SearchRequest: {query:%s,offset:%d;number:%d;max_latency:%f;"
- + "is_presubmit:%b;search_provider:%s;callerPackageName:%s}", mQuery,
- mResultOffset, mResultNumber, mMaxLatencyMillis, isPresubmit, searchProvider,
- mCallerPackageName);
+ return "";
}
@Override
public int hashCode() {
- return Objects.hash(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
- mSearchConstraints, mCallerPackageName);
+ return 0;
}
/**
@@ -264,87 +159,62 @@ public final class SearchRequest implements Parcelable {
*/
@SystemApi
public static final class Builder {
- private String mQuery;
- private int mResultOffset;
- private int mResultNumber;
- private float mMaxLatencyMillis;
- private Bundle mSearchConstraints;
- private String mCallerPackageName;
-
/**
- *
* @param query the query for search.
- *
* @hide
*/
@SystemApi
public Builder(@NonNull String query) {
- mQuery = query;
-
- mResultOffset = 0;
- mResultNumber = 10;
- mMaxLatencyMillis = 200;
- mSearchConstraints = Bundle.EMPTY;
- mCallerPackageName = "DEFAULT_CALLER";
}
/** Sets the input query. */
@NonNull
public Builder setQuery(@NonNull String query) {
- this.mQuery = query;
return this;
}
/** Sets the search result offset. */
@NonNull
public Builder setResultOffset(int resultOffset) {
- this.mResultOffset = resultOffset;
return this;
}
/** Sets the expected number of search result. */
@NonNull
public Builder setResultNumber(int resultNumber) {
- this.mResultNumber = resultNumber;
return this;
}
/** Sets the maximum acceptable search latency. */
@NonNull
public Builder setMaxLatencyMillis(float maxLatencyMillis) {
- this.mMaxLatencyMillis = maxLatencyMillis;
return this;
}
- /** Sets the search constraints, such as the user location, the search type(presubmit or
- * postsubmit), and the target search providers. */
+ /**
+ * Sets the search constraints, such as the user location, the search type(presubmit or
+ * postsubmit), and the target search providers.
+ */
@NonNull
public Builder setSearchConstraints(@Nullable Bundle searchConstraints) {
- this.mSearchConstraints = searchConstraints;
return this;
}
- /** Sets the caller, and this will be set by the system server.
+ /**
+ * Sets the caller, and this will be set by the system server.
*
* @hide
*/
@NonNull
@TestApi
public Builder setCallerPackageName(@NonNull String callerPackageName) {
- this.mCallerPackageName = callerPackageName;
return this;
}
/** Builds a SearchRequest based-on the given params. */
@NonNull
public SearchRequest build() {
- if (mQuery == null || mResultOffset < 0 || mResultNumber < 1 || mMaxLatencyMillis < 0
- || mSearchConstraints == null) {
- throw new IllegalStateException("Please make sure all required args are valid.");
- }
-
- return new SearchRequest(mQuery, mResultOffset, mResultNumber, mMaxLatencyMillis,
- mSearchConstraints, mCallerPackageName);
+ return new SearchRequest();
}
}
}
diff --git a/core/java/android/app/cloudsearch/SearchResponse.java b/core/java/android/app/cloudsearch/SearchResponse.java
index 607bd561d331..c86142e0d22d 100644
--- a/core/java/android/app/cloudsearch/SearchResponse.java
+++ b/core/java/android/app/cloudsearch/SearchResponse.java
@@ -15,8 +15,6 @@
*/
package android.app.cloudsearch;
-import static java.util.Objects.requireNonNull;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;
@@ -25,7 +23,6 @@ import android.os.Parcelable;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
/**
* A {@link SearchResponse} includes search results and associated meta information.
@@ -37,77 +34,53 @@ public final class SearchResponse implements Parcelable {
/** @hide */
@IntDef(prefix = {"SEARCH_STATUS_"},
value = {SEARCH_STATUS_UNKNOWN,
- SEARCH_STATUS_OK,
- SEARCH_STATUS_TIME_OUT,
- SEARCH_STATUS_NO_INTERNET})
- public @interface SearchStatusCode {}
+ SEARCH_STATUS_OK,
+ SEARCH_STATUS_TIME_OUT,
+ SEARCH_STATUS_NO_INTERNET})
+ public @interface SearchStatusCode {
+ }
+
public static final int SEARCH_STATUS_UNKNOWN = -1;
public static final int SEARCH_STATUS_OK = 0;
public static final int SEARCH_STATUS_TIME_OUT = 1;
public static final int SEARCH_STATUS_NO_INTERNET = 2;
- private final int mStatusCode;
-
- /** Auto set by system servier, and the provider cannot set it. */
- @NonNull
- private String mSource;
-
- @NonNull
- private final List<SearchResult> mSearchResults;
-
- private SearchResponse(Parcel in) {
- this.mStatusCode = in.readInt();
- this.mSource = in.readString();
- this.mSearchResults = in.createTypedArrayList(SearchResult.CREATOR);
- }
-
- private SearchResponse(@SearchStatusCode int statusCode, String source,
- List<SearchResult> searchResults) {
- mStatusCode = statusCode;
- mSource = source;
- mSearchResults = searchResults;
+ private SearchResponse() {
}
/** Gets the search status code. */
public int getStatusCode() {
- return mStatusCode;
+ return SEARCH_STATUS_UNKNOWN;
}
/** Gets the search provider package name. */
@NonNull
public String getSource() {
- return mSource;
+ return "";
}
/** Gets the search results, which can be empty. */
@NonNull
public List<SearchResult> getSearchResults() {
- return mSearchResults;
+ return new ArrayList<SearchResult>();
}
- /** Sets the search provider, and this will be set by the system server.
+ /**
+ * Sets the search provider, and this will be set by the system server.
*
* @hide
*/
public void setSource(@NonNull String source) {
- this.mSource = source;
- }
-
- private SearchResponse(Builder b) {
- mStatusCode = b.mStatusCode;
- mSource = requireNonNull(b.mSource);
- mSearchResults = requireNonNull(b.mSearchResults);
}
/**
- *
* @see Creator
- *
*/
- @NonNull public static final Creator<SearchResponse> CREATOR = new Creator<SearchResponse>() {
+ @NonNull
+ public static final Creator<SearchResponse> CREATOR = new Creator<SearchResponse>() {
@Override
public SearchResponse createFromParcel(Parcel p) {
- return new SearchResponse(p);
+ return new SearchResponse();
}
@Override
@@ -118,9 +91,6 @@ public final class SearchResponse implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeInt(this.mStatusCode);
- dest.writeString(this.mSource);
- dest.writeTypedList(this.mSearchResults);
}
@Override
@@ -130,23 +100,12 @@ public final class SearchResponse implements Parcelable {
@Override
public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
-
- SearchResponse that = (SearchResponse) obj;
- return mStatusCode == that.mStatusCode
- && Objects.equals(mSource, that.mSource)
- && Objects.equals(mSearchResults, that.mSearchResults);
+ return false;
}
@Override
public int hashCode() {
- return Objects.hash(mStatusCode, mSource, mSearchResults);
+ return 0;
}
/**
@@ -156,59 +115,40 @@ public final class SearchResponse implements Parcelable {
*/
@SystemApi
public static final class Builder {
- private int mStatusCode;
- private String mSource;
- private List<SearchResult> mSearchResults;
-
/**
- *
* @param statusCode the search status code.
- *
* @hide
*/
@SystemApi
public Builder(@SearchStatusCode int statusCode) {
- mStatusCode = statusCode;
-
- /** Init with a default value. */
- mSource = "DEFAULT";
-
- mSearchResults = new ArrayList<SearchResult>();
}
/** Sets the search status code. */
@NonNull
public Builder setStatusCode(@SearchStatusCode int statusCode) {
- this.mStatusCode = statusCode;
return this;
}
- /** Sets the search provider, and this will be set by the system server.
+ /**
+ * Sets the search provider, and this will be set by the system server.
*
* @hide
*/
@NonNull
public Builder setSource(@NonNull String source) {
- this.mSource = source;
return this;
}
/** Sets the search results. */
@NonNull
public Builder setSearchResults(@NonNull List<SearchResult> searchResults) {
- this.mSearchResults = searchResults;
return this;
}
/** Builds a SearchResponse based-on the given parameters. */
@NonNull
public SearchResponse build() {
- if (mStatusCode < SEARCH_STATUS_UNKNOWN || mStatusCode > SEARCH_STATUS_NO_INTERNET
- || mSearchResults == null) {
- throw new IllegalStateException("Please make sure all @NonNull args are assigned.");
- }
-
- return new SearchResponse(mStatusCode, mSource, mSearchResults);
+ return new SearchResponse();
}
}
}
diff --git a/core/java/android/app/cloudsearch/SearchResult.java b/core/java/android/app/cloudsearch/SearchResult.java
index c6583b65f9c2..123c3a2a5f48 100644
--- a/core/java/android/app/cloudsearch/SearchResult.java
+++ b/core/java/android/app/cloudsearch/SearchResult.java
@@ -15,8 +15,6 @@
*/
package android.app.cloudsearch;
-import static java.util.Objects.requireNonNull;
-
import android.annotation.NonNull;
import android.annotation.StringDef;
import android.annotation.SuppressLint;
@@ -27,7 +25,6 @@ import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
/**
* A {@link SearchResult} includes all the information for one result item.
@@ -37,17 +34,6 @@ import java.util.Objects;
@SystemApi
public final class SearchResult implements Parcelable {
- /** Short content best describing the result item. */
- @NonNull
- private final String mTitle;
-
- /** Matched contents in the result item. */
- @NonNull
- private final String mSnippet;
-
- /** Ranking Score provided by the search provider. */
- private final float mScore;
-
/**
* List of public static KEYS for Bundles in mExtraInfos.
* mExtraInfos contains various information specified for different data types.
@@ -56,28 +42,30 @@ public final class SearchResult implements Parcelable {
*/
@Retention(RetentionPolicy.SOURCE)
@StringDef(prefix = {"EXTRAINFO_"},
- value = {EXTRAINFO_APP_DOMAIN_URL,
- EXTRAINFO_APP_ICON,
- EXTRAINFO_APP_DEVELOPER_NAME,
- EXTRAINFO_APP_SIZE_BYTES,
- EXTRAINFO_APP_STAR_RATING,
- EXTRAINFO_APP_IARC,
- EXTRAINFO_APP_REVIEW_COUNT,
- EXTRAINFO_APP_CONTAINS_ADS_DISCLAIMER,
- EXTRAINFO_APP_CONTAINS_IAP_DISCLAIMER,
- EXTRAINFO_SHORT_DESCRIPTION,
- EXTRAINFO_LONG_DESCRIPTION,
- EXTRAINFO_SCREENSHOTS,
- EXTRAINFO_APP_BADGES,
- EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING,
- EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING,
- EXTRAINFO_ACTION_APP_CARD,
- EXTRAINFO_ACTION_INSTALL_BUTTON,
- EXTRAINFO_APP_PACKAGE_NAME,
- EXTRAINFO_APP_INSTALL_COUNT,
- EXTRAINFO_WEB_URL,
- EXTRAINFO_WEB_ICON})
- public @interface SearchResultExtraInfoKey {}
+ value = {EXTRAINFO_APP_DOMAIN_URL,
+ EXTRAINFO_APP_ICON,
+ EXTRAINFO_APP_DEVELOPER_NAME,
+ EXTRAINFO_APP_SIZE_BYTES,
+ EXTRAINFO_APP_STAR_RATING,
+ EXTRAINFO_APP_IARC,
+ EXTRAINFO_APP_REVIEW_COUNT,
+ EXTRAINFO_APP_CONTAINS_ADS_DISCLAIMER,
+ EXTRAINFO_APP_CONTAINS_IAP_DISCLAIMER,
+ EXTRAINFO_SHORT_DESCRIPTION,
+ EXTRAINFO_LONG_DESCRIPTION,
+ EXTRAINFO_SCREENSHOTS,
+ EXTRAINFO_APP_BADGES,
+ EXTRAINFO_ACTION_BUTTON_TEXT_PREREGISTERING,
+ EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING,
+ EXTRAINFO_ACTION_APP_CARD,
+ EXTRAINFO_ACTION_INSTALL_BUTTON,
+ EXTRAINFO_APP_PACKAGE_NAME,
+ EXTRAINFO_APP_INSTALL_COUNT,
+ EXTRAINFO_WEB_URL,
+ EXTRAINFO_WEB_ICON})
+ public @interface SearchResultExtraInfoKey {
+ }
+
/** This App developer website's domain URL, String value expected. */
public static final String EXTRAINFO_APP_DOMAIN_URL = "android.app.cloudsearch.APP_DOMAIN_URL";
/** This App icon, android.graphics.drawable.Icon expected. */
@@ -90,7 +78,8 @@ public final class SearchResult implements Parcelable {
/** This App developer's name, Double value expected. */
public static final String EXTRAINFO_APP_STAR_RATING =
"android.app.cloudsearch.APP_STAR_RATING";
- /** This App's IARC rating, String value expected.
+ /**
+ * This App's IARC rating, String value expected.
* IARC (International Age Rating Coalition) is partnered globally with major
* content rating organizations to provide a centralized and one-stop-shop for
* rating content on a global scale.
@@ -142,62 +131,40 @@ public final class SearchResult implements Parcelable {
/** Web content's domain icon, android.graphics.drawable.Icon expected. */
public static final String EXTRAINFO_WEB_ICON = "android.app.cloudsearch.WEB_ICON";
- @NonNull
- private Bundle mExtraInfos;
-
- private SearchResult(Parcel in) {
- this.mTitle = in.readString();
- this.mSnippet = in.readString();
- this.mScore = in.readFloat();
- this.mExtraInfos = in.readBundle();
- }
-
- private SearchResult(String title, String snippet, float score, Bundle extraInfos) {
- mTitle = title;
- mSnippet = snippet;
- mScore = score;
- mExtraInfos = extraInfos;
+ private SearchResult() {
}
/** Gets the search result title. */
@NonNull
public String getTitle() {
- return mTitle;
+ return "";
}
/** Gets the search result snippet. */
@NonNull
public String getSnippet() {
- return mSnippet;
+ return "";
}
/** Gets the ranking score provided by the original search provider. */
public float getScore() {
- return mScore;
+ return 0;
}
/** Gets the extra information associated with the search result. */
@NonNull
public Bundle getExtraInfos() {
- return mExtraInfos;
- }
-
- private SearchResult(Builder b) {
- mTitle = requireNonNull(b.mTitle);
- mSnippet = requireNonNull(b.mSnippet);
- mScore = b.mScore;
- mExtraInfos = requireNonNull(b.mExtraInfos);
+ return Bundle.EMPTY;
}
/**
- *
* @see Creator
- *
*/
- @NonNull public static final Creator<SearchResult> CREATOR = new Creator<SearchResult>() {
+ @NonNull
+ public static final Creator<SearchResult> CREATOR = new Creator<SearchResult>() {
@Override
public SearchResult createFromParcel(Parcel p) {
- return new SearchResult(p);
+ return new SearchResult();
}
@Override
@@ -208,10 +175,6 @@ public final class SearchResult implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(this.mTitle);
- dest.writeString(this.mSnippet);
- dest.writeFloat(this.mScore);
- dest.writeBundle(this.mExtraInfos);
}
@Override
@@ -221,24 +184,12 @@ public final class SearchResult implements Parcelable {
@Override
public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
-
- SearchResult that = (SearchResult) obj;
- return Objects.equals(mTitle, that.mTitle)
- && Objects.equals(mSnippet, that.mSnippet)
- && mScore == that.mScore
- && Objects.equals(mExtraInfos, that.mExtraInfos);
+ return false;
}
@Override
public int hashCode() {
- return Objects.hash(mTitle, mSnippet, mScore, mExtraInfos);
+ return 0;
}
/**
@@ -248,63 +199,43 @@ public final class SearchResult implements Parcelable {
*/
@SystemApi
public static final class Builder {
- private String mTitle;
- private String mSnippet;
- private float mScore;
- private Bundle mExtraInfos;
-
/**
- *
- * @param title the title to the search result.
+ * @param title the title to the search result.
* @param extraInfos the extra infos associated with the search result.
- *
* @hide
*/
@SystemApi
public Builder(@NonNull String title, @NonNull Bundle extraInfos) {
- mTitle = title;
- mExtraInfos = extraInfos;
-
- mSnippet = "";
- mScore = 0;
}
/** Sets the title to the search result. */
@NonNull
public Builder setTitle(@NonNull String title) {
- this.mTitle = title;
return this;
}
/** Sets the snippet to the search result. */
@NonNull
public Builder setSnippet(@NonNull String snippet) {
- this.mSnippet = snippet;
return this;
}
/** Sets the ranking score to the search result. */
@NonNull
public Builder setScore(float score) {
- this.mScore = score;
return this;
}
/** Adds extra information to the search result for rendering in the UI. */
@NonNull
public Builder setExtraInfos(@NonNull Bundle extraInfos) {
- this.mExtraInfos = extraInfos;
return this;
}
/** Builds a SearchResult based-on the given parameters. */
@NonNull
public SearchResult build() {
- if (mTitle == null || mExtraInfos == null || mSnippet == null) {
- throw new IllegalStateException("Please make sure all required args are assigned.");
- }
-
- return new SearchResult(mTitle, mSnippet, mScore, mExtraInfos);
+ return new SearchResult();
}
}
}
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 8cfbf2faee67..9c99da52d962 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -57,6 +57,12 @@ interface IVirtualDevice {
void onAudioSessionEnded();
+ void createVirtualDpad(
+ int displayId,
+ String inputDeviceName,
+ int vendorId,
+ int productId,
+ IBinder token);
void createVirtualKeyboard(
int displayId,
String inputDeviceName,
@@ -77,6 +83,7 @@ interface IVirtualDevice {
IBinder token,
in Point screenSize);
void unregisterInputDevice(IBinder token);
+ boolean sendDpadKeyEvent(IBinder token, in VirtualKeyEvent event);
boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event);
boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event);
boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event);
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 1b93bb851567..fadfa5cd68a5 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -37,6 +37,7 @@ import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplay;
import android.hardware.display.VirtualDisplayConfig;
+import android.hardware.input.VirtualDpad;
import android.hardware.input.VirtualKeyboard;
import android.hardware.input.VirtualMouse;
import android.hardware.input.VirtualTouchscreen;
@@ -315,6 +316,32 @@ public final class VirtualDeviceManager {
}
/**
+ * Creates a virtual dpad.
+ *
+ * @param display the display that the events inputted through this device should target
+ * @param inputDeviceName the name to call this input device
+ * @param vendorId the PCI vendor id
+ * @param productId the product id, as defined by the vendor
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ public VirtualDpad createVirtualDpad(
+ @NonNull VirtualDisplay display,
+ @NonNull String inputDeviceName,
+ int vendorId,
+ int productId) {
+ try {
+ final IBinder token = new Binder(
+ "android.hardware.input.VirtualDpad:" + inputDeviceName);
+ mVirtualDevice.createVirtualDpad(display.getDisplay().getDisplayId(),
+ inputDeviceName, vendorId, productId, token);
+ return new VirtualDpad(mVirtualDevice, token);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Creates a virtual keyboard.
*
* @param display the display that the events inputted through this device should target
@@ -418,8 +445,8 @@ public final class VirtualDeviceManager {
@Nullable Executor executor,
@Nullable AudioConfigurationChangeCallback callback) {
if (mVirtualAudioDevice == null) {
- mVirtualAudioDevice = new VirtualAudioDevice(
- mContext, mVirtualDevice, display, executor, callback);
+ mVirtualAudioDevice = new VirtualAudioDevice(mContext, mVirtualDevice, display,
+ executor, callback, () -> mVirtualAudioDevice = null);
}
return mVirtualAudioDevice;
}
diff --git a/core/java/android/companion/virtual/audio/VirtualAudioDevice.java b/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
index 0db7b5fe8289..e200a117e475 100644
--- a/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
+++ b/core/java/android/companion/virtual/audio/VirtualAudioDevice.java
@@ -64,11 +64,24 @@ public final class VirtualAudioDevice implements Closeable {
void onRecordingConfigChanged(@NonNull List<AudioRecordingConfiguration> configs);
}
+ /**
+ * Interface to be notified when {@link #close()} is called.
+ *
+ * @hide
+ */
+ public interface CloseListener {
+ /**
+ * Notifies when {@link #close()} is called.
+ */
+ void onClosed();
+ }
+
private final Context mContext;
private final IVirtualDevice mVirtualDevice;
private final VirtualDisplay mVirtualDisplay;
private final AudioConfigurationChangeCallback mCallback;
private final Executor mExecutor;
+ private final CloseListener mListener;
@Nullable
private VirtualAudioSession mOngoingSession;
@@ -77,12 +90,13 @@ public final class VirtualAudioDevice implements Closeable {
*/
public VirtualAudioDevice(Context context, IVirtualDevice virtualDevice,
@NonNull VirtualDisplay virtualDisplay, @Nullable Executor executor,
- @Nullable AudioConfigurationChangeCallback callback) {
+ @Nullable AudioConfigurationChangeCallback callback, @Nullable CloseListener listener) {
mContext = context;
mVirtualDevice = virtualDevice;
mVirtualDisplay = virtualDisplay;
mExecutor = executor;
mCallback = callback;
+ mListener = listener;
}
/**
@@ -169,6 +183,10 @@ public final class VirtualAudioDevice implements Closeable {
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
+
+ if (mListener != null) {
+ mListener.onClosed();
+ }
}
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 97da2daf6e59..430b52c28f27 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -51,6 +51,7 @@ import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PermissionMethod;
import android.content.res.AssetManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -6066,6 +6067,7 @@ public abstract class Context {
*/
@CheckResult(suggest="#enforcePermission(String,int,int,String)")
@PackageManager.PermissionResult
+ @PermissionMethod
public abstract int checkPermission(@NonNull String permission, int pid, int uid);
/** @hide */
@@ -6098,6 +6100,7 @@ public abstract class Context {
*/
@CheckResult(suggest="#enforceCallingPermission(String,String)")
@PackageManager.PermissionResult
+ @PermissionMethod
public abstract int checkCallingPermission(@NonNull String permission);
/**
@@ -6118,6 +6121,7 @@ public abstract class Context {
*/
@CheckResult(suggest="#enforceCallingOrSelfPermission(String,String)")
@PackageManager.PermissionResult
+ @PermissionMethod
public abstract int checkCallingOrSelfPermission(@NonNull String permission);
/**
@@ -6146,6 +6150,7 @@ public abstract class Context {
*
* @see #checkPermission(String, int, int)
*/
+ @PermissionMethod
public abstract void enforcePermission(
@NonNull String permission, int pid, int uid, @Nullable String message);
@@ -6167,6 +6172,7 @@ public abstract class Context {
*
* @see #checkCallingPermission(String)
*/
+ @PermissionMethod
public abstract void enforceCallingPermission(
@NonNull String permission, @Nullable String message);
@@ -6183,6 +6189,7 @@ public abstract class Context {
*
* @see #checkCallingOrSelfPermission(String)
*/
+ @PermissionMethod
public abstract void enforceCallingOrSelfPermission(
@NonNull String permission, @Nullable String message);
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index f9d3222ada2a..b95f8bb72218 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -1,11 +1,10 @@
# Bug component: 36137
-toddke@android.com
-toddke@google.com
patb@google.com
+per-file Package* = file:/PACKAGE_MANAGER_OWNERS
per-file PackageParser.java = set noparent
-per-file PackageParser.java = chiuwinson@google.com,patb@google.com,toddke@google.com
+per-file PackageParser.java = chiuwinson@google.com,patb@google.com
per-file *Capability* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file AppSearchPerson.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8e2a5eaff2d2..db991dcd3afc 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4187,6 +4187,13 @@ public abstract class PackageManager {
public static final String FEATURE_WINDOW_MAGNIFICATION =
"android.software.window_magnification";
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device
+ * supports retrieval of user credentials, via integration with credential providers.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_CREDENTIALS = "android.software.credentials";
+
/** @hide */
public static final boolean APP_ENUMERATION_ENABLED_BY_DEFAULT = true;
diff --git a/core/java/android/content/pm/PermissionMethod.java b/core/java/android/content/pm/PermissionMethod.java
new file mode 100644
index 000000000000..021b2e1cd8f1
--- /dev/null
+++ b/core/java/android/content/pm/PermissionMethod.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.CLASS;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * Documents that the subject method's job is to look
+ * up whether the provided or calling uid/pid has the requested permission.
+ *
+ * Methods should either return `void`, but potentially throw {@link SecurityException},
+ * or return {@link android.content.pm.PackageManager.PermissionResult} `int`.
+ *
+ * @hide
+ */
+@Retention(CLASS)
+@Target({METHOD})
+public @interface PermissionMethod {}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 10d6f2d6d04b..64fed63c7159 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -78,7 +78,6 @@ public class ApkLiteParseUtils {
public static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
private static final int PARSE_IS_SYSTEM_DIR = 1 << 4;
private static final int PARSE_COLLECT_CERTIFICATES = 1 << 5;
- private static final int PARSE_FRAMEWORK_RES_SPLITS = 1 << 8;
private static final String TAG_APPLICATION = "application";
private static final String TAG_PACKAGE_VERIFIER = "package-verifier";
private static final String TAG_PROFILEABLE = "profileable";
@@ -103,7 +102,7 @@ public class ApkLiteParseUtils {
public static ParseResult<PackageLite> parsePackageLite(ParseInput input,
File packageFile, int flags) {
if (packageFile.isDirectory()) {
- return parseClusterPackageLite(input, packageFile, /* frameworkSplits= */ null, flags);
+ return parseClusterPackageLite(input, packageFile, flags);
} else {
return parseMonolithicPackageLite(input, packageFile, flags);
}
@@ -137,38 +136,19 @@ public class ApkLiteParseUtils {
/**
* Parse lightweight details about a directory of APKs.
*
- * @param packageDirOrApk is the folder that contains split apks for a regular app or the
- * framework-res.apk for framwork-res splits (in which case the
- * splits come in the <code>frameworkSplits</code> parameter)
+ * @param packageDir is the folder that contains split apks for a regular app
*/
public static ParseResult<PackageLite> parseClusterPackageLite(ParseInput input,
- File packageDirOrApk, List<File> frameworkSplits, int flags) {
+ File packageDir, int flags) {
final File[] files;
- final boolean parsingFrameworkSplits = (flags & PARSE_FRAMEWORK_RES_SPLITS) != 0;
- if (parsingFrameworkSplits) {
- if (ArrayUtils.isEmpty(frameworkSplits)) {
- return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
- "No packages found in split");
- }
- files = frameworkSplits.toArray(new File[frameworkSplits.size() + 1]);
- // we also want to process the base apk so add it to the array
- files[files.length - 1] = packageDirOrApk;
- } else {
- files = packageDirOrApk.listFiles();
- if (ArrayUtils.isEmpty(files)) {
- return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
- "No packages found in split");
- }
- // Apk directory is directly nested under the current directory
- if (files.length == 1 && files[0].isDirectory()) {
- return parseClusterPackageLite(input, files[0], frameworkSplits, flags);
- }
+ files = packageDir.listFiles();
+ if (ArrayUtils.isEmpty(files)) {
+ return input.error(PackageManager.INSTALL_PARSE_FAILED_NOT_APK,
+ "No packages found in split");
}
-
- if (parsingFrameworkSplits) {
- // disable the flag for checking the certificates of the splits. We know they
- // won't match, but we rely on the mainline apex to be safe if it was installed
- flags = flags & ~PARSE_COLLECT_CERTIFICATES;
+ // Apk directory is directly nested under the current directory
+ if (files.length == 1 && files[0].isDirectory()) {
+ return parseClusterPackageLite(input, files[0], flags);
}
String packageName = null;
@@ -186,10 +166,6 @@ public class ApkLiteParseUtils {
}
final ApkLite lite = result.getResult();
- if (parsingFrameworkSplits && file == files[files.length - 1]) {
- baseApk = lite;
- break;
- }
// Assert that all package names and version codes are
// consistent with the first one we encounter.
if (packageName == null) {
@@ -201,8 +177,7 @@ public class ApkLiteParseUtils {
"Inconsistent package " + lite.getPackageName() + " in " + file
+ "; expected " + packageName);
}
- // we allow version codes that do not match for framework splits
- if (!parsingFrameworkSplits && versionCode != lite.getVersionCode()) {
+ if (versionCode != lite.getVersionCode()) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Inconsistent version " + lite.getVersionCode() + " in " + file
+ "; expected " + versionCode);
@@ -217,15 +192,11 @@ public class ApkLiteParseUtils {
}
}
}
- // baseApk is set in the last iteration of the for each loop when we are parsing
- // frameworkRes splits or needs to be done now otherwise
- if (!parsingFrameworkSplits) {
- baseApk = apks.remove(null);
- }
+ baseApk = apks.remove(null);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- return composePackageLiteFromApks(input, packageDirOrApk, baseApk, apks);
+ return composePackageLiteFromApks(input, packageDir, baseApk, apks);
}
/**
diff --git a/core/java/android/content/res/ResourceTimer.java b/core/java/android/content/res/ResourceTimer.java
new file mode 100644
index 000000000000..d51f64ce8106
--- /dev/null
+++ b/core/java/android/content/res/ResourceTimer.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.res;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import android.app.AppProtoEnums;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Provides access to the resource timers without intruding on other classes.
+ * @hide
+ */
+public final class ResourceTimer {
+ private static final String TAG = "ResourceTimer";
+
+ // Enable metrics in this process. The flag may be set false in some processes. The flag is
+ // never set true at runtime, so setting it false here disables the feature entirely.
+ private static boolean sEnabled = true;
+
+ // Set true for incremental metrics (the counters are reset after every fetch). Set false for
+ // cumulative metrics (the counters are never reset and accumulate values for the lifetime of
+ // the process).
+ private static boolean sIncrementalMetrics = true;
+
+ // Set true to enable some debug behavior.
+ private static boolean ENABLE_DEBUG = false;
+
+ // The global lock.
+ private static final Object sLock = new Object();
+
+ // The singleton cache manager
+ private static ResourceTimer sManager;
+
+ // The handler for the timeouts.
+ private static Handler mHandler;
+
+ // The time at which the process started.
+ private final static long sProcessStart = SystemClock.uptimeMillis();
+
+ // Metrics are published at offsets from the process start. Processes publish at five minutes
+ // and one hour. Thereafter, metrics are published every 12 hours. The values in this array
+ // are in units of minutes.
+ private static final long[] sPublicationPoints = new long[]{ 5, 60, 60 * 12 };
+
+ // The index of the latest publication point.
+ private static int sCurrentPoint;
+
+ /**
+ * The runtime timer configuration.
+ */
+ private static class Config {
+ // The number of timers in the runtime
+ int maxTimer;
+ // The number of histogram buckets per timer.
+ int maxBuckets;
+ // The number of "largest" values per timer.
+ int maxLargest;
+ // A string label for each timer. This array has maxTimer elements.
+ String[] timers;
+ }
+
+ /**
+ * The timer information that is pulled from the native runtime. All times have units of ns.
+ */
+ private static class Timer {
+ int count;
+ long total;
+ int mintime;
+ int maxtime;
+ int[] largest;
+ int[] percentile;
+ @Override
+ public String toString() {
+ return TextUtils.formatSimple("%d:%d:%d:%d", count, total, mintime, maxtime);
+ }
+ }
+
+ /**
+ * A singleton Config. This is initialized when the timers are started.
+ */
+ @GuardedBy("sLock")
+ private static Config sConfig;
+
+ /**
+ * This array contains the statsd enum associated with each timer entry. A value of NONE (0)
+ * means that the entry should not be logged to statsd. (This would be the case for timers
+ * that are created for temporary debugging.)
+ */
+ @GuardedBy("sLock")
+ private static int[] sApiMap;
+
+ /**
+ * A singleton Summary object that is refilled from the native side. The length of the array
+ * is the number of timers that can be fetched. nativeGetTimers() will fill the array to the
+ * smaller of the length of the array or the actual number of timers in the runtime. The
+ * actual number of timers in the run time is returned by the function.
+ */
+ @GuardedBy("sLock")
+ private static Timer[] sTimers;
+
+ /**
+ * The time at which the local timer array was last updated. This has the same units as
+ * sProcessStart; the difference is the process run time.
+ */
+ @GuardedBy("sLock")
+ private static long sLastUpdated = 0;
+
+ // The constructor is private. Use the factory to get a hold of the manager.
+ private ResourceTimer() {
+ throw new RuntimeException("ResourceTimer constructor");
+ }
+
+ /**
+ * Start the manager. This runs a periodic job that collects and publishes timer values.
+ * This is not part of the constructor only because the looper failicities might not be
+ * available at the beginning of time.
+ */
+ public static void start() {
+ synchronized (sLock) {
+ if (!sEnabled) {
+ return;
+ }
+ if (mHandler != null) {
+ // Nothing to be done. The job has already been started.
+ return;
+ }
+ if (Looper.getMainLooper() == null) {
+ throw new RuntimeException("ResourceTimer started too early");
+ }
+ mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ ResourceTimer.handleMessage(msg);
+ }
+ };
+
+ // Initialize the container that holds information from the native runtime. The
+ // container is built according to the dimensions returned by the native layer.
+ sConfig = new Config();
+ nativeEnableTimers(sConfig);
+ sTimers = new Timer[sConfig.maxTimer];
+ for (int i = 0; i < sTimers.length; i++) {
+ sTimers[i] = new Timer();
+ sTimers[i].percentile = new int[sConfig.maxBuckets];
+ sTimers[i].largest = new int[sConfig.maxLargest];
+ }
+ // Map the values returned from the runtime to statsd enumerals The runtime may
+ // return timers that are not meant to be logged via statsd. Such timers are mapped
+ // to RESOURCE_API_NONE.
+ sApiMap = new int[sConfig.maxTimer];
+ for (int i = 0; i < sApiMap.length; i++) {
+ if (sConfig.timers[i].equals("GetResourceValue")) {
+ sApiMap[i] = AppProtoEnums.RESOURCE_API_GET_VALUE;
+ } else if (sConfig.timers[i].equals("RetrieveAttributes")) {
+ sApiMap[i] = AppProtoEnums.RESOURCE_API_RETRIEVE_ATTRIBUTES;
+ } else {
+ sApiMap[i] = AppProtoEnums.RESOURCE_API_NONE;
+ }
+ }
+
+ sCurrentPoint = 0;
+ startTimer();
+ }
+ }
+
+ /**
+ * Handle a refresh message. Publish the metrics and start a timer for the next publication.
+ * The message parameter is unused.
+ */
+ private static void handleMessage(Message msg) {
+ synchronized (sLock) {
+ publish();
+ startTimer();
+ }
+ }
+
+ /**
+ * Start a timer to the next publication point. Publication points are referenced from
+ * process start.
+ */
+ @GuardedBy("sLock")
+ private static void startTimer() {
+ // The delay is in minutes.
+ long delay;
+ if (sCurrentPoint < sPublicationPoints.length) {
+ delay = sPublicationPoints[sCurrentPoint];
+ } else {
+ // Repeat with the final publication point.
+ final long repeated = sPublicationPoints[sPublicationPoints.length - 1];
+ final int prelude = sPublicationPoints.length - 1;
+ delay = (sCurrentPoint - prelude) * repeated;
+ }
+ // Convert minutes to milliseconds.
+ delay *= 60 * 1000;
+ // If debug is enabled, convert hours down to minutes.
+ if (ENABLE_DEBUG) {
+ delay /= 60;
+ }
+ mHandler.sendEmptyMessageAtTime(0, sProcessStart + delay);
+ }
+
+ /**
+ * Update the local copy of the timers. The current time is saved as well.
+ */
+ @GuardedBy("sLock")
+ private static void update(boolean reset) {
+ nativeGetTimers(sTimers, reset);
+ sLastUpdated = SystemClock.uptimeMillis();
+ }
+
+ /**
+ * Retrieve the accumulated timer information, reset the native counters, and advance the
+ * publication point.
+ */
+ @GuardedBy("sLock")
+ private static void publish() {
+ update(true);
+ // Log the number of records read. This happens a few times a day.
+ for (int i = 0; i < sTimers.length; i++) {
+ var timer = sTimers[i];
+ if (timer.count > 0) {
+ Log.i(TAG, TextUtils.formatSimple("%s count=%d pvalues=%s",
+ sConfig.timers[i], timer.count, packedString(timer.percentile)));
+ if (sApiMap[i] != AppProtoEnums.RESOURCE_API_NONE) {
+ FrameworkStatsLog.write(FrameworkStatsLog.RESOURCE_API_INFO,
+ sApiMap[i],
+ timer.count, timer.total,
+ timer.percentile[0], timer.percentile[1],
+ timer.percentile[2], timer.percentile[3],
+ timer.largest[0], timer.largest[1], timer.largest[2],
+ timer.largest[3], timer.largest[4]);
+ }
+ }
+ }
+ sCurrentPoint++;
+ }
+
+ /**
+ * Given an int[], return a string that is formatted as "a,b,c" with no spaces.
+ */
+ private static String packedString(int[] a) {
+ return Arrays.toString(a).replaceAll("[\\]\\[ ]", "");
+ }
+
+ /**
+ * Update the metrics information and dump it.
+ * @hide
+ */
+ public static void dumpTimers(@NonNull ParcelFileDescriptor pfd, @Nullable String[] args) {
+ FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor());
+ PrintWriter pw = new FastPrintWriter(fout);
+ synchronized (sLock) {
+ if (!sEnabled || (sConfig == null)) {
+ pw.println(" Timers are not enabled in this process");
+ pw.flush();
+ return;
+ }
+ }
+
+ // Look for the --refresh switch. If the switch is present, then sTimers is updated.
+ // Otherwise, the current value of sTimers is displayed.
+ boolean refresh = Arrays.asList(args).contains("-refresh");
+
+ synchronized (sLock) {
+ update(refresh);
+ long runtime = sLastUpdated - sProcessStart;
+ pw.format(" config runtime=%d proc=%s\n", runtime, Process.myProcessName());
+ for (int i = 0; i < sTimers.length; i++) {
+ Timer t = sTimers[i];
+ if (t.count != 0) {
+ String name = sConfig.timers[i];
+ pw.format(" stats timer=%s cnt=%d avg=%d min=%d max=%d pval=%s "
+ + "largest=%s\n",
+ name, t.count, t.total / t.count, t.mintime, t.maxtime,
+ packedString(t.percentile),
+ packedString(t.largest));
+ }
+ }
+ }
+ pw.flush();
+ }
+
+ // Enable (or disabled) the runtime timers. Note that timers are disabled by default. This
+ // retrieves the runtime timer configuration that are taking effect
+ private static native int nativeEnableTimers(@NonNull Config config);
+
+ // Retrieve the timers from the native layer. If reset is true, the timers are reset after
+ // being fetched. The method returns the number of timers that are defined in the runtime
+ // layer. The stats array is filled out to the smaller of its actual size and the number of
+ // runtime timers; it never overflows.
+ private static native int nativeGetTimers(@NonNull Timer[] stats, boolean reset);
+}
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
index 53dfed374fbc..3915a6ccb46d 100644
--- a/core/java/android/content/res/XmlBlock.java
+++ b/core/java/android/content/res/XmlBlock.java
@@ -17,6 +17,7 @@
package android.content.res;
import static android.content.res.Resources.ID_NULL;
+import static android.system.OsConstants.EINVAL;
import android.annotation.AnyRes;
import android.annotation.NonNull;
@@ -28,6 +29,7 @@ import android.util.TypedValue;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.XmlUtils;
+import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
import org.xmlpull.v1.XmlPullParserException;
@@ -92,6 +94,16 @@ public final class XmlBlock implements AutoCloseable {
}
}
+ /**
+ * Reference Error.h UNEXPECTED_NULL
+ */
+ private static final int ERROR_NULL_DOCUMENT = Integer.MIN_VALUE + 8;
+ /**
+ * The reason not to ResXMLParser::BAD_DOCUMENT which is -1 is that other places use the same
+ * value. Reference Error.h BAD_VALUE = -EINVAL
+ */
+ private static final int ERROR_BAD_DOCUMENT = -EINVAL;
+
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public final class Parser implements XmlResourceParser {
Parser(long parseState, XmlBlock block) {
@@ -168,7 +180,11 @@ public final class XmlBlock implements AutoCloseable {
return id >= 0 ? getSequenceString(mStrings.getSequence(id)) : null;
}
public int getLineNumber() {
- return nativeGetLineNumber(mParseState);
+ final int lineNumber = nativeGetLineNumber(mParseState);
+ if (lineNumber == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
+ return lineNumber;
}
public int getEventType() throws XmlPullParserException {
return mEventType;
@@ -203,7 +219,10 @@ public final class XmlBlock implements AutoCloseable {
}
@NonNull
public String getAttributeNamespace(int index) {
- int id = nativeGetAttributeNamespace(mParseState, index);
+ final int id = nativeGetAttributeNamespace(mParseState, index);
+ if (id == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
if (DEBUG) System.out.println("getAttributeNamespace of " + index + " = " + id);
if (id >= 0) return getSequenceString(mStrings.getSequence(id));
else if (id == -1) return "";
@@ -211,8 +230,11 @@ public final class XmlBlock implements AutoCloseable {
}
@NonNull
public String getAttributeName(int index) {
- int id = nativeGetAttributeName(mParseState, index);
+ final int id = nativeGetAttributeName(mParseState, index);
if (DEBUG) System.out.println("getAttributeName of " + index + " = " + id);
+ if (id == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
if (id >= 0) return getSequenceString(mStrings.getSequence(id));
throw new IndexOutOfBoundsException(String.valueOf(index));
}
@@ -224,21 +246,38 @@ public final class XmlBlock implements AutoCloseable {
return false;
}
public int getAttributeCount() {
- return mEventType == START_TAG ? nativeGetAttributeCount(mParseState) : -1;
+ if (mEventType == START_TAG) {
+ final int count = nativeGetAttributeCount(mParseState);
+ if (count == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
+ return count;
+ } else {
+ return -1;
+ }
}
@NonNull
public String getAttributeValue(int index) {
- int id = nativeGetAttributeStringValue(mParseState, index);
+ final int id = nativeGetAttributeStringValue(mParseState, index);
+ if (id == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
if (DEBUG) System.out.println("getAttributeValue of " + index + " = " + id);
if (id >= 0) return getSequenceString(mStrings.getSequence(id));
// May be some other type... check and try to convert if so.
- int t = nativeGetAttributeDataType(mParseState, index);
+ final int t = nativeGetAttributeDataType(mParseState, index);
+ if (t == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
if (t == TypedValue.TYPE_NULL) {
throw new IndexOutOfBoundsException(String.valueOf(index));
}
- int v = nativeGetAttributeData(mParseState, index);
+ final int v = nativeGetAttributeData(mParseState, index);
+ if (v == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
return TypedValue.coerceToString(t, v);
}
public String getAttributeType(int index) {
@@ -272,6 +311,9 @@ public final class XmlBlock implements AutoCloseable {
return END_DOCUMENT;
}
int ev = nativeNext(mParseState);
+ if (ev == ERROR_BAD_DOCUMENT) {
+ throw new XmlPullParserException("Corrupt XML binary file");
+ }
if (mDecNextDepth) {
mDepth--;
mDecNextDepth = false;
@@ -338,7 +380,11 @@ public final class XmlBlock implements AutoCloseable {
}
public int getAttributeNameResource(int index) {
- return nativeGetAttributeResource(mParseState, index);
+ final int resourceNameId = nativeGetAttributeResource(mParseState, index);
+ if (resourceNameId == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
+ return resourceNameId;
}
public int getAttributeListValue(String namespace, String attribute,
@@ -393,8 +439,14 @@ public final class XmlBlock implements AutoCloseable {
public int getAttributeListValue(int idx,
String[] options, int defaultValue) {
- int t = nativeGetAttributeDataType(mParseState, idx);
- int v = nativeGetAttributeData(mParseState, idx);
+ final int t = nativeGetAttributeDataType(mParseState, idx);
+ if (t == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
+ final int v = nativeGetAttributeData(mParseState, idx);
+ if (v == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
if (t == TypedValue.TYPE_STRING) {
return XmlUtils.convertValueToList(
mStrings.getSequence(v), options, defaultValue);
@@ -403,62 +455,99 @@ public final class XmlBlock implements AutoCloseable {
}
public boolean getAttributeBooleanValue(int idx,
boolean defaultValue) {
- int t = nativeGetAttributeDataType(mParseState, idx);
+ final int t = nativeGetAttributeDataType(mParseState, idx);
+ if (t == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
// Note: don't attempt to convert any other types, because
// we want to count on aapt doing the conversion for us.
- if (t >= TypedValue.TYPE_FIRST_INT &&
- t <= TypedValue.TYPE_LAST_INT) {
- return nativeGetAttributeData(mParseState, idx) != 0;
+ if (t >= TypedValue.TYPE_FIRST_INT && t <= TypedValue.TYPE_LAST_INT) {
+ final int v = nativeGetAttributeData(mParseState, idx);
+ if (v == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
+ return v != 0;
}
return defaultValue;
}
public int getAttributeResourceValue(int idx, int defaultValue) {
- int t = nativeGetAttributeDataType(mParseState, idx);
+ final int t = nativeGetAttributeDataType(mParseState, idx);
+ if (t == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
// Note: don't attempt to convert any other types, because
// we want to count on aapt doing the conversion for us.
if (t == TypedValue.TYPE_REFERENCE) {
- return nativeGetAttributeData(mParseState, idx);
+ final int v = nativeGetAttributeData(mParseState, idx);
+ if (v == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
+ return v;
}
return defaultValue;
}
public int getAttributeIntValue(int idx, int defaultValue) {
- int t = nativeGetAttributeDataType(mParseState, idx);
+ final int t = nativeGetAttributeDataType(mParseState, idx);
+ if (t == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
// Note: don't attempt to convert any other types, because
// we want to count on aapt doing the conversion for us.
- if (t >= TypedValue.TYPE_FIRST_INT &&
- t <= TypedValue.TYPE_LAST_INT) {
- return nativeGetAttributeData(mParseState, idx);
+ if (t >= TypedValue.TYPE_FIRST_INT && t <= TypedValue.TYPE_LAST_INT) {
+ final int v = nativeGetAttributeData(mParseState, idx);
+ if (v == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
+ return v;
}
return defaultValue;
}
public int getAttributeUnsignedIntValue(int idx, int defaultValue) {
int t = nativeGetAttributeDataType(mParseState, idx);
+ if (t == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
// Note: don't attempt to convert any other types, because
// we want to count on aapt doing the conversion for us.
- if (t >= TypedValue.TYPE_FIRST_INT &&
- t <= TypedValue.TYPE_LAST_INT) {
- return nativeGetAttributeData(mParseState, idx);
+ if (t >= TypedValue.TYPE_FIRST_INT && t <= TypedValue.TYPE_LAST_INT) {
+ final int v = nativeGetAttributeData(mParseState, idx);
+ if (v == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
+ return v;
}
return defaultValue;
}
public float getAttributeFloatValue(int idx, float defaultValue) {
- int t = nativeGetAttributeDataType(mParseState, idx);
+ final int t = nativeGetAttributeDataType(mParseState, idx);
+ if (t == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
// Note: don't attempt to convert any other types, because
// we want to count on aapt doing the conversion for us.
if (t == TypedValue.TYPE_FLOAT) {
- return Float.intBitsToFloat(
- nativeGetAttributeData(mParseState, idx));
+ final int v = nativeGetAttributeData(mParseState, idx);
+ if (v == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
+ return Float.intBitsToFloat(v);
}
throw new RuntimeException("not a float!");
}
@Nullable
public String getIdAttribute() {
- int id = nativeGetIdAttribute(mParseState);
+ final int id = nativeGetIdAttribute(mParseState);
+ if (id == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
return id >= 0 ? getSequenceString(mStrings.getSequence(id)) : null;
}
@Nullable
public String getClassAttribute() {
- int id = nativeGetClassAttribute(mParseState);
+ final int id = nativeGetClassAttribute(mParseState);
+ if (id == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
return id >= 0 ? getSequenceString(mStrings.getSequence(id)) : null;
}
@@ -468,7 +557,11 @@ public final class XmlBlock implements AutoCloseable {
}
public int getStyleAttribute() {
- return nativeGetStyleAttribute(mParseState);
+ final int styleAttributeId = nativeGetStyleAttribute(mParseState);
+ if (styleAttributeId == ERROR_NULL_DOCUMENT) {
+ throw new NullPointerException("Null document");
+ }
+ return styleAttributeId;
}
private String getSequenceString(@Nullable CharSequence str) {
@@ -544,37 +637,55 @@ public final class XmlBlock implements AutoCloseable {
// ----------- @FastNative ------------------
@FastNative
+ private static native int nativeGetAttributeIndex(
+ long state, String namespace, String name);
+
+ // ----------- @CriticalNative ------------------
+ @CriticalNative
/*package*/ static final native int nativeNext(long state);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetNamespace(long state);
- @FastNative
+
+ @CriticalNative
/*package*/ static final native int nativeGetName(long state);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetText(long state);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetLineNumber(long state);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetAttributeCount(long state);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetAttributeNamespace(long state, int idx);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetAttributeName(long state, int idx);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetAttributeResource(long state, int idx);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetAttributeDataType(long state, int idx);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetAttributeData(long state, int idx);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetAttributeStringValue(long state, int idx);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetIdAttribute(long state);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetClassAttribute(long state);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetStyleAttribute(long state);
- @FastNative
- private static final native int nativeGetAttributeIndex(long state, String namespace, String name);
- @FastNative
+
+ @CriticalNative
private static final native int nativeGetSourceResId(long state);
}
diff --git a/core/java/android/ddm/DdmHandleViewDebug.java b/core/java/android/ddm/DdmHandleViewDebug.java
index 6b0f78f2c1c3..0f66fcbdbec9 100644
--- a/core/java/android/ddm/DdmHandleViewDebug.java
+++ b/core/java/android/ddm/DdmHandleViewDebug.java
@@ -16,12 +16,16 @@
package android.ddm;
+import static com.android.internal.util.Preconditions.checkArgument;
+
import android.util.Log;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewRootImpl;
import android.view.WindowManagerGlobal;
+import com.android.internal.annotations.VisibleForTesting;
+
import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
import org.apache.harmony.dalvik.ddmc.DdmServer;
@@ -34,6 +38,7 @@ import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
/**
* Handle various requests related to profiling / debugging of the view system.
@@ -123,14 +128,15 @@ public class DdmHandleViewDebug extends DdmHandle {
}
if (type == CHUNK_VURT) {
- if (op == VURT_DUMP_HIERARCHY)
+ if (op == VURT_DUMP_HIERARCHY) {
return dumpHierarchy(rootView, in);
- else if (op == VURT_CAPTURE_LAYERS)
+ } else if (op == VURT_CAPTURE_LAYERS) {
return captureLayers(rootView);
- else if (op == VURT_DUMP_THEME)
+ } else if (op == VURT_DUMP_THEME) {
return dumpTheme(rootView);
- else
+ } else {
return createFailChunk(ERR_INVALID_OP, "Unknown view root operation: " + op);
+ }
}
final View targetView = getTargetView(rootView, in);
@@ -207,9 +213,9 @@ public class DdmHandleViewDebug extends DdmHandle {
/**
* Returns the view hierarchy and/or view properties starting at the provided view.
* Based on the input options, the return data may include:
- * - just the view hierarchy
- * - view hierarchy & the properties for each of the views
- * - just the view properties for a specific view.
+ * - just the view hierarchy
+ * - view hierarchy & the properties for each of the views
+ * - just the view properties for a specific view.
* TODO: Currently this only returns views starting at the root, need to fix so that
* it can return properties of any view.
*/
@@ -220,7 +226,7 @@ public class DdmHandleViewDebug extends DdmHandle {
long start = System.currentTimeMillis();
- ByteArrayOutputStream b = new ByteArrayOutputStream(2*1024*1024);
+ ByteArrayOutputStream b = new ByteArrayOutputStream(2 * 1024 * 1024);
try {
if (v2) {
ViewDebug.dumpv2(rootView, b);
@@ -304,17 +310,47 @@ public class DdmHandleViewDebug extends DdmHandle {
* Invokes provided method on the view.
* The method name and its arguments are passed in as inputs via the byte buffer.
* The buffer contains:<ol>
- * <li> len(method name) </li>
- * <li> method name </li>
- * <li> # of args </li>
- * <li> arguments: Each argument comprises of a type specifier followed by the actual argument.
- * The type specifier is a single character as used in JNI:
- * (Z - boolean, B - byte, C - char, S - short, I - int, J - long,
- * F - float, D - double). <p>
- * The type specifier is followed by the actual value of argument.
- * Booleans are encoded via bytes with 0 indicating false.</li>
+ * <li> len(method name) </li>
+ * <li> method name (encoded as UTF-16 2-byte characters) </li>
+ * <li> # of args </li>
+ * <li> arguments: Each argument comprises of a type specifier followed by the actual argument.
+ * The type specifier is one character modelled after JNI signatures:
+ * <ul>
+ * <li>[ - array<br>
+ * This is followed by a second character according to this spec, indicating the
+ * array type, then the array length as an Int, followed by a repeated encoding
+ * of the actual data.
+ * WARNING: Only <b>byte[]</b> is supported currently.
+ * </li>
+ * <li>Z - boolean<br>
+ * Booleans are encoded via bytes with 0 indicating false</li>
+ * <li>B - byte</li>
+ * <li>C - char</li>
+ * <li>S - short</li>
+ * <li>I - int</li>
+ * <li>J - long</li>
+ * <li>F - float</li>
+ * <li>D - double</li>
+ * <li>V - void<br>
+ * NOT followed by a value. Only used for return types</li>
+ * <li>R - String (not a real JNI type, but added for convenience)<br>
+ * Strings are encoded as an unsigned short of the number of <b>bytes</b>,
+ * followed by the actual UTF-8 encoded bytes.
+ * WARNING: This is the same encoding as produced by
+ * ViewHierarchyEncoder#writeString. However, note that this encoding is
+ * different to what DdmHandle#getString() expects, which is used in other places
+ * in this class.
+ * WARNING: Since the length is the number of UTF-8 encoded bytes, Strings can
+ * contain up to 64k ASCII characters, yet depending on the actual data, the true
+ * maximum might be as little as 21844 unicode characters.
+ * <b>null</b> String objects are encoded as an empty string
+ * </li>
+ * </ul>
+ * </li>
* </ol>
* Methods that take no arguments need only specify the method name.
+ *
+ * The return value is encoded the same way as a single parameter (type + value)
*/
private Chunk invokeViewMethod(final View rootView, final View targetView, ByteBuffer in) {
int l = in.getInt();
@@ -327,54 +363,17 @@ public class DdmHandleViewDebug extends DdmHandle {
args = new Object[0];
} else {
int nArgs = in.getInt();
-
argTypes = new Class<?>[nArgs];
args = new Object[nArgs];
- for (int i = 0; i < nArgs; i++) {
- char c = in.getChar();
- switch (c) {
- case 'Z':
- argTypes[i] = boolean.class;
- args[i] = in.get() == 0 ? false : true;
- break;
- case 'B':
- argTypes[i] = byte.class;
- args[i] = in.get();
- break;
- case 'C':
- argTypes[i] = char.class;
- args[i] = in.getChar();
- break;
- case 'S':
- argTypes[i] = short.class;
- args[i] = in.getShort();
- break;
- case 'I':
- argTypes[i] = int.class;
- args[i] = in.getInt();
- break;
- case 'J':
- argTypes[i] = long.class;
- args[i] = in.getLong();
- break;
- case 'F':
- argTypes[i] = float.class;
- args[i] = in.getFloat();
- break;
- case 'D':
- argTypes[i] = double.class;
- args[i] = in.getDouble();
- break;
- default:
- Log.e(TAG, "arg " + i + ", unrecognized type: " + c);
- return createFailChunk(ERR_INVALID_PARAM,
- "Unsupported parameter type (" + c + ") to invoke view method.");
- }
+ try {
+ deserializeMethodParameters(args, argTypes, in);
+ } catch (ViewMethodInvocationSerializationException e) {
+ return createFailChunk(ERR_INVALID_PARAM, e.getMessage());
}
}
- Method method = null;
+ Method method;
try {
method = targetView.getClass().getMethod(methodName, argTypes);
} catch (NoSuchMethodException e) {
@@ -384,7 +383,10 @@ public class DdmHandleViewDebug extends DdmHandle {
}
try {
- ViewDebug.invokeViewMethod(targetView, method, args);
+ Object result = ViewDebug.invokeViewMethod(targetView, method, args);
+ Class<?> returnType = method.getReturnType();
+ byte[] returnValue = serializeReturnValue(returnType, returnType.cast(result));
+ return new Chunk(CHUNK_VUOP, returnValue, 0, returnValue.length);
} catch (Exception e) {
Log.e(TAG, "Exception while invoking method: " + e.getCause().getMessage());
String msg = e.getCause().getMessage();
@@ -393,8 +395,6 @@ public class DdmHandleViewDebug extends DdmHandle {
}
return createFailChunk(ERR_EXCEPTION, msg);
}
-
- return null;
}
private Chunk setLayoutParameter(final View rootView, final View targetView, ByteBuffer in) {
@@ -406,7 +406,7 @@ public class DdmHandleViewDebug extends DdmHandle {
} catch (Exception e) {
Log.e(TAG, "Exception setting layout parameter: " + e);
return createFailChunk(ERR_EXCEPTION, "Error accessing field "
- + param + ":" + e.getMessage());
+ + param + ":" + e.getMessage());
}
return null;
@@ -431,4 +431,175 @@ public class DdmHandleViewDebug extends DdmHandle {
byte[] data = b.toByteArray();
return new Chunk(CHUNK_VUOP, data, 0, data.length);
}
+
+ /**
+ * Deserializes parameters according to the VUOP_INVOKE_VIEW_METHOD protocol the {@code in}
+ * buffer.
+ *
+ * The length of {@code args} determines how many arguments are read. The {@code argTypes} must
+ * be the same length, and will be set to the argument types of the data read.
+ *
+ * @hide
+ */
+ @VisibleForTesting
+ public static void deserializeMethodParameters(
+ Object[] args, Class<?>[] argTypes, ByteBuffer in) throws
+ ViewMethodInvocationSerializationException {
+ checkArgument(args.length == argTypes.length);
+
+ for (int i = 0; i < args.length; i++) {
+ char typeSignature = in.getChar();
+ boolean isArray = typeSignature == SIG_ARRAY;
+ if (isArray) {
+ char arrayType = in.getChar();
+ if (arrayType != SIG_BYTE) {
+ // This implementation only supports byte-arrays for now.
+ throw new ViewMethodInvocationSerializationException(
+ "Unsupported array parameter type (" + typeSignature
+ + ") to invoke view method @argument " + i);
+ }
+
+ int arrayLength = in.getInt();
+ if (arrayLength > in.remaining()) {
+ // The sender did not actually sent the specified amount of bytes. This
+ // avoids a malformed packet to trigger an out-of-memory error.
+ throw new BufferUnderflowException();
+ }
+
+ byte[] byteArray = new byte[arrayLength];
+ in.get(byteArray);
+
+ argTypes[i] = byte[].class;
+ args[i] = byteArray;
+ } else {
+ switch (typeSignature) {
+ case SIG_BOOLEAN:
+ argTypes[i] = boolean.class;
+ args[i] = in.get() != 0;
+ break;
+ case SIG_BYTE:
+ argTypes[i] = byte.class;
+ args[i] = in.get();
+ break;
+ case SIG_CHAR:
+ argTypes[i] = char.class;
+ args[i] = in.getChar();
+ break;
+ case SIG_SHORT:
+ argTypes[i] = short.class;
+ args[i] = in.getShort();
+ break;
+ case SIG_INT:
+ argTypes[i] = int.class;
+ args[i] = in.getInt();
+ break;
+ case SIG_LONG:
+ argTypes[i] = long.class;
+ args[i] = in.getLong();
+ break;
+ case SIG_FLOAT:
+ argTypes[i] = float.class;
+ args[i] = in.getFloat();
+ break;
+ case SIG_DOUBLE:
+ argTypes[i] = double.class;
+ args[i] = in.getDouble();
+ break;
+ case SIG_STRING: {
+ argTypes[i] = String.class;
+ int stringUtf8ByteCount = Short.toUnsignedInt(in.getShort());
+ byte[] rawStringBuffer = new byte[stringUtf8ByteCount];
+ in.get(rawStringBuffer);
+ args[i] = new String(rawStringBuffer, StandardCharsets.UTF_8);
+ break;
+ }
+ default:
+ Log.e(TAG, "arg " + i + ", unrecognized type: " + typeSignature);
+ throw new ViewMethodInvocationSerializationException(
+ "Unsupported parameter type (" + typeSignature
+ + ") to invoke view method.");
+ }
+ }
+
+ }
+ }
+
+ /**
+ * Serializes {@code value} to the wire protocol of VUOP_INVOKE_VIEW_METHOD.
+ * @hide
+ */
+ @VisibleForTesting
+ public static byte[] serializeReturnValue(Class<?> type, Object value)
+ throws ViewMethodInvocationSerializationException, IOException {
+ ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream(1024);
+ DataOutputStream dos = new DataOutputStream(byteOutStream);
+
+ if (type.isArray()) {
+ if (!type.equals(byte[].class)) {
+ // Only byte arrays are supported currently.
+ throw new ViewMethodInvocationSerializationException(
+ "Unsupported array return type (" + type + ")");
+ }
+ byte[] byteArray = (byte[]) value;
+ dos.writeChar(SIG_ARRAY);
+ dos.writeChar(SIG_BYTE);
+ dos.writeInt(byteArray.length);
+ dos.write(byteArray);
+ } else if (boolean.class.equals(type)) {
+ dos.writeChar(SIG_BOOLEAN);
+ dos.write((boolean) value ? 1 : 0);
+ } else if (byte.class.equals(type)) {
+ dos.writeChar(SIG_BYTE);
+ dos.writeByte((byte) value);
+ } else if (char.class.equals(type)) {
+ dos.writeChar(SIG_CHAR);
+ dos.writeChar((char) value);
+ } else if (short.class.equals(type)) {
+ dos.writeChar(SIG_SHORT);
+ dos.writeShort((short) value);
+ } else if (int.class.equals(type)) {
+ dos.writeChar(SIG_INT);
+ dos.writeInt((int) value);
+ } else if (long.class.equals(type)) {
+ dos.writeChar(SIG_LONG);
+ dos.writeLong((long) value);
+ } else if (double.class.equals(type)) {
+ dos.writeChar(SIG_DOUBLE);
+ dos.writeDouble((double) value);
+ } else if (float.class.equals(type)) {
+ dos.writeChar(SIG_FLOAT);
+ dos.writeFloat((float) value);
+ } else if (String.class.equals(type)) {
+ dos.writeChar(SIG_STRING);
+ dos.writeUTF(value != null ? (String) value : "");
+ } else {
+ dos.writeChar(SIG_VOID);
+ }
+
+ return byteOutStream.toByteArray();
+ }
+
+ // Prefixes for simple primitives. These match the JNI definitions.
+ private static final char SIG_ARRAY = '[';
+ private static final char SIG_BOOLEAN = 'Z';
+ private static final char SIG_BYTE = 'B';
+ private static final char SIG_SHORT = 'S';
+ private static final char SIG_CHAR = 'C';
+ private static final char SIG_INT = 'I';
+ private static final char SIG_LONG = 'J';
+ private static final char SIG_FLOAT = 'F';
+ private static final char SIG_DOUBLE = 'D';
+ private static final char SIG_VOID = 'V';
+ // Prefixes for some commonly used objects
+ private static final char SIG_STRING = 'R';
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static class ViewMethodInvocationSerializationException extends Exception {
+ ViewMethodInvocationSerializationException(String message) {
+ super(message);
+ }
+ }
}
diff --git a/core/java/android/hardware/DataSpace.java b/core/java/android/hardware/DataSpace.java
index 6c42776cca5e..15eae0920e7d 100644
--- a/core/java/android/hardware/DataSpace.java
+++ b/core/java/android/hardware/DataSpace.java
@@ -385,14 +385,6 @@ public final class DataSpace {
*/
public static final int RANGE_EXTENDED = 3 << 27;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {
- DATASPACE_DEPTH,
- DATASPACE_DYNAMIC_DEPTH,
- })
- public @interface DataSpaceDepth {};
-
/**
* Depth.
*
@@ -407,13 +399,6 @@ public final class DataSpace {
*/
public static final int DATASPACE_DYNAMIC_DEPTH = 4098;
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, value = {
- DATASPACE_HEIF,
- })
- public @interface DataSpaceFileFormat {};
-
/**
* High Efficiency Image File Format (HEIF).
*
@@ -442,7 +427,7 @@ public final class DataSpace {
DATASPACE_DCI_P3,
DATASPACE_SRGB_LINEAR
})
- public @interface NamedDataSpace {};
+ public @interface ColorDataSpace {};
/**
* Default-assumption data space, when not explicitly specified.
@@ -635,6 +620,30 @@ public final class DataSpace {
*/
public static final int DATASPACE_SRGB_LINEAR = 138477568;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(flag = true, value = {
+ DATASPACE_DEPTH,
+ DATASPACE_DYNAMIC_DEPTH,
+ DATASPACE_HEIF,
+ DATASPACE_UNKNOWN,
+ DATASPACE_SCRGB_LINEAR,
+ DATASPACE_SRGB,
+ DATASPACE_SCRGB,
+ DATASPACE_DISPLAY_P3,
+ DATASPACE_BT2020_HLG,
+ DATASPACE_BT2020_PQ,
+ DATASPACE_ADOBE_RGB,
+ DATASPACE_JFIF,
+ DATASPACE_BT601_625,
+ DATASPACE_BT601_525,
+ DATASPACE_BT2020,
+ DATASPACE_BT709,
+ DATASPACE_DCI_P3,
+ DATASPACE_SRGB_LINEAR
+ })
+ public @interface NamedDataSpace {};
+
private DataSpace() {}
/**
@@ -647,7 +656,7 @@ public final class DataSpace {
*
* @return The int dataspace packed by standard, transfer and range value
*/
- public static @NamedDataSpace int pack(@DataSpaceStandard int standard,
+ public static @ColorDataSpace int pack(@DataSpaceStandard int standard,
@DataSpaceTransfer int transfer,
@DataSpaceRange int range) {
if ((standard & STANDARD_MASK) != standard) {
@@ -669,7 +678,7 @@ public final class DataSpace {
*
* @return The standard aspect
*/
- public static @DataSpaceStandard int getStandard(@NamedDataSpace int dataSpace) {
+ public static @DataSpaceStandard int getStandard(@ColorDataSpace int dataSpace) {
@DataSpaceStandard int standard = dataSpace & STANDARD_MASK;
return standard;
}
@@ -681,7 +690,7 @@ public final class DataSpace {
*
* @return The transfer aspect
*/
- public static @DataSpaceTransfer int getTransfer(@NamedDataSpace int dataSpace) {
+ public static @DataSpaceTransfer int getTransfer(@ColorDataSpace int dataSpace) {
@DataSpaceTransfer int transfer = dataSpace & TRANSFER_MASK;
return transfer;
}
@@ -693,7 +702,7 @@ public final class DataSpace {
*
* @return The range aspect
*/
- public static @DataSpaceRange int getRange(@NamedDataSpace int dataSpace) {
+ public static @DataSpaceRange int getRange(@ColorDataSpace int dataSpace) {
@DataSpaceRange int range = dataSpace & RANGE_MASK;
return range;
}
diff --git a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
index c59d7571ee6d..257ad7162e9e 100644
--- a/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFingerprintConstants.java
@@ -315,17 +315,17 @@ public interface BiometricFingerprintConstants {
int FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000;
/**
- * Whether the FingerprintAcquired message is a signal to turn off HBM
+ * Whether the FingerprintAcquired message is a signal to disable the UDFPS display mode.
+ * We want to disable the UDFPS mode as soon as possible to conserve power and provide better
+ * UX. For example, prolonged high-brightness illumination of optical sensors can be unpleasant
+ * to the user, can cause long term display burn-in, and can drain the battery faster.
*/
- static boolean shouldTurnOffHbm(@FingerprintAcquired int acquiredInfo) {
+ static boolean shouldDisableUdfpsDisplayMode(@FingerprintAcquired int acquiredInfo) {
switch (acquiredInfo) {
case FINGERPRINT_ACQUIRED_START:
- // Authentication just began
+ // Keep the UDFPS mode because the authentication just began.
return false;
case FINGERPRINT_ACQUIRED_GOOD:
- // Good image captured. Turn off HBM. Success/Reject comes after, which is when
- // hideUdfpsOverlay will be called.
- return true;
case FINGERPRINT_ACQUIRED_PARTIAL:
case FINGERPRINT_ACQUIRED_INSUFFICIENT:
case FINGERPRINT_ACQUIRED_IMAGER_DIRTY:
@@ -334,11 +334,12 @@ public interface BiometricFingerprintConstants {
case FINGERPRINT_ACQUIRED_IMMOBILE:
case FINGERPRINT_ACQUIRED_TOO_BRIGHT:
case FINGERPRINT_ACQUIRED_VENDOR:
- // Bad image captured. Turn off HBM. Matcher will not run, so there's no need to
- // keep HBM on.
+ // Disable the UDFPS mode because the image capture has finished. The overlay
+ // can be hidden later, once the authentication result arrives.
return true;
case FINGERPRINT_ACQUIRED_UNKNOWN:
default:
+ // Keep the UDFPS mode in case of an unknown message.
return false;
}
}
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index 9e8703779863..90e92dbe2ab0 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -159,8 +159,9 @@ public final class OutputConfiguration implements Parcelable {
*
* <li> For a SurfaceView output surface, the timestamp base is {@link
* #TIMESTAMP_BASE_CHOREOGRAPHER_SYNCED}. The timestamp is overridden with choreographer
- * pulses from the display subsystem for smoother display of camera frames. The timestamp
- * is roughly in the same time base as {@link android.os.SystemClock#uptimeMillis}.</li>
+ * pulses from the display subsystem for smoother display of camera frames when the camera
+ * device runs in fixed frame rate. The timestamp is roughly in the same time base as
+ * {@link android.os.SystemClock#uptimeMillis}.</li>
* <li> For an output surface of MediaRecorder, MediaCodec, or ImageReader with {@link
* android.hardware.HardwareBuffer#USAGE_VIDEO_ENCODE} usge flag, the timestamp base is
* {@link #TIMESTAMP_BASE_MONOTONIC}, which is roughly the same time base as
@@ -231,7 +232,8 @@ public final class OutputConfiguration implements Parcelable {
*
* <p>The timestamp of the output images are overridden with choreographer pulses from the
* display subsystem for smoother display of camera frames. An output target of SurfaceView
- * uses this time base by default.</p>
+ * uses this time base by default. Note that the timestamp override is done for fixed camera
+ * frame rate only.</p>
*
* <p>This timestamp base isn't applicable to SurfaceTexture targets. SurfaceTexture's
* {@link android.graphics.SurfaceTexture#updateTexImage updateTexImage} function always
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 30aa4db938da..bdd45e6df448 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -16,6 +16,7 @@
package android.hardware.devicestate;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -115,6 +116,52 @@ public final class DeviceStateManager {
}
/**
+ * Submits a {@link DeviceStateRequest request} to override the base state of the device. This
+ * should only be used for testing, where you want to simulate the physical change to the
+ * device state.
+ * <p>
+ * By default, the request is kept active until one of the following occurs:
+ * <ul>
+ * <li>The physical state of the device changes</li>
+ * <li>The system deems the request can no longer be honored, for example if the requested
+ * state becomes unsupported.
+ * <li>A call to {@link #cancelBaseStateOverride}.
+ * <li>Another processes submits a request succeeding this request in which case the request
+ * will be canceled.
+ * </ul>
+ *
+ * Submitting a base state override request may not cause any change in the presentation
+ * of the system if there is an emulated request made through {@link #requestState}, as the
+ * emulated override requests take priority.
+ *
+ * @throws IllegalArgumentException if the requested state is unsupported.
+ * @throws SecurityException if the caller does not hold the
+ * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
+ *
+ * @see DeviceStateRequest
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
+ @Nullable @CallbackExecutor Executor executor,
+ @Nullable DeviceStateRequest.Callback callback) {
+ mGlobal.requestBaseStateOverride(request, executor, callback);
+ }
+
+ /**
+ * Cancels the active {@link DeviceStateRequest} previously submitted with a call to
+ * {@link #requestBaseStateOverride(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ * <p>
+ * This method is noop if there is no base state request currently active.
+ *
+ * @throws SecurityException if the caller does not hold the
+ * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
+ */
+ @RequiresPermission(Manifest.permission.CONTROL_DEVICE_STATE)
+ public void cancelBaseStateOverride() {
+ mGlobal.cancelBaseStateOverride();
+ }
+
+ /**
* Registers a callback to receive notifications about changes in device state.
*
* @param executor the executor to process notifications.
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index aba538f51043..738045dafdf1 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -18,6 +18,7 @@ package android.hardware.devicestate;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.os.Binder;
@@ -81,6 +82,7 @@ public final class DeviceStateManagerGlobal {
@VisibleForTesting
public DeviceStateManagerGlobal(@NonNull IDeviceStateManager deviceStateManager) {
mDeviceStateManager = deviceStateManager;
+ registerCallbackIfNeededLocked();
}
/**
@@ -116,27 +118,22 @@ public final class DeviceStateManagerGlobal {
* DeviceStateRequest.Callback)
* @see DeviceStateRequest
*/
+ @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
+ conditional = true)
public void requestState(@NonNull DeviceStateRequest request,
@Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
- if (callback == null && executor != null) {
- throw new IllegalArgumentException("Callback must be supplied with executor.");
- } else if (executor == null && callback != null) {
- throw new IllegalArgumentException("Executor must be supplied with callback.");
- }
-
+ DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback,
+ executor);
synchronized (mLock) {
- registerCallbackIfNeededLocked();
-
if (findRequestTokenLocked(request) != null) {
// This request has already been submitted.
return;
}
-
// Add the request wrapper to the mRequests array before requesting the state as the
// callback could be triggered immediately if the mDeviceStateManager IBinder is in the
// same process as this instance.
IBinder token = new Binder();
- mRequests.put(token, new DeviceStateRequestWrapper(request, callback, executor));
+ mRequests.put(token, requestWrapper);
try {
mDeviceStateManager.requestState(token, request.getState(), request.getFlags());
@@ -153,10 +150,10 @@ public final class DeviceStateManagerGlobal {
*
* @see DeviceStateManager#cancelStateRequest
*/
+ @RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
+ conditional = true)
public void cancelStateRequest() {
synchronized (mLock) {
- registerCallbackIfNeededLocked();
-
try {
mDeviceStateManager.cancelStateRequest();
} catch (RemoteException ex) {
@@ -166,6 +163,56 @@ public final class DeviceStateManagerGlobal {
}
/**
+ * Submits a {@link DeviceStateRequest request} to modify the base state of the device.
+ *
+ * @see DeviceStateManager#requestBaseStateOverride(DeviceStateRequest, Executor,
+ * DeviceStateRequest.Callback)
+ * @see DeviceStateRequest
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
+ @Nullable Executor executor, @Nullable DeviceStateRequest.Callback callback) {
+ DeviceStateRequestWrapper requestWrapper = new DeviceStateRequestWrapper(request, callback,
+ executor);
+ synchronized (mLock) {
+ if (findRequestTokenLocked(request) != null) {
+ // This request has already been submitted.
+ return;
+ }
+ // Add the request wrapper to the mRequests array before requesting the state as the
+ // callback could be triggered immediately if the mDeviceStateManager IBinder is in the
+ // same process as this instance.
+ IBinder token = new Binder();
+ mRequests.put(token, requestWrapper);
+
+ try {
+ mDeviceStateManager.requestBaseStateOverride(token, request.getState(),
+ request.getFlags());
+ } catch (RemoteException ex) {
+ mRequests.remove(token);
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Cancels a {@link DeviceStateRequest request} previously submitted with a call to
+ * {@link #requestBaseStateOverride(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ *
+ * @see DeviceStateManager#cancelBaseStateOverride
+ */
+ @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
+ public void cancelBaseStateOverride() {
+ synchronized (mLock) {
+ try {
+ mDeviceStateManager.cancelBaseStateOverride();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
* Registers a callback to receive notifications about changes in device state.
*
* @see DeviceStateManager#registerCallback(Executor, DeviceStateCallback)
@@ -179,9 +226,6 @@ public final class DeviceStateManagerGlobal {
// This callback is already registered.
return;
}
-
- registerCallbackIfNeededLocked();
-
// Add the callback wrapper to the mCallbacks array after registering the callback as
// the callback could be triggered immediately if the mDeviceStateManager IBinder is in
// the same process as this instance.
@@ -357,6 +401,8 @@ public final class DeviceStateManagerGlobal {
DeviceStateRequestWrapper(@NonNull DeviceStateRequest request,
@Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ validateRequestWrapperParameters(callback, executor);
+
mRequest = request;
mCallback = callback;
mExecutor = executor;
@@ -377,5 +423,14 @@ public final class DeviceStateManagerGlobal {
mExecutor.execute(() -> mCallback.onRequestCanceled(mRequest));
}
+
+ private void validateRequestWrapperParameters(
+ @Nullable DeviceStateRequest.Callback callback, @Nullable Executor executor) {
+ if (callback == null && executor != null) {
+ throw new IllegalArgumentException("Callback must be supplied with executor.");
+ } else if (executor == null && callback != null) {
+ throw new IllegalArgumentException("Executor must be supplied with callback.");
+ }
+ }
}
}
diff --git a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
index e450e42497a0..7175eae58a26 100644
--- a/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
+++ b/core/java/android/hardware/devicestate/IDeviceStateManager.aidl
@@ -41,6 +41,10 @@ interface IDeviceStateManager {
* previously registered with {@link #registerCallback(IDeviceStateManagerCallback)} before a
* call to this method.
*
+ * Requesting a state does not cancel a base state override made through
+ * {@link #requestBaseStateOverride}, but will still attempt to put the device into the
+ * supplied {@code state}.
+ *
* @param token the request token provided
* @param state the state of device the request is asking for
* @param flags any flags that correspond to the request
@@ -50,14 +54,53 @@ interface IDeviceStateManager {
* @throws IllegalStateException if the supplied {@code token} has already been registered.
* @throws IllegalArgumentException if the supplied {@code state} is not supported.
*/
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true)")
void requestState(IBinder token, int state, int flags);
/**
* Cancels the active request previously submitted with a call to
- * {@link #requestState(IBinder, int, int)}.
+ * {@link #requestState(IBinder, int, int)}. Will have no effect on any base state override that
+ * was previously requested with {@link #requestBaseStateOverride}.
*
* @throws IllegalStateException if a callback has not yet been registered for the calling
* process.
*/
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true)")
void cancelStateRequest();
+
+ /**
+ * Requests that the device's base state be overridden to the supplied {@code state}. A callback
+ * <b>MUST</b> have been previously registered with
+ * {@link #registerCallback(IDeviceStateManagerCallback)} before a call to this method.
+ *
+ * This method should only be used for testing, when you want to simulate the device physically
+ * changing states. If you are looking to change device state for a feature, where the system
+ * should still be aware that the physical state is different than the emulated state, use
+ * {@link #requestState}.
+ *
+ * @param token the request token provided
+ * @param state the state of device the request is asking for
+ * @param flags any flags that correspond to the request
+ *
+ * @throws IllegalStateException if a callback has not yet been registered for the calling
+ * process.
+ * @throws IllegalStateException if the supplied {@code token} has already been registered.
+ * @throws IllegalArgumentException if the supplied {@code state} is not supported.
+ */
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)")
+ void requestBaseStateOverride(IBinder token, int state, int flags);
+
+ /**
+ * Cancels the active base state request previously submitted with a call to
+ * {@link #overrideBaseState(IBinder, int, int)}.
+ *
+ * @throws IllegalStateException if a callback has not yet been registered for the calling
+ * process.
+ */
+ @JavaPassthrough(annotation=
+ "@android.annotation.RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)")
+ void cancelBaseStateOverride();
}
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8bc11cbc61de..dfb42365357d 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -855,6 +855,16 @@ public final class DisplayManager {
return mGlobal.getUserDisabledHdrTypes();
}
+ /**
+ * Overrides HDR modes for a display device.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
+ @TestApi
+ public void overrideHdrTypes(int displayId, @NonNull int[] modes) {
+ mGlobal.overrideHdrTypes(displayId, modes);
+ }
/**
* Creates a virtual display.
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index e2f8592ad329..9294dea50b0d 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -20,9 +20,11 @@ package android.hardware.display;
import static android.hardware.display.DisplayManager.EventsMask;
import static android.view.Display.HdrCapabilities.HdrType;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.app.PropertyInvalidatedCache;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -577,6 +579,20 @@ public final class DisplayManagerGlobal {
}
}
+ /**
+ * Overrides HDR modes for a display device.
+ *
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
+ public void overrideHdrTypes(int displayId, int[] modes) {
+ try {
+ mDm.overrideHdrTypes(displayId, modes);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+
public void requestColorMode(int displayId, int colorMode) {
try {
mDm.requestColorMode(displayId, colorMode);
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 52cef0f1efd0..00bccc686919 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -31,6 +31,7 @@ import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.window.DisplayWindowPolicyController;
+import android.window.ScreenCapture;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -118,7 +119,7 @@ public abstract class DisplayManagerInternal {
* @param displayId The display id to take the screenshot of.
* @return The buffer or null if we have failed.
*/
- public abstract SurfaceControl.ScreenshotHardwareBuffer systemScreenshot(int displayId);
+ public abstract ScreenCapture.ScreenshotHardwareBuffer systemScreenshot(int displayId);
/**
* General screenshot functionality that excludes secure layers and applies appropriate
@@ -127,7 +128,7 @@ public abstract class DisplayManagerInternal {
* @param displayId The display id to take the screenshot of.
* @return The buffer or null if we have failed.
*/
- public abstract SurfaceControl.ScreenshotHardwareBuffer userScreenshot(int displayId);
+ public abstract ScreenCapture.ScreenshotHardwareBuffer userScreenshot(int displayId);
/**
* Returns information about the specified logical display.
@@ -398,6 +399,11 @@ public abstract class DisplayManagerInternal {
public abstract DisplayWindowPolicyController getDisplayWindowPolicyController(int displayId);
/**
+ * Get DisplayPrimaries from SF for a particular display.
+ */
+ public abstract SurfaceControl.DisplayPrimaries getDisplayNativePrimaries(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 ca3e58094400..b166e215075a 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -83,6 +83,9 @@ interface IDisplayManager {
// No permissions required.
int[] getUserDisabledHdrTypes();
+ // Requires ACCESS_SURFACE_FLINGER permission.
+ void overrideHdrTypes(int displayId, in int[] modes);
+
// Requires CONFIGURE_DISPLAY_COLOR_MODE
void requestColorMode(int displayId, int colorMode);
@@ -184,4 +187,9 @@ interface IDisplayManager {
// Query for DISPLAY_DECORATION support.
DisplayDecorationSupport getDisplayDecorationSupport(int displayId);
+
+ // This method is to support behavior that was calling hidden APIs. The caller was requesting
+ // to set the layerStack after the display was created, which is not something we support in
+ // DMS. This should be deleted in V release.
+ void setDisplayIdToMirror(in IBinder token, int displayId);
}
diff --git a/core/java/android/hardware/display/VirtualDisplay.java b/core/java/android/hardware/display/VirtualDisplay.java
index 71cbd20e8005..02ab8be5fd1a 100644
--- a/core/java/android/hardware/display/VirtualDisplay.java
+++ b/core/java/android/hardware/display/VirtualDisplay.java
@@ -68,6 +68,13 @@ public final class VirtualDisplay {
}
/**
+ * @hide
+ */
+ public IVirtualDisplayCallback getToken() {
+ return mToken;
+ }
+
+ /**
* Sets the surface that backs the virtual display.
* <p>
* Detaching the surface that backs a virtual display has a similar effect to
diff --git a/core/java/android/hardware/input/VirtualDpad.java b/core/java/android/hardware/input/VirtualDpad.java
new file mode 100644
index 000000000000..d7cda9ec33cf
--- /dev/null
+++ b/core/java/android/hardware/input/VirtualDpad.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.companion.virtual.IVirtualDevice;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.view.KeyEvent;
+
+import java.io.Closeable;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * A virtual dpad representing a key input mechanism on a remote device.
+ *
+ * This registers an InputDevice that is interpreted like a physically-connected device and
+ * dispatches received events to it.
+ *
+ * @hide
+ */
+@SystemApi
+public class VirtualDpad implements Closeable {
+
+ private final Set<Integer> mSupportedKeyCodes =
+ Collections.unmodifiableSet(
+ new HashSet<>(
+ Arrays.asList(
+ KeyEvent.KEYCODE_DPAD_UP,
+ KeyEvent.KEYCODE_DPAD_DOWN,
+ KeyEvent.KEYCODE_DPAD_LEFT,
+ KeyEvent.KEYCODE_DPAD_RIGHT,
+ KeyEvent.KEYCODE_DPAD_CENTER)));
+ private final IVirtualDevice mVirtualDevice;
+ private final IBinder mToken;
+
+ /** @hide */
+ public VirtualDpad(IVirtualDevice virtualDevice, IBinder token) {
+ mVirtualDevice = virtualDevice;
+ mToken = token;
+ }
+
+ @Override
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void close() {
+ try {
+ mVirtualDevice.unregisterInputDevice(mToken);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sends a key event to the system.
+ *
+ * Supported key codes are KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT,
+ * KEYCODE_DPAD_RIGHT and KEYCODE_DPAD_CENTER,
+ *
+ * @param event the event to send
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ public void sendKeyEvent(@NonNull VirtualKeyEvent event) {
+ try {
+ if (!mSupportedKeyCodes.contains(event.getKeyCode())) {
+ throw new IllegalArgumentException(
+ "Unsupported key code "
+ + event.getKeyCode()
+ + " sent to a VirtualDpad input device.");
+ }
+ mVirtualDevice.sendDpadKeyEvent(mToken, event);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+}
diff --git a/core/java/android/hardware/input/VirtualKeyEvent.java b/core/java/android/hardware/input/VirtualKeyEvent.java
index 80a49c925208..dc30e55e36fc 100644
--- a/core/java/android/hardware/input/VirtualKeyEvent.java
+++ b/core/java/android/hardware/input/VirtualKeyEvent.java
@@ -158,6 +158,7 @@ public final class VirtualKeyEvent implements Parcelable {
KeyEvent.KEYCODE_DPAD_UP,
KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_RIGHT,
+ KeyEvent.KEYCODE_DPAD_CENTER,
KeyEvent.KEYCODE_MOVE_END,
KeyEvent.KEYCODE_MOVE_HOME,
KeyEvent.KEYCODE_PAGE_DOWN,
diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java
index ee9b659e9521..901401fea32c 100644
--- a/core/java/android/hardware/input/VirtualKeyboard.java
+++ b/core/java/android/hardware/input/VirtualKeyboard.java
@@ -22,6 +22,7 @@ import android.annotation.SystemApi;
import android.companion.virtual.IVirtualDevice;
import android.os.IBinder;
import android.os.RemoteException;
+import android.view.KeyEvent;
import java.io.Closeable;
@@ -37,6 +38,7 @@ import java.io.Closeable;
@SystemApi
public class VirtualKeyboard implements Closeable {
+ private final int mUnsupportedKeyCode = KeyEvent.KEYCODE_DPAD_CENTER;
private final IVirtualDevice mVirtualDevice;
private final IBinder mToken;
@@ -64,6 +66,11 @@ public class VirtualKeyboard implements Closeable {
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
public void sendKeyEvent(@NonNull VirtualKeyEvent event) {
try {
+ if (mUnsupportedKeyCode == event.getKeyCode()) {
+ throw new IllegalArgumentException(
+ "Unsupported key code " + event.getKeyCode()
+ + " sent to a VirtualKeyboard input device.");
+ }
mVirtualDevice.sendKeyEvent(mToken, event);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index c3113799ad45..1df9b75f0b09 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -60,15 +60,29 @@ public final class Light implements Parcelable {
public static final int LIGHT_TYPE_PLAYER_ID = 10002;
/**
+ * Type for lights that illuminate keyboard keys.
+ */
+ public static final int LIGHT_TYPE_KEYBOARD_BACKLIGHT = 10003;
+
+ /**
* Capability for lights that could adjust its LED brightness. If the capability is not present
- * the led can only be turned either on or off.
+ * the LED can only be turned either on or off.
*/
public static final int LIGHT_CAPABILITY_BRIGHTNESS = 1 << 0;
/**
- * Capability for lights that has red, green and blue LEDs to control the light's color.
+ * Capability for lights that have red, green and blue LEDs to control the light's color.
+ */
+ public static final int LIGHT_CAPABILITY_COLOR_RGB = 1 << 1;
+
+ /**
+ * Capability for lights that have red, green and blue LEDs to control the light's color.
+ *
+ * @deprecated Wrong int based flag with value 0. Use capability flag {@code
+ * LIGHT_CAPABILITY_COLOR_RGB} instead.
*/
- public static final int LIGHT_CAPABILITY_RGB = 0 << 1;
+ @Deprecated
+ public static final int LIGHT_CAPABILITY_RGB = 0;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -77,6 +91,7 @@ public final class Light implements Parcelable {
LIGHT_TYPE_MICROPHONE,
LIGHT_TYPE_INPUT,
LIGHT_TYPE_PLAYER_ID,
+ LIGHT_TYPE_KEYBOARD_BACKLIGHT,
})
public @interface LightType {}
@@ -85,6 +100,7 @@ public final class Light implements Parcelable {
@IntDef(flag = true, prefix = {"LIGHT_CAPABILITY_"},
value = {
LIGHT_CAPABILITY_BRIGHTNESS,
+ LIGHT_CAPABILITY_COLOR_RGB,
LIGHT_CAPABILITY_RGB,
})
public @interface LightCapability {}
@@ -233,7 +249,7 @@ public final class Light implements Parcelable {
* @return True if the hardware can control the RGB led, otherwise false.
*/
public boolean hasRgbControl() {
- return (mCapabilities & LIGHT_CAPABILITY_RGB) == LIGHT_CAPABILITY_RGB;
+ return (mCapabilities & LIGHT_CAPABILITY_COLOR_RGB) == LIGHT_CAPABILITY_COLOR_RGB;
}
}
diff --git a/core/java/android/hardware/radio/TunerAdapter.java b/core/java/android/hardware/radio/TunerAdapter.java
index aa5480abafb4..4a18333aee9f 100644
--- a/core/java/android/hardware/radio/TunerAdapter.java
+++ b/core/java/android/hardware/radio/TunerAdapter.java
@@ -16,12 +16,13 @@
package android.hardware.radio;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -29,28 +30,36 @@ import java.util.Objects;
/**
* Implements the RadioTuner interface by forwarding calls to radio service.
*/
-class TunerAdapter extends RadioTuner {
+final class TunerAdapter extends RadioTuner {
private static final String TAG = "BroadcastRadio.TunerAdapter";
- @NonNull private final ITuner mTuner;
- @NonNull private final TunerCallbackAdapter mCallback;
- private boolean mIsClosed = false;
+ private final ITuner mTuner;
+ private final TunerCallbackAdapter mCallback;
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private boolean mIsClosed;
- private @RadioManager.Band int mBand;
+ @GuardedBy("mLock")
+ @RadioManager.Band
+ private int mBand;
+ @GuardedBy("mLock")
private ProgramList mLegacyListProxy;
+
+ @GuardedBy("mLock")
private Map<String, String> mLegacyListFilter;
- TunerAdapter(@NonNull ITuner tuner, @NonNull TunerCallbackAdapter callback,
+ TunerAdapter(ITuner tuner, TunerCallbackAdapter callback,
@RadioManager.Band int band) {
- mTuner = Objects.requireNonNull(tuner);
- mCallback = Objects.requireNonNull(callback);
+ mTuner = Objects.requireNonNull(tuner, "Tuner cannot be null");
+ mCallback = Objects.requireNonNull(callback, "Callback cannot be null");
mBand = band;
}
@Override
public void close() {
- synchronized (mTuner) {
+ synchronized (mLock) {
if (mIsClosed) {
Log.v(TAG, "Tuner is already closed");
return;
@@ -60,8 +69,8 @@ class TunerAdapter extends RadioTuner {
mLegacyListProxy.close();
mLegacyListProxy = null;
}
- mCallback.close();
}
+ mCallback.close();
try {
mTuner.close();
} catch (RemoteException e) {
@@ -71,16 +80,20 @@ class TunerAdapter extends RadioTuner {
@Override
public int setConfiguration(RadioManager.BandConfig config) {
- if (config == null) return RadioManager.STATUS_BAD_VALUE;
+ if (config == null) {
+ return RadioManager.STATUS_BAD_VALUE;
+ }
try {
mTuner.setConfiguration(config);
- mBand = config.getType();
+ synchronized (mLock) {
+ mBand = config.getType();
+ }
return RadioManager.STATUS_OK;
} catch (IllegalArgumentException e) {
Log.e(TAG, "Can't set configuration", e);
return RadioManager.STATUS_BAD_VALUE;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
}
@@ -94,7 +107,7 @@ class TunerAdapter extends RadioTuner {
config[0] = mTuner.getConfiguration();
return RadioManager.STATUS_OK;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
}
@@ -107,7 +120,7 @@ class TunerAdapter extends RadioTuner {
Log.e(TAG, "Can't set muted", e);
return RadioManager.STATUS_ERROR;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
return RadioManager.STATUS_OK;
@@ -118,7 +131,7 @@ class TunerAdapter extends RadioTuner {
try {
return mTuner.isMuted();
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return true;
}
}
@@ -126,12 +139,13 @@ class TunerAdapter extends RadioTuner {
@Override
public int step(int direction, boolean skipSubChannel) {
try {
- mTuner.step(direction == RadioTuner.DIRECTION_DOWN, skipSubChannel);
+ mTuner.step(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN,
+ skipSubChannel);
} catch (IllegalStateException e) {
Log.e(TAG, "Can't step", e);
return RadioManager.STATUS_INVALID_OPERATION;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
return RadioManager.STATUS_OK;
@@ -140,12 +154,13 @@ class TunerAdapter extends RadioTuner {
@Override
public int scan(int direction, boolean skipSubChannel) {
try {
- mTuner.scan(direction == RadioTuner.DIRECTION_DOWN, skipSubChannel);
+ mTuner.scan(/* directionDown= */ direction == RadioTuner.DIRECTION_DOWN,
+ skipSubChannel);
} catch (IllegalStateException e) {
Log.e(TAG, "Can't scan", e);
return RadioManager.STATUS_INVALID_OPERATION;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
return RadioManager.STATUS_OK;
@@ -154,7 +169,11 @@ class TunerAdapter extends RadioTuner {
@Override
public int tune(int channel, int subChannel) {
try {
- mTuner.tune(ProgramSelector.createAmFmSelector(mBand, channel, subChannel));
+ int band;
+ synchronized (mLock) {
+ band = mBand;
+ }
+ mTuner.tune(ProgramSelector.createAmFmSelector(band, channel, subChannel));
} catch (IllegalStateException e) {
Log.e(TAG, "Can't tune", e);
return RadioManager.STATUS_INVALID_OPERATION;
@@ -162,18 +181,18 @@ class TunerAdapter extends RadioTuner {
Log.e(TAG, "Can't tune", e);
return RadioManager.STATUS_BAD_VALUE;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
return RadioManager.STATUS_OK;
}
@Override
- public void tune(@NonNull ProgramSelector selector) {
+ public void tune(ProgramSelector selector) {
try {
mTuner.tune(selector);
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@@ -185,7 +204,7 @@ class TunerAdapter extends RadioTuner {
Log.e(TAG, "Can't cancel", e);
return RadioManager.STATUS_INVALID_OPERATION;
} catch (RemoteException e) {
- Log.e(TAG, "service died", e);
+ Log.e(TAG, "Service died", e);
return RadioManager.STATUS_DEAD_OBJECT;
}
return RadioManager.STATUS_OK;
@@ -196,7 +215,7 @@ class TunerAdapter extends RadioTuner {
try {
mTuner.cancelAnnouncement();
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@@ -217,11 +236,12 @@ class TunerAdapter extends RadioTuner {
}
@Override
- public @Nullable Bitmap getMetadataImage(int id) {
+ @Nullable
+ public Bitmap getMetadataImage(int id) {
try {
return mTuner.getImage(id);
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@@ -230,66 +250,72 @@ class TunerAdapter extends RadioTuner {
try {
return mTuner.startBackgroundScan();
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@Override
- public @NonNull List<RadioManager.ProgramInfo>
+ public List<RadioManager.ProgramInfo>
getProgramList(@Nullable Map<String, String> vendorFilter) {
- synchronized (mTuner) {
+ synchronized (mLock) {
if (mLegacyListProxy == null || !Objects.equals(mLegacyListFilter, vendorFilter)) {
Log.i(TAG, "Program list filter has changed, requesting new list");
mLegacyListProxy = new ProgramList();
mLegacyListFilter = vendorFilter;
-
mCallback.clearLastCompleteList();
- mCallback.setProgramListObserver(mLegacyListProxy, () -> { });
- try {
- mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter));
- } catch (RemoteException ex) {
- throw new RuntimeException("service died", ex);
- }
+ mCallback.setProgramListObserver(mLegacyListProxy, () -> {
+ Log.i(TAG, "Empty closeListener in programListObserver");
+ });
}
+ }
+ try {
+ mTuner.startProgramListUpdates(new ProgramList.Filter(vendorFilter));
+ } catch (RemoteException ex) {
+ throw new RuntimeException("Service died", ex);
+ }
- List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList();
- if (list == null) throw new IllegalStateException("Program list is not ready yet");
- return list;
+ List<RadioManager.ProgramInfo> list = mCallback.getLastCompleteList();
+ if (list == null) {
+ throw new IllegalStateException("Program list is not ready yet");
}
+ return list;
}
@Override
- public @Nullable ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
- synchronized (mTuner) {
+ @Nullable
+ public ProgramList getDynamicProgramList(@Nullable ProgramList.Filter filter) {
+ synchronized (mLock) {
if (mLegacyListProxy != null) {
mLegacyListProxy.close();
mLegacyListProxy = null;
}
mLegacyListFilter = null;
-
- ProgramList list = new ProgramList();
- mCallback.setProgramListObserver(list, () -> {
- try {
- mTuner.stopProgramListUpdates();
- } catch (IllegalStateException ex) {
- // it's fine to not stop updates if tuner is already closed
- } catch (RemoteException ex) {
- Log.e(TAG, "Couldn't stop program list updates", ex);
- }
- });
-
+ }
+ ProgramList list = new ProgramList();
+ mCallback.setProgramListObserver(list, () -> {
try {
- mTuner.startProgramListUpdates(filter);
- } catch (UnsupportedOperationException ex) {
- Log.i(TAG, "Program list is not supported with this hardware");
- return null;
+ mTuner.stopProgramListUpdates();
+ } catch (IllegalStateException ex) {
+ // it's fine to not stop updates if tuner is already closed
+ Log.e(TAG, "Tuner may already be closed", ex);
} catch (RemoteException ex) {
- mCallback.setProgramListObserver(null, () -> { });
- throw new RuntimeException("service died", ex);
+ Log.e(TAG, "Couldn't stop program list updates", ex);
}
+ });
- return list;
+ try {
+ mTuner.startProgramListUpdates(filter);
+ } catch (UnsupportedOperationException ex) {
+ Log.i(TAG, "Program list is not supported with this hardware");
+ return null;
+ } catch (RemoteException ex) {
+ mCallback.setProgramListObserver(null, () -> {
+ Log.i(TAG, "Empty closeListener in programListObserver");
+ });
+ throw new RuntimeException("Service died", ex);
}
+
+ return list;
}
@Override
@@ -315,7 +341,7 @@ class TunerAdapter extends RadioTuner {
try {
return mTuner.isConfigFlagSupported(flag);
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@@ -324,7 +350,7 @@ class TunerAdapter extends RadioTuner {
try {
return mTuner.isConfigFlagSet(flag);
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@@ -333,25 +359,26 @@ class TunerAdapter extends RadioTuner {
try {
mTuner.setConfigFlag(flag, value);
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@Override
- public @NonNull Map<String, String> setParameters(@NonNull Map<String, String> parameters) {
+ public Map<String, String> setParameters(Map<String, String> parameters) {
try {
- return mTuner.setParameters(Objects.requireNonNull(parameters));
+ return mTuner.setParameters(Objects.requireNonNull(parameters,
+ "Parameters cannot be null"));
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
@Override
- public @NonNull Map<String, String> getParameters(@NonNull List<String> keys) {
+ public Map<String, String> getParameters(List<String> keys) {
try {
- return mTuner.getParameters(Objects.requireNonNull(keys));
+ return mTuner.getParameters(Objects.requireNonNull(keys, "Keys cannot be null"));
} catch (RemoteException e) {
- throw new RuntimeException("service died", e);
+ throw new RuntimeException("Service died", e);
}
}
diff --git a/core/java/android/hardware/radio/TunerCallbackAdapter.java b/core/java/android/hardware/radio/TunerCallbackAdapter.java
index e3f7001d8740..b9782a87735d 100644
--- a/core/java/android/hardware/radio/TunerCallbackAdapter.java
+++ b/core/java/android/hardware/radio/TunerCallbackAdapter.java
@@ -16,12 +16,13 @@
package android.hardware.radio;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
+import com.android.internal.annotations.GuardedBy;
+
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -29,23 +30,31 @@ import java.util.Objects;
/**
* Implements the ITunerCallback interface by forwarding calls to RadioTuner.Callback.
*/
-class TunerCallbackAdapter extends ITunerCallback.Stub {
+final class TunerCallbackAdapter extends ITunerCallback.Stub {
private static final String TAG = "BroadcastRadio.TunerCallbackAdapter";
private final Object mLock = new Object();
- @NonNull private final RadioTuner.Callback mCallback;
- @NonNull private final Handler mHandler;
+ private final RadioTuner.Callback mCallback;
+ private final Handler mHandler;
+ @GuardedBy("mLock")
@Nullable ProgramList mProgramList;
// cache for deprecated methods
+ @GuardedBy("mLock")
boolean mIsAntennaConnected = true;
+
+ @GuardedBy("mLock")
@Nullable List<RadioManager.ProgramInfo> mLastCompleteList;
- private boolean mDelayedCompleteCallback = false;
+
+ @GuardedBy("mLock")
+ private boolean mDelayedCompleteCallback;
+
+ @GuardedBy("mLock")
@Nullable RadioManager.ProgramInfo mCurrentProgramInfo;
- TunerCallbackAdapter(@NonNull RadioTuner.Callback callback, @Nullable Handler handler) {
- mCallback = callback;
+ TunerCallbackAdapter(RadioTuner.Callback callback, @Nullable Handler handler) {
+ mCallback = Objects.requireNonNull(callback, "Callback cannot be null");
if (handler == null) {
mHandler = new Handler(Looper.getMainLooper());
} else {
@@ -55,31 +64,39 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
void close() {
synchronized (mLock) {
- if (mProgramList != null) mProgramList.close();
+ if (mProgramList != null) {
+ mProgramList.close();
+ }
}
}
void setProgramListObserver(@Nullable ProgramList programList,
- @NonNull ProgramList.OnCloseListener closeListener) {
- Objects.requireNonNull(closeListener);
+ ProgramList.OnCloseListener closeListener) {
+ Objects.requireNonNull(closeListener, "CloseListener cannot be null");
synchronized (mLock) {
if (mProgramList != null) {
Log.w(TAG, "Previous program list observer wasn't properly closed, closing it...");
mProgramList.close();
}
mProgramList = programList;
- if (programList == null) return;
+ if (programList == null) {
+ return;
+ }
programList.setOnCloseListener(() -> {
synchronized (mLock) {
- if (mProgramList != programList) return;
+ if (mProgramList != programList) {
+ return;
+ }
mProgramList = null;
mLastCompleteList = null;
- closeListener.onClose();
}
+ closeListener.onClose();
});
programList.addOnCompleteListener(() -> {
synchronized (mLock) {
- if (mProgramList != programList) return;
+ if (mProgramList != programList) {
+ return;
+ }
mLastCompleteList = programList.toList();
if (mDelayedCompleteCallback) {
Log.d(TAG, "Sending delayed onBackgroundScanComplete callback");
@@ -109,7 +126,11 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
}
boolean isAntennaConnected() {
- return mIsAntennaConnected;
+ boolean isConnected;
+ synchronized (mLock) {
+ isConnected = mIsAntennaConnected;
+ }
+ return isConnected;
}
@Override
@@ -177,7 +198,9 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
@Override
public void onAntennaState(boolean connected) {
- mIsAntennaConnected = connected;
+ synchronized (mLock) {
+ mIsAntennaConnected = connected;
+ }
mHandler.post(() -> mCallback.onAntennaState(connected));
}
@@ -186,6 +209,7 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
mHandler.post(() -> mCallback.onBackgroundScanAvailabilityChange(isAvailable));
}
+ @GuardedBy("mLock")
private void sendBackgroundScanCompleteLocked() {
mDelayedCompleteCallback = false;
mHandler.post(() -> mCallback.onBackgroundScanComplete());
@@ -213,8 +237,10 @@ class TunerCallbackAdapter extends ITunerCallback.Stub {
public void onProgramListUpdated(ProgramList.Chunk chunk) {
mHandler.post(() -> {
synchronized (mLock) {
- if (mProgramList == null) return;
- mProgramList.apply(Objects.requireNonNull(chunk));
+ if (mProgramList == null) {
+ return;
+ }
+ mProgramList.apply(Objects.requireNonNull(chunk, "Chunk cannot be null"));
}
});
}
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 05daf63fdd5d..a87b1338339a 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -147,119 +147,146 @@ class IInputMethodWrapper extends IInputMethod.Stub
@MainThread
@Override
public void executeMessage(Message msg) {
- InputMethod inputMethod = mInputMethod.get();
- // Need a valid reference to the inputMethod for everything except a dump.
- if (inputMethod == null && msg.what != DO_DUMP) {
- Log.w(TAG, "Input method reference was null, ignoring message: " + msg.what);
- return;
- }
-
+ final InputMethod inputMethod = mInputMethod.get();
+ final InputMethodServiceInternal target = mTarget.get();
switch (msg.what) {
case DO_DUMP: {
- InputMethodServiceInternal target = mTarget.get();
- if (target == null) {
- return;
- }
SomeArgs args = (SomeArgs)msg.obj;
- try {
- target.dump((FileDescriptor) args.arg1,
- (PrintWriter) args.arg2, (String[]) args.arg3);
- } catch (RuntimeException e) {
- ((PrintWriter)args.arg2).println("Exception: " + e);
- }
- synchronized (args.arg4) {
- ((CountDownLatch)args.arg4).countDown();
+ if (isValid(inputMethod, target, "DO_DUMP")) {
+ final FileDescriptor fd = (FileDescriptor) args.arg1;
+ final PrintWriter fout = (PrintWriter) args.arg2;
+ final String[] dumpArgs = (String[]) args.arg3;
+ final CountDownLatch latch = (CountDownLatch) args.arg4;
+ try {
+ target.dump(fd, fout, dumpArgs);
+ } catch (RuntimeException e) {
+ fout.println("Exception: " + e);
+ } finally {
+ latch.countDown();
+ }
}
args.recycle();
return;
}
case DO_INITIALIZE_INTERNAL:
- inputMethod.initializeInternal((IInputMethod.InitParams) msg.obj);
+ if (isValid(inputMethod, target, "DO_INITIALIZE_INTERNAL")) {
+ inputMethod.initializeInternal((IInputMethod.InitParams) msg.obj);
+ }
return;
case DO_SET_INPUT_CONTEXT: {
- inputMethod.bindInput((InputBinding)msg.obj);
+ if (isValid(inputMethod, target, "DO_SET_INPUT_CONTEXT")) {
+ inputMethod.bindInput((InputBinding) msg.obj);
+ }
return;
}
case DO_UNSET_INPUT_CONTEXT:
- inputMethod.unbindInput();
+ if (isValid(inputMethod, target, "DO_UNSET_INPUT_CONTEXT")) {
+ inputMethod.unbindInput();
+ }
return;
case DO_START_INPUT: {
final SomeArgs args = (SomeArgs) msg.obj;
- final InputConnection inputConnection = (InputConnection) args.arg1;
- final IInputMethod.StartInputParams params =
- (IInputMethod.StartInputParams) args.arg2;
- inputMethod.dispatchStartInput(inputConnection, params);
+ if (isValid(inputMethod, target, "DO_START_INPUT")) {
+ final InputConnection inputConnection = (InputConnection) args.arg1;
+ final IInputMethod.StartInputParams params =
+ (IInputMethod.StartInputParams) args.arg2;
+ inputMethod.dispatchStartInput(inputConnection, params);
+ }
args.recycle();
return;
}
case DO_ON_NAV_BUTTON_FLAGS_CHANGED:
- inputMethod.onNavButtonFlagsChanged(msg.arg1);
+ if (isValid(inputMethod, target, "DO_ON_NAV_BUTTON_FLAGS_CHANGED")) {
+ inputMethod.onNavButtonFlagsChanged(msg.arg1);
+ }
return;
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
- inputMethod.createSession(new InputMethodSessionCallbackWrapper(
- mContext, (InputChannel) args.arg1,
- (IInputMethodSessionCallback) args.arg2));
+ if (isValid(inputMethod, target, "DO_CREATE_SESSION")) {
+ inputMethod.createSession(new InputMethodSessionCallbackWrapper(
+ mContext, (InputChannel) args.arg1,
+ (IInputMethodSessionCallback) args.arg2));
+ }
args.recycle();
return;
}
case DO_SET_SESSION_ENABLED:
- inputMethod.setSessionEnabled((InputMethodSession)msg.obj,
- msg.arg1 != 0);
+ if (isValid(inputMethod, target, "DO_SET_SESSION_ENABLED")) {
+ inputMethod.setSessionEnabled((InputMethodSession) msg.obj, msg.arg1 != 0);
+ }
return;
case DO_SHOW_SOFT_INPUT: {
final SomeArgs args = (SomeArgs)msg.obj;
- inputMethod.showSoftInputWithToken(
- msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1);
+ if (isValid(inputMethod, target, "DO_SHOW_SOFT_INPUT")) {
+ inputMethod.showSoftInputWithToken(
+ msg.arg1, (ResultReceiver) args.arg2, (IBinder) args.arg1);
+ }
args.recycle();
return;
}
case DO_HIDE_SOFT_INPUT: {
final SomeArgs args = (SomeArgs) msg.obj;
- inputMethod.hideSoftInputWithToken(msg.arg1, (ResultReceiver) args.arg2,
- (IBinder) args.arg1);
+ if (isValid(inputMethod, target, "DO_HIDE_SOFT_INPUT")) {
+ inputMethod.hideSoftInputWithToken(msg.arg1, (ResultReceiver) args.arg2,
+ (IBinder) args.arg1);
+ }
args.recycle();
return;
}
case DO_CHANGE_INPUTMETHOD_SUBTYPE:
- inputMethod.changeInputMethodSubtype((InputMethodSubtype)msg.obj);
+ if (isValid(inputMethod, target, "DO_CHANGE_INPUTMETHOD_SUBTYPE")) {
+ inputMethod.changeInputMethodSubtype((InputMethodSubtype) msg.obj);
+ }
return;
case DO_CREATE_INLINE_SUGGESTIONS_REQUEST: {
final SomeArgs args = (SomeArgs) msg.obj;
- inputMethod.onCreateInlineSuggestionsRequest(
- (InlineSuggestionsRequestInfo) args.arg1,
- (IInlineSuggestionsRequestCallback) args.arg2);
+ if (isValid(inputMethod, target, "DO_CREATE_INLINE_SUGGESTIONS_REQUEST")) {
+ inputMethod.onCreateInlineSuggestionsRequest(
+ (InlineSuggestionsRequestInfo) args.arg1,
+ (IInlineSuggestionsRequestCallback) args.arg2);
+ }
args.recycle();
return;
}
case DO_CAN_START_STYLUS_HANDWRITING: {
- inputMethod.canStartStylusHandwriting(msg.arg1);
+ if (isValid(inputMethod, target, "DO_CAN_START_STYLUS_HANDWRITING")) {
+ inputMethod.canStartStylusHandwriting(msg.arg1);
+ }
return;
}
case DO_UPDATE_TOOL_TYPE: {
- inputMethod.updateEditorToolType(msg.arg1);
+ if (isValid(inputMethod, target, "DO_UPDATE_TOOL_TYPE")) {
+ inputMethod.updateEditorToolType(msg.arg1);
+ }
return;
}
case DO_START_STYLUS_HANDWRITING: {
final SomeArgs args = (SomeArgs) msg.obj;
- inputMethod.startStylusHandwriting(msg.arg1, (InputChannel) args.arg1,
- (List<MotionEvent>) args.arg2);
+ if (isValid(inputMethod, target, "DO_START_STYLUS_HANDWRITING")) {
+ inputMethod.startStylusHandwriting(msg.arg1, (InputChannel) args.arg1,
+ (List<MotionEvent>) args.arg2);
+ }
args.recycle();
return;
}
case DO_INIT_INK_WINDOW: {
- inputMethod.initInkWindow();
+ if (isValid(inputMethod, target, "DO_INIT_INK_WINDOW")) {
+ inputMethod.initInkWindow();
+ }
return;
}
case DO_FINISH_STYLUS_HANDWRITING: {
- inputMethod.finishStylusHandwriting();
+ if (isValid(inputMethod, target, "DO_FINISH_STYLUS_HANDWRITING")) {
+ inputMethod.finishStylusHandwriting();
+ }
return;
}
case DO_REMOVE_STYLUS_HANDWRITING_WINDOW: {
- inputMethod.removeStylusHandwritingWindow();
+ if (isValid(inputMethod, target, "DO_REMOVE_STYLUS_HANDWRITING_WINDOW")) {
+ inputMethod.removeStylusHandwritingWindow();
+ }
return;
}
-
}
Log.w(TAG, "Unhandled message code: " + msg.what);
}
@@ -445,4 +472,15 @@ class IInputMethodWrapper extends IInputMethod.Stub
public void removeStylusHandwritingWindow() {
mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_REMOVE_STYLUS_HANDWRITING_WINDOW));
}
+
+ private static boolean isValid(InputMethod inputMethod, InputMethodServiceInternal target,
+ String msg) {
+ if (inputMethod != null && target != null && !target.isServiceDestroyed()) {
+ return true;
+ } else {
+ Log.w(TAG, "Ignoring " + msg + ", InputMethod:" + inputMethod
+ + ", InputMethodServiceInternal:" + target);
+ return false;
+ }
+ }
}
diff --git a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
index 5ce38e9a279a..891da2466bff 100644
--- a/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
+++ b/core/java/android/inputmethodservice/IRemoteInputConnectionInvoker.java
@@ -27,6 +27,7 @@ import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.DeleteGesture;
+import android.view.inputmethod.DeleteRangeGesture;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.HandwritingGesture;
@@ -36,6 +37,7 @@ import android.view.inputmethod.InsertGesture;
import android.view.inputmethod.JoinOrSplitGesture;
import android.view.inputmethod.RemoveSpaceGesture;
import android.view.inputmethod.SelectGesture;
+import android.view.inputmethod.SelectRangeGesture;
import android.view.inputmethod.SurroundingText;
import android.view.inputmethod.TextAttribute;
@@ -636,7 +638,9 @@ final class IRemoteInputConnectionInvoker {
/**
* Invokes one of {@link IRemoteInputConnection#performHandwritingSelectGesture},
+ * {@link IRemoteInputConnection#performHandwritingSelectRangeGesture},
* {@link IRemoteInputConnection#performHandwritingDeleteGesture},
+ * {@link IRemoteInputConnection#performHandwritingDeleteRangeGesture},
* {@link IRemoteInputConnection#performHandwritingInsertGesture},
* {@link IRemoteInputConnection#performHandwritingRemoveSpaceGesture},
* {@link IRemoteInputConnection#performHandwritingJoinOrSplitGesture}.
@@ -655,12 +659,18 @@ final class IRemoteInputConnectionInvoker {
if (gesture instanceof SelectGesture) {
mConnection.performHandwritingSelectGesture(
createHeader(), (SelectGesture) gesture, resultReceiver);
+ } else if (gesture instanceof SelectRangeGesture) {
+ mConnection.performHandwritingSelectRangeGesture(
+ createHeader(), (SelectRangeGesture) gesture, resultReceiver);
} else if (gesture instanceof InsertGesture) {
mConnection.performHandwritingInsertGesture(
createHeader(), (InsertGesture) gesture, resultReceiver);
} else if (gesture instanceof DeleteGesture) {
mConnection.performHandwritingDeleteGesture(
createHeader(), (DeleteGesture) gesture, resultReceiver);
+ } else if (gesture instanceof DeleteRangeGesture) {
+ mConnection.performHandwritingDeleteRangeGesture(
+ createHeader(), (DeleteRangeGesture) gesture, resultReceiver);
} else if (gesture instanceof RemoveSpaceGesture) {
mConnection.performHandwritingRemoveSpaceGesture(
createHeader(), (RemoveSpaceGesture) gesture, resultReceiver);
@@ -765,4 +775,33 @@ final class IRemoteInputConnectionInvoker {
return false;
}
}
+
+ /**
+ * Invokes {@link IRemoteInputConnection#replaceText(InputConnectionCommandHeader, int, int,
+ * CharSequence, TextAttribute)}.
+ *
+ * @param start the character index where the replacement should start.
+ * @param end the character index where the replacement should end.
+ * @param newCursorPosition the new cursor position around the text. If > 0, this is relative to
+ * the end of the text - 1; if <= 0, this is relative to the start of the text. So a value
+ * of 1 will always advance you to the position after the full text being inserted. Note
+ * that this means you can't position the cursor within the text.
+ * @param text the text to replace. This may include styles.
+ * @param textAttribute The extra information about the text. This value may be null.
+ */
+ @AnyThread
+ public boolean replaceText(
+ int start,
+ int end,
+ @NonNull CharSequence text,
+ int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ try {
+ mConnection.replaceText(
+ createHeader(), start, end, text, newCursorPosition, textAttribute);
+ return true;
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 3a157d30d4c4..92088e9f0a33 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -65,6 +65,7 @@ import android.annotation.TestApi;
import android.annotation.UiContext;
import android.app.ActivityManager;
import android.app.Dialog;
+import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
@@ -546,6 +547,20 @@ public class InputMethodService extends AbstractInputMethodService {
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.S)
public static final long FINISH_INPUT_NO_FALLBACK_CONNECTION = 156215187L; // This is a bug id.
+ /**
+ * Disallow IMEs to override {@link InputMethodService#onCreateInputMethodSessionInterface()}
+ * method.
+ *
+ * <p>If IMEs targeting on Android U and beyond override the
+ * {@link InputMethodService#onCreateInputMethodSessionInterface()}, an {@link LinkageError}
+ * would be thrown.</p>
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ private static final long DISALLOW_INPUT_METHOD_INTERFACE_OVERRIDE = 148086656L;
+
LayoutInflater mInflater;
TypedArray mThemeAttrs;
@UnsupportedAppUsage
@@ -700,11 +715,6 @@ public class InputMethodService extends AbstractInputMethodService {
@MainThread
@Override
public final void initializeInternal(@NonNull IInputMethod.InitParams params) {
- if (mDestroyed) {
- Log.i(TAG, "The InputMethodService has already onDestroyed()."
- + "Ignore the initialization.");
- return;
- }
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal");
mConfigTracker.onInitialize(params.configChanges);
mPrivOps.set(params.privilegedOperations);
@@ -1532,6 +1542,11 @@ public class InputMethodService extends AbstractInputMethodService {
}
@Override public void onCreate() {
+ if (methodIsOverridden("onCreateInputMethodSessionInterface")
+ && CompatChanges.isChangeEnabled(DISALLOW_INPUT_METHOD_INTERFACE_OVERRIDE)) {
+ throw new LinkageError("InputMethodService#onCreateInputMethodSessionInterface()"
+ + " can no longer be overridden!");
+ }
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onCreate");
mTheme = Resources.selectSystemTheme(mTheme,
getApplicationInfo().targetSdkVersion,
@@ -1744,7 +1759,16 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* Implement to return our standard {@link InputMethodImpl}. Subclasses
* can override to provide their own customized version.
+ *
+ * @deprecated IME developers don't need to override this method to get callbacks information.
+ * Most methods in {@link InputMethodImpl} have corresponding callbacks.
+ * Use {@link InputMethodService#onBindInput()}, {@link InputMethodService#onUnbindInput()},
+ * {@link InputMethodService#onWindowShown()}, {@link InputMethodService#onWindowHidden()}, etc.
+ *
+ * <p>Starting from Android U and later, override this method won't guarantee that IME works
+ * as previous platform behavior.</p>
*/
+ @Deprecated
@Override
public AbstractInputMethodImpl onCreateInputMethodInterface() {
return new InputMethodImpl();
@@ -1753,7 +1777,18 @@ public class InputMethodService extends AbstractInputMethodService {
/**
* Implement to return our standard {@link InputMethodSessionImpl}. Subclasses
* can override to provide their own customized version.
+ *
+ * @deprecated IME developers don't need to override this method to get callbacks information.
+ * Most methods in {@link InputMethodSessionImpl} have corresponding callbacks.
+ * Use {@link InputMethodService#onFinishInput()},
+ * {@link InputMethodService#onDisplayCompletions(CompletionInfo[])},
+ * {@link InputMethodService#onUpdateExtractedText(int, ExtractedText)},
+ * {@link InputMethodService#onUpdateSelection(int, int, int, int, int, int)} instead.
+ *
+ * <p>IMEs targeting on Android U and above cannot override this method, or an
+ * {@link LinkageError} would be thrown.</p>
*/
+ @Deprecated
@Override
public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
return new InputMethodSessionImpl();
@@ -3921,6 +3956,14 @@ public class InputMethodService extends AbstractInputMethodService {
public void triggerServiceDump(String where, @Nullable byte[] icProto) {
ImeTracing.getInstance().triggerServiceDump(where, mDumper, icProto);
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isServiceDestroyed() {
+ return mDestroyed;
+ }
};
}
@@ -4047,4 +4090,13 @@ public class InputMethodService extends AbstractInputMethodService {
final KeyEvent upEvent = createBackKeyEvent(KeyEvent.ACTION_UP, hasStartedTracking);
onKeyUp(KeyEvent.KEYCODE_BACK, upEvent);
}
+
+ private boolean methodIsOverridden(String methodName, Class<?>... parameterTypes) {
+ try {
+ return getClass().getMethod(methodName, parameterTypes).getDeclaringClass()
+ != InputMethodService.class;
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Method must exist.", e);
+ }
+ }
}
diff --git a/core/java/android/inputmethodservice/InputMethodServiceInternal.java b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
index f44f49d7dcaf..c6612f6e54c5 100644
--- a/core/java/android/inputmethodservice/InputMethodServiceInternal.java
+++ b/core/java/android/inputmethodservice/InputMethodServiceInternal.java
@@ -85,4 +85,11 @@ interface InputMethodServiceInternal {
*/
default void triggerServiceDump(@NonNull String where, @Nullable byte[] icProto) {
}
+
+ /**
+ * @return {@code true} if {@link InputMethodService} is destroyed.
+ */
+ default boolean isServiceDestroyed() {
+ return false;
+ };
}
diff --git a/core/java/android/inputmethodservice/RemoteInputConnection.java b/core/java/android/inputmethodservice/RemoteInputConnection.java
index 694293c62bd7..2b5f14d530e8 100644
--- a/core/java/android/inputmethodservice/RemoteInputConnection.java
+++ b/core/java/android/inputmethodservice/RemoteInputConnection.java
@@ -498,6 +498,17 @@ final class RemoteInputConnection implements InputConnection {
return mInvoker.setImeConsumesInput(imeConsumesInput);
}
+ /** See {@link InputConnection#replaceText(int, int, CharSequence, int, TextAttribute)}. */
+ @AnyThread
+ public boolean replaceText(
+ int start,
+ int end,
+ @NonNull CharSequence text,
+ int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ return mInvoker.replaceText(start, end, text, newCursorPosition, textAttribute);
+ }
+
@AnyThread
@Override
public String toString() {
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 3979c6c78dcb..10ce3bf78b2a 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -483,9 +483,6 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
final VpnProfile profile = new VpnProfile("" /* Key; value unused by IKEv2VpnProfile(s) */,
mIsRestrictedToTestNetworks, mExcludeLocalRoutes, mRequiresInternetValidation,
mIkeTunConnParams);
-
- profile.server = getServerAddr();
- profile.ipsecIdentifier = getUserIdentity();
profile.proxy = mProxyInfo;
profile.isBypassable = mIsBypassable;
profile.isMetered = mIsMetered;
@@ -499,6 +496,8 @@ public final class Ikev2VpnProfile extends PlatformVpnProfile {
}
profile.type = mType;
+ profile.server = getServerAddr();
+ profile.ipsecIdentifier = getUserIdentity();
profile.setAllowedAlgorithms(mAllowedAlgorithms);
switch (mType) {
case TYPE_IKEV2_IPSEC_USER_PASS:
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 4f26ad2b4f52..4df013949de5 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -22,7 +22,6 @@ import android.annotation.SystemApi;
import android.app.AppOpsManager;
import android.compat.annotation.UnsupportedAppUsage;
import android.util.ExceptionUtils;
-import android.util.IntArray;
import android.util.Log;
import android.util.Slog;
@@ -46,6 +45,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Modifier;
+import java.util.concurrent.atomic.AtomicReferenceArray;
/**
* Base class for a remotable object, the core part of a lightweight
@@ -144,9 +144,6 @@ public class Binder implements IBinder {
*/
private static volatile boolean sStackTrackingEnabled = false;
- private static final Object sTracingUidsWriteLock = new Object();
- private static volatile IntArray sTracingUidsImmutable = new IntArray();
-
/**
* Enable Binder IPC stack tracking. If enabled, every binder transaction will be logged to
* {@link TransactionTracker}.
@@ -167,17 +164,6 @@ public class Binder implements IBinder {
}
/**
- * @hide
- */
- public static void enableTracingForUid(int uid) {
- synchronized (sTracingUidsWriteLock) {
- final IntArray copy = sTracingUidsImmutable.clone();
- copy.add(uid);
- sTracingUidsImmutable = copy;
- }
- }
-
- /**
* Check if binder transaction stack tracking is enabled.
*
* @hide
@@ -187,13 +173,6 @@ public class Binder implements IBinder {
}
/**
- * @hide
- */
- public static boolean isTracingEnabled(int callingUid) {
- return sTracingUidsImmutable.indexOf(callingUid) != -1;
- }
-
- /**
* Get the binder transaction tracker for this process.
*
* @hide
@@ -313,7 +292,7 @@ public class Binder implements IBinder {
private IInterface mOwner;
@Nullable
private String mDescriptor;
- private volatile String[] mTransactionTraceNames = null;
+ private volatile AtomicReferenceArray<String> mTransactionTraceNames = null;
private volatile String mSimpleDescriptor = null;
private static final int TRANSACTION_TRACE_NAME_ID_LIMIT = 1024;
@@ -917,28 +896,32 @@ public class Binder implements IBinder {
@VisibleForTesting
public final @NonNull String getTransactionTraceName(int transactionCode) {
if (mTransactionTraceNames == null) {
- final String descriptor = getSimpleDescriptor();
final int highestId = Math.min(getMaxTransactionId(), TRANSACTION_TRACE_NAME_ID_LIMIT);
- final String[] transactionNames = new String[highestId + 1];
- final StringBuffer buf = new StringBuffer();
- for (int i = 0; i <= highestId; i++) {
- String transactionName = getTransactionName(i + FIRST_CALL_TRANSACTION);
- if (transactionName != null) {
- buf.append(descriptor).append(':').append(transactionName);
- } else {
- buf.append(descriptor).append('#').append(i + FIRST_CALL_TRANSACTION);
- }
- transactionNames[i] = buf.toString();
- buf.setLength(0);
- }
- mSimpleDescriptor = descriptor;
- mTransactionTraceNames = transactionNames;
+ mSimpleDescriptor = getSimpleDescriptor();
+ mTransactionTraceNames = new AtomicReferenceArray(highestId + 1);
}
+
final int index = transactionCode - FIRST_CALL_TRANSACTION;
- if (index < 0 || index >= mTransactionTraceNames.length) {
+ if (index < 0 || index >= mTransactionTraceNames.length()) {
return mSimpleDescriptor + "#" + transactionCode;
}
- return mTransactionTraceNames[index];
+
+ String transactionTraceName = mTransactionTraceNames.getAcquire(index);
+ if (transactionTraceName == null) {
+ final String transactionName = getTransactionName(transactionCode);
+ final StringBuffer buf = new StringBuffer();
+
+ if (transactionName != null) {
+ buf.append(mSimpleDescriptor).append(":").append(transactionName);
+ } else {
+ buf.append(mSimpleDescriptor).append("#").append(transactionCode);
+ }
+
+ transactionTraceName = buf.toString();
+ mTransactionTraceNames.setRelease(index, transactionTraceName);
+ }
+
+ return transactionTraceName;
}
private @NonNull String getSimpleDescriptor() {
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index edfcb3d6f12a..d5c3de146015 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -54,6 +54,7 @@ import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
import android.text.TextUtils;
+import android.util.DataUnit;
import android.util.Log;
import android.util.Slog;
import android.webkit.MimeTypeMap;
@@ -83,6 +84,7 @@ import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -1309,6 +1311,85 @@ public final class FileUtils {
return val * pow;
}
+ private static long toBytes(long value, String unit) {
+ unit = unit.toUpperCase();
+
+ if (List.of("B").contains(unit)) {
+ return value;
+ }
+
+ if (List.of("K", "KB").contains(unit)) {
+ return DataUnit.KILOBYTES.toBytes(value);
+ }
+
+ if (List.of("M", "MB").contains(unit)) {
+ return DataUnit.MEGABYTES.toBytes(value);
+ }
+
+ if (List.of("G", "GB").contains(unit)) {
+ return DataUnit.GIGABYTES.toBytes(value);
+ }
+
+ if (List.of("KI", "KIB").contains(unit)) {
+ return DataUnit.KIBIBYTES.toBytes(value);
+ }
+
+ if (List.of("MI", "MIB").contains(unit)) {
+ return DataUnit.MEBIBYTES.toBytes(value);
+ }
+
+ if (List.of("GI", "GIB").contains(unit)) {
+ return DataUnit.GIBIBYTES.toBytes(value);
+ }
+
+ return Long.MIN_VALUE;
+ }
+
+ /**
+ * @param fmtSize The string that contains the size to be parsed. The
+ * expected format is:
+ *
+ * <p>"^((\\s*[-+]?[0-9]+)\\s*(B|K|KB|M|MB|G|GB|Ki|KiB|Mi|MiB|Gi|GiB)\\s*)$"
+ *
+ * <p>For example: 10Kb, 500GiB, 100mb. The unit is not case sensitive.
+ *
+ * @return the size in bytes. If {@code fmtSize} has invalid format, it
+ * returns {@link Long#MIN_VALUE}.
+ * @hide
+ */
+ public static long parseSize(@Nullable String fmtSize) {
+ if (fmtSize == null || fmtSize.isBlank()) {
+ return Long.MIN_VALUE;
+ }
+
+ int sign = 1;
+ fmtSize = fmtSize.trim();
+ char first = fmtSize.charAt(0);
+ if (first == '-' || first == '+') {
+ if (first == '-') {
+ sign = -1;
+ }
+
+ fmtSize = fmtSize.replace(first + "", "");
+ }
+
+ int index = 0;
+ // Find the last index of the value in fmtSize.
+ while (index < fmtSize.length() && Character.isDigit(fmtSize.charAt(index))) {
+ index++;
+ }
+
+ // Check if number and units are present.
+ if (index == 0 || index == fmtSize.length()) {
+ return Long.MIN_VALUE;
+ }
+
+ long value = sign * Long.valueOf(fmtSize.substring(0, index));
+ String unit = fmtSize.substring(index).trim();
+
+ return toBytes(value, unit);
+ }
+
/**
* Closes the given object quietly, ignoring any checked exceptions. Does
* nothing if the given object is {@code null}.
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 88649cbf9e42..933769a352e9 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -76,7 +76,7 @@ interface IUserManager {
String getUserAccount(int userId);
void setUserAccount(int userId, String accountName);
long getUserCreationTime(int userId);
- boolean isUserSwitcherEnabled(int mUserId);
+ boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable, int mUserId);
boolean isRestricted(int userId);
boolean canHaveRestrictedProfile(int userId);
int getUserSerialNumber(int userId);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 9189c6c8b6c7..a863a87cc2f7 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3666,9 +3666,6 @@ public final class Parcel {
* previously been written via {@link #writeTypedList} with the same object
* type.
*
- * @return A newly created ArrayList containing objects with the same data
- * as those that were previously written.
- *
* @see #writeTypedList
*/
public final <T> void readTypedList(@NonNull List<T> list, @NonNull Parcelable.Creator<T> c) {
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index f4edcb1317f4..acfd15cb005e 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -21,6 +21,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.util.ArrayMap;
+import android.util.Slog;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -50,6 +51,8 @@ import java.util.ArrayList;
*/
public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
XmlUtils.WriteMapCallback {
+ private static final String TAG = "PersistableBundle";
+
private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
/** An unmodifiable {@code PersistableBundle} that is always {@link #isEmpty() empty}. */
@@ -118,7 +121,11 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
* @hide
*/
public PersistableBundle(Bundle b) {
- this(b.getItemwiseMap());
+ this(b, true);
+ }
+
+ private PersistableBundle(Bundle b, boolean throwException) {
+ this(b.getItemwiseMap(), throwException);
}
/**
@@ -127,7 +134,7 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
* @param map a Map containing only those items that can be persisted.
* @throws IllegalArgumentException if any element of #map cannot be persisted.
*/
- private PersistableBundle(ArrayMap<String, Object> map) {
+ private PersistableBundle(ArrayMap<String, Object> map, boolean throwException) {
super();
mFlags = FLAG_DEFUSABLE;
@@ -136,16 +143,23 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
// Now verify each item throwing an exception if there is a violation.
final int N = mMap.size();
- for (int i=0; i<N; i++) {
+ for (int i = N - 1; i >= 0; --i) {
Object value = mMap.valueAt(i);
if (value instanceof ArrayMap) {
// Fix up any Maps by replacing them with PersistableBundles.
- mMap.setValueAt(i, new PersistableBundle((ArrayMap<String, Object>) value));
+ mMap.setValueAt(i,
+ new PersistableBundle((ArrayMap<String, Object>) value, throwException));
} else if (value instanceof Bundle) {
- mMap.setValueAt(i, new PersistableBundle(((Bundle) value)));
+ mMap.setValueAt(i, new PersistableBundle((Bundle) value, throwException));
} else if (!isValidType(value)) {
- throw new IllegalArgumentException("Bad value in PersistableBundle key="
- + mMap.keyAt(i) + " value=" + value);
+ final String errorMsg = "Bad value in PersistableBundle key="
+ + mMap.keyAt(i) + " value=" + value;
+ if (throwException) {
+ throw new IllegalArgumentException(errorMsg);
+ } else {
+ Slog.wtfStack(TAG, errorMsg);
+ mMap.removeAt(i);
+ }
}
}
}
@@ -268,6 +282,15 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
/** @hide */
public void saveToXml(TypedXmlSerializer out) throws IOException, XmlPullParserException {
unparcel();
+ // Explicitly drop invalid types an attacker may have added before persisting.
+ for (int i = mMap.size() - 1; i >= 0; --i) {
+ final Object value = mMap.valueAt(i);
+ if (!isValidType(value)) {
+ Slog.e(TAG, "Dropping bad data before persisting: "
+ + mMap.keyAt(i) + "=" + value);
+ mMap.removeAt(i);
+ }
+ }
XmlUtils.writeMapXml(mMap, out, this);
}
@@ -322,9 +345,12 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa
while (((event = in.next()) != XmlPullParser.END_DOCUMENT) &&
(event != XmlPullParser.END_TAG || in.getDepth() < outerDepth)) {
if (event == XmlPullParser.START_TAG) {
+ // Don't throw an exception when restoring from XML since an attacker could try to
+ // input invalid data in the persisted file.
return new PersistableBundle((ArrayMap<String, Object>)
XmlUtils.readThisArrayMapXml(in, startTag, tagName,
- new MyReadMapCallback()));
+ new MyReadMapCallback()),
+ /* throwException */ false);
}
}
return new PersistableBundle(); // An empty mutable PersistableBundle
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 14082f3388a0..c943a3d1861c 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -26,6 +26,7 @@ import android.annotation.TestApi;
import android.annotation.UptimeMillisLong;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build.VERSION_CODES;
+import android.sysprop.MemoryProperties;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
@@ -1330,6 +1331,24 @@ public class Process {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public static final native void sendSignalQuiet(int pid, int signal);
+ /**
+ * @return The advertised memory of the system, as the end user would encounter in a retail
+ * display environment. If the advertised memory is not defined, it returns
+ * {@code getTotalMemory()} rounded.
+ *
+ * @hide
+ */
+ public static final long getAdvertisedMem() {
+ String formatSize = MemoryProperties.memory_ddr_size().orElse("0KB");
+ long memSize = FileUtils.parseSize(formatSize);
+
+ if (memSize == Long.MIN_VALUE) {
+ return FileUtils.roundStorageSize(getTotalMemory());
+ }
+
+ return memSize;
+ }
+
/** @hide */
@UnsupportedAppUsage
public static final native long getFreeMemory();
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index 113a64048796..a6b62b0b7930 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -441,7 +441,7 @@ public final class StrictMode {
* different penalties for different detected actions.
*/
public static final class ThreadPolicy {
- /** The default, lax policy which doesn't catch anything. */
+ /** The lax policy which doesn't catch anything. */
public static final ThreadPolicy LAX = new ThreadPolicy(0, null, null);
@UnsupportedAppUsage
@@ -728,7 +728,7 @@ public final class StrictMode {
* <p>The policy is enabled by {@link #setVmPolicy}.
*/
public static final class VmPolicy {
- /** The default, lax policy which doesn't catch anything. */
+ /** The lax policy which doesn't catch anything. */
public static final VmPolicy LAX = new VmPolicy(0, EMPTY_CLASS_LIMIT_MAP, null, null);
@UnsupportedAppUsage
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index ecea054db216..2b75a23505d6 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -377,7 +377,7 @@ public final class SystemClock {
}
long currentNanos = elapsedRealtimeNanos();
long deltaMs = (currentNanos - time.getElapsedRealtimeNanos()) / 1000000L;
- return time.getTime() + deltaMs;
+ return time.getUnixEpochTimeMillis() + deltaMs;
}
};
}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index f6aaee8e3de0..607d1e101193 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2843,7 +2843,6 @@ public class UserManager {
/**
* @hide
*/
- @TestApi
public static boolean isUsersOnSecondaryDisplaysEnabled() {
return SystemProperties.getBoolean("fw.users_on_secondary_displays",
Resources.getSystem()
@@ -2853,10 +2852,12 @@ public class UserManager {
/**
* Returns whether the device allows users to run (and launch activities) on secondary displays.
*
- * @return {@code false} for most devices, except automotive vehicles with passenger displays.
+ * @return {@code false} for most devices, except on automotive builds for vehiches with
+ * passenger displays.
*
* @hide
*/
+ @TestApi
public boolean isUsersOnSecondaryDisplaysSupported() {
return isUsersOnSecondaryDisplaysEnabled();
}
@@ -2868,10 +2869,10 @@ public class UserManager {
* It includes:
*
* <ol>
- * <li>The current foreground user in the main display.
- * <li>Current background users in secondary displays (for example, passenger users on
- * automotive, using the display associated with their seats).
- * <li>Profile users (in the running / started state) of other visible users.
+ * <li>The current foreground user.
+ * <li>(Running) profiles of the current foreground user.
+ * <li>Background users assigned to secondary displays (for example, passenger users on
+ * automotive builds, using the display associated with their seats).
* </ol>
*
* @return whether the user is visible at the moment, as defined above.
@@ -5264,36 +5265,10 @@ public class UserManager {
public boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable) {
try {
- if (!mService.isUserSwitcherEnabled(mUserId)) {
- return false;
- }
+ return mService.isUserSwitcherEnabled(showEvenIfNotActionable, mUserId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
-
- // The feature is enabled. But is it worth showing?
- return showEvenIfNotActionable
- || areThereUsersToWhichToSwitch() // There are switchable users.
- || !hasUserRestrictionForUser(DISALLOW_ADD_USER, mUserId); // New users can be added
- }
-
- /** Returns whether there are any users (other than the current user) to which to switch. */
- @RequiresPermission(anyOf = {
- android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.CREATE_USERS
- })
- private boolean areThereUsersToWhichToSwitch() {
- final List<UserInfo> users = getAliveUsers();
- if (users == null) {
- return false;
- }
- int switchableUserCount = 0;
- for (UserInfo user : users) {
- if (user.supportsSwitchToByUser()) {
- ++switchableUserCount;
- }
- }
- return switchableUserCount > 1;
}
/**
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 08de87ebe2e6..c1606e89645e 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -2549,7 +2549,7 @@ public class StorageManager {
* called on first creation of a new file on external storage, and whenever the
* media type of the file is updated later.
*
- * This API doesn't require any special permissions, though typical implementations
+ * This API requires MANAGE_EXTERNAL_STORAGE permission and typical implementations
* will require being called from an SELinux domain that allows setting file attributes
* related to quota (eg the GID or project ID).
*
@@ -2568,11 +2568,16 @@ public class StorageManager {
* @hide
*/
@SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
public void updateExternalStorageFileQuotaType(@NonNull File path,
@QuotaType int quotaType) throws IOException {
long projectId;
final String filePath = path.getCanonicalPath();
- final StorageVolume volume = getStorageVolume(path);
+ // MANAGE_EXTERNAL_STORAGE permission is required as FLAG_INCLUDE_SHARED_PROFILE is being
+ // set while querying getVolumeList.
+ final StorageVolume[] availableVolumes = getVolumeList(mContext.getUserId(),
+ FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE | FLAG_INCLUDE_SHARED_PROFILE);
+ final StorageVolume volume = getStorageVolume(availableVolumes, path);
if (volume == null) {
Log.w(TAG, "Failed to update quota type for " + filePath);
return;
diff --git a/core/java/android/preference/GenericInflater.java b/core/java/android/preference/GenericInflater.java
index 7edc987ee0c1..fe53d4eeff64 100644
--- a/core/java/android/preference/GenericInflater.java
+++ b/core/java/android/preference/GenericInflater.java
@@ -406,7 +406,7 @@ abstract class GenericInflater<T, P extends GenericInflater.Parent> {
InflateException ie = new InflateException(attrs
.getPositionDescription()
+ ": Error inflating class "
- + constructor.getClass().getName());
+ + constructor.getDeclaringClass().getName());
ie.initCause(e);
throw ie;
}
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 0a6a405fbce6..2c0be870836a 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -541,7 +541,8 @@ public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callba
}
public void postUpdateSlider(int volume, int lastAudibleVolume, boolean mute) {
- obtainMessage(UPDATE_SLIDER, volume, lastAudibleVolume, new Boolean(mute)).sendToTarget();
+ obtainMessage(UPDATE_SLIDER, volume, lastAudibleVolume, Boolean.valueOf(mute))
+ .sendToTarget();
}
}
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index d125cbb80a27..345486e390cd 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -203,6 +203,15 @@ public final class DeviceConfig {
@SystemApi
public static final String NAMESPACE_TETHERING = "tethering";
+
+ /**
+ * Namespace for Nearby module.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_NEARBY = "nearby";
+
/**
* Namespace for content capture feature used by on-device machine intelligence
* to provide suggestions in a privacy-safe manner.
@@ -776,6 +785,14 @@ public final class DeviceConfig {
public static final String NAMESPACE_WEAR = "wear";
/**
+ * Namespace for features relating to MBA transparency metadata.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_TRANSPARENCY_METADATA = "transparency_metadata";
+
+ /**
* Namespace for the input method manager platform features.
*
* @hide
@@ -783,6 +800,14 @@ public final class DeviceConfig {
@TestApi
public static final String NAMESPACE_INPUT_METHOD_MANAGER = "input_method_manager";
+ /**
+ * Namespace for backup and restore service related features.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String NAMESPACE_BACKUP_AND_RESTORE = "backup_and_restore";
+
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a99d0f0a5656..cab6acbfe2b7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7462,6 +7462,13 @@ public final class Settings {
"trust_agents_initialized";
/**
+ * Set to 1 by the system after the list of known trust agents have been initialized.
+ * @hide
+ */
+ public static final String KNOWN_TRUST_AGENTS_INITIALIZED =
+ "known_trust_agents_initialized";
+
+ /**
* The Logging ID (a unique 64-bit value) as a hex string.
* Used as a pseudonymous identifier for logging.
* @deprecated This identifier is poorly initialized and has
@@ -9825,6 +9832,7 @@ public final class Settings {
* Whether or not virtual sensors are enabled.
* @hide
*/
+ @TestApi
@Readable
public static final String BIOMETRIC_VIRTUAL_ENABLED = "biometric_virtual_enabled";
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 154fcab8827d..194e1b553322 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4316,13 +4316,25 @@ public final class Telephony {
* subscription and while is in voice call.
*
* Default value is empty string.
- *
+ * @deprecated This column is no longer supported. Use
+ * {@link #COLUMN_ENABLED_MOBILE_DATA_POLICIES} instead.
* @hide
*/
+ @Deprecated
public static final String COLUMN_DATA_ENABLED_OVERRIDE_RULES =
"data_enabled_override_rules";
/**
+ * TelephonyProvider column name enabled_mobile_data_policies.
+ * A list of mobile data policies, each of which represented by an integer and joint by ",".
+ *
+ * Default value is empty string.
+ * @hide
+ */
+ public static final String COLUMN_ENABLED_MOBILE_DATA_POLICIES =
+ "enabled_mobile_data_policies";
+
+ /**
* TelephonyProvider column name for user displayed name.
* <P>Type: TEXT (String)</P>
*
@@ -4820,5 +4832,12 @@ public final class Telephony {
* @hide
*/
public static final String COLUMN_USAGE_SETTING = "usage_setting";
+
+ /**
+ * TelephonyProvider column name for user handle associated with this sim.
+ *
+ * @hide
+ */
+ public static final String COLUMN_USER_HANDLE = "user_handle";
}
}
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index f90055829b89..8efc5eb6b6ff 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -68,6 +68,8 @@ public final class KeymasterDefs {
public static final int KM_TAG_RSA_PUBLIC_EXPONENT = Tag.RSA_PUBLIC_EXPONENT; // KM_ULONG | 200;
public static final int KM_TAG_INCLUDE_UNIQUE_ID = Tag.INCLUDE_UNIQUE_ID; // KM_BOOL | 202;
+ public static final int KM_TAG_RSA_OAEP_MGF_DIGEST = Tag.RSA_OAEP_MGF_DIGEST;
+ // KM_ENUM_REP | 203;
public static final int KM_TAG_ACTIVE_DATETIME = Tag.ACTIVE_DATETIME; // KM_DATE | 400;
public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME =
diff --git a/core/java/android/security/keymaster/OWNERS b/core/java/android/security/keymaster/OWNERS
index 65129a46d113..c4d605c952ad 100644
--- a/core/java/android/security/keymaster/OWNERS
+++ b/core/java/android/security/keymaster/OWNERS
@@ -1,5 +1,5 @@
# Bug component: 189335
swillden@google.com
-jdanis@google.com
+eranm@google.com
jbires@google.com
diff --git a/core/java/android/service/cloudsearch/CloudSearchService.java b/core/java/android/service/cloudsearch/CloudSearchService.java
index 5efa1acf8ffa..0ce968939e02 100644
--- a/core/java/android/service/cloudsearch/CloudSearchService.java
+++ b/core/java/android/service/cloudsearch/CloudSearchService.java
@@ -15,25 +15,14 @@
*/
package android.service.cloudsearch;
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.app.Service;
-import android.app.cloudsearch.ICloudSearchManager;
import android.app.cloudsearch.SearchRequest;
import android.app.cloudsearch.SearchResponse;
-import android.content.Context;
import android.content.Intent;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.service.cloudsearch.ICloudSearchService.Stub;
-import android.util.Log;
-import android.util.Slog;
/**
* A service for returning search results from cloud services in response to an on device query.
@@ -72,39 +61,18 @@ public abstract class CloudSearchService extends Service {
*/
public static final String SERVICE_INTERFACE =
"android.service.cloudsearch.CloudSearchService";
- private static final boolean DEBUG = false;
- private static final String TAG = "CloudSearchService";
- private Handler mHandler;
- private ICloudSearchManager mService;
-
- private final android.service.cloudsearch.ICloudSearchService mInterface = new Stub() {
- @Override
- public void onSearch(SearchRequest request) {
- mHandler.sendMessage(
- obtainMessage(CloudSearchService::onSearch,
- CloudSearchService.this, request));
- }
- };
@CallSuper
@Override
public void onCreate() {
super.onCreate();
- if (DEBUG) {
- Log.d(TAG, "onCreate CloudSearchService");
- }
- mHandler = new Handler(Looper.getMainLooper(), null, true);
-
- IBinder b = ServiceManager.getService(Context.CLOUDSEARCH_SERVICE);
- mService = android.app.cloudsearch.ICloudSearchManager.Stub.asInterface(b);
}
/**
* onSearch receives the input request, retrievals the search provider's own
* corpus and returns the search response through returnResults below.
*
- *@param request the search request passed from the client.
- *
+ * @param request the search request passed from the client.
*/
public abstract void onSearch(@NonNull SearchRequest request);
@@ -112,30 +80,16 @@ public abstract class CloudSearchService extends Service {
* returnResults returnes the response and its associated requestId, where
* requestIs is generated by request through getRequestId().
*
- *@param requestId the request ID got from request.getRequestId().
- *@param response the search response returned from the search provider.
- *
+ * @param requestId the request ID got from request.getRequestId().
+ * @param response the search response returned from the search provider.
*/
public final void returnResults(@NonNull String requestId,
- @NonNull SearchResponse response) {
- try {
- mService.returnResults(mInterface.asBinder(), requestId, response);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ @NonNull SearchResponse response) {
}
@Override
@NonNull
public final IBinder onBind(@NonNull Intent intent) {
- if (DEBUG) {
- Log.d(TAG, "onBind CloudSearchService");
- }
- if (SERVICE_INTERFACE.equals(intent.getAction())) {
- return mInterface.asBinder();
- }
- Slog.w(TAG, "Tried to bind to wrong intent (should be "
- + SERVICE_INTERFACE + ": " + intent);
return null;
}
}
diff --git a/core/java/android/service/credentials/Action.java b/core/java/android/service/credentials/Action.java
new file mode 100644
index 000000000000..186b2a60c430
--- /dev/null
+++ b/core/java/android/service/credentials/Action.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * An action defined by the provider that intents into the provider's app for specific
+ * user actions.
+ *
+ * @hide
+ */
+public final class Action implements Parcelable {
+ /** Info to be displayed with this action on the UI. */
+ private final @NonNull Slice mInfo;
+ /**
+ * The pending intent to be invoked when the user selects this action.
+ */
+ private final @NonNull PendingIntent mPendingIntent;
+
+ /**
+ * Constructs an action to be displayed on the UI.
+ *
+ * @param actionInfo The info to be displayed along with this action.
+ * @param pendingIntent The intent to be invoked when the user selects this action.
+ * @throws NullPointerException If {@code actionInfo}, or {@code pendingIntent} is null.
+ */
+ public Action(@NonNull Slice actionInfo, @NonNull PendingIntent pendingIntent) {
+ Objects.requireNonNull(actionInfo, "actionInfo must not be null");
+ Objects.requireNonNull(pendingIntent, "pendingIntent must not be null");
+ mInfo = actionInfo;
+ mPendingIntent = pendingIntent;
+ }
+
+ private Action(@NonNull Parcel in) {
+ mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+ PendingIntent.class);
+ }
+
+ public static final @NonNull Creator<Action> CREATOR = new Creator<Action>() {
+ @Override
+ public Action createFromParcel(@NonNull Parcel in) {
+ return new Action(in);
+ }
+
+ @Override
+ public Action[] newArray(int size) {
+ return new Action[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mInfo.writeToParcel(dest, flags);
+ mPendingIntent.writeToParcel(dest, flags);
+ }
+
+ /**
+ * Returns the action info as a {@link Slice} object, to be displayed on the UI.
+ */
+ public @NonNull Slice getActionInfo() {
+ return mInfo;
+ }
+
+ /**
+ * Returns the {@link PendingIntent} to be invoked when the action is selected.
+ */
+ public @NonNull PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+}
diff --git a/core/java/android/service/credentials/CreateCredentialCallback.java b/core/java/android/service/credentials/CreateCredentialCallback.java
new file mode 100644
index 000000000000..6108eea5bea1
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialCallback.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Callback to be invoked as a response to {@link CreateCredentialRequest}.
+ *
+ * @hide
+ */
+public final class CreateCredentialCallback {
+ private static final String TAG = "CreateCredentialCallback";
+
+ private final ICreateCredentialCallback mCallback;
+
+ /** @hide */
+ public CreateCredentialCallback(@NonNull ICreateCredentialCallback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Invoked on a successful response for {@link CreateCredentialRequest}
+ * @param response The response from the credential provider.
+ */
+ public void onSuccess(@NonNull CreateCredentialResponse response) {
+ try {
+ mCallback.onSuccess(response);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Invoked on a failure response for {@link CreateCredentialRequest}
+ * @param errorCode The code defining the type of error.
+ * @param message The message corresponding to the failure.
+ */
+ public void onFailure(int errorCode, @Nullable CharSequence message) {
+ Log.w(TAG, "onFailure: " + message);
+ try {
+ mCallback.onFailure(errorCode, message);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/CreateCredentialRequest.aidl b/core/java/android/service/credentials/CreateCredentialRequest.aidl
new file mode 100644
index 000000000000..eb7fba9405f7
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialRequest.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable CreateCredentialRequest; \ No newline at end of file
diff --git a/core/java/android/service/credentials/CreateCredentialRequest.java b/core/java/android/service/credentials/CreateCredentialRequest.java
new file mode 100644
index 000000000000..ac11e04bcb77
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialRequest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * Request for creating a credential.
+ *
+ * @hide
+ */
+public final class CreateCredentialRequest implements Parcelable {
+ private final @NonNull String mCallingPackage;
+ private final @NonNull String mType;
+ private final @NonNull Bundle mData;
+
+ /**
+ * Constructs a new instance.
+ *
+ * @throws IllegalArgumentException If {@code callingPackage}, or {@code type} string is
+ * null or empty.
+ * @throws NullPointerException If {@code data} is null.
+ */
+ public CreateCredentialRequest(@NonNull String callingPackage,
+ @NonNull String type, @NonNull Bundle data) {
+ mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage,
+ "callingPackage must not be null or empty");
+ mType = Preconditions.checkStringNotEmpty(type,
+ "type must not be null or empty");
+ mData = Objects.requireNonNull(data, "data must not be null");
+ }
+
+ private CreateCredentialRequest(@NonNull Parcel in) {
+ mCallingPackage = in.readString8();
+ mType = in.readString8();
+ mData = in.readBundle();
+ }
+
+ public static final @NonNull Creator<CreateCredentialRequest> CREATOR =
+ new Creator<CreateCredentialRequest>() {
+ @Override
+ public CreateCredentialRequest createFromParcel(@NonNull Parcel in) {
+ return new CreateCredentialRequest(in);
+ }
+
+ @Override
+ public CreateCredentialRequest[] newArray(int size) {
+ return new CreateCredentialRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mCallingPackage);
+ dest.writeString8(mType);
+ dest.writeBundle(mData);
+ }
+
+ /** Returns the calling package of the calling app. */
+ @NonNull
+ public String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ /** Returns the type of the credential to be created. */
+ @NonNull
+ public String getType() {
+ return mType;
+ }
+
+ /** Returns the data to be used while creating the credential. */
+ @NonNull
+ public Bundle getData() {
+ return mData;
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt b/core/java/android/service/credentials/CreateCredentialResponse.aidl
index f9b08000290f..73c9147b0ae4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/CommonAssertions.kt
+++ b/core/java/android/service/credentials/CreateCredentialResponse.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open 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,6 @@
* limitations under the License.
*/
-@file:JvmName("CommonAssertions")
-package com.android.wm.shell.flicker.pip
+package android.service.credentials;
-internal const val PIP_WINDOW_COMPONENT = "PipMenuActivity"
+parcelable CreateCredentialResponse;
diff --git a/core/java/android/service/credentials/CreateCredentialResponse.java b/core/java/android/service/credentials/CreateCredentialResponse.java
new file mode 100644
index 000000000000..f2ad7272f207
--- /dev/null
+++ b/core/java/android/service/credentials/CreateCredentialResponse.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Response to a {@link CreateCredentialRequest}.
+ *
+ * @hide
+ */
+public final class CreateCredentialResponse implements Parcelable {
+ private final @Nullable CharSequence mHeader;
+ private final @NonNull List<SaveEntry> mSaveEntries;
+
+ private CreateCredentialResponse(@NonNull Parcel in) {
+ mHeader = in.readCharSequence();
+ mSaveEntries = in.createTypedArrayList(SaveEntry.CREATOR);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeCharSequence(mHeader);
+ dest.writeTypedList(mSaveEntries);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<CreateCredentialResponse> CREATOR =
+ new Creator<CreateCredentialResponse>() {
+ @Override
+ public CreateCredentialResponse createFromParcel(@NonNull Parcel in) {
+ return new CreateCredentialResponse(in);
+ }
+
+ @Override
+ public CreateCredentialResponse[] newArray(int size) {
+ return new CreateCredentialResponse[size];
+ }
+ };
+
+ /* package-private */ CreateCredentialResponse(
+ @Nullable CharSequence header,
+ @NonNull List<SaveEntry> saveEntries) {
+ this.mHeader = header;
+ this.mSaveEntries = saveEntries;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mSaveEntries);
+ }
+
+ /** Returns the header to be displayed on the UI. */
+ public @Nullable CharSequence getHeader() {
+ return mHeader;
+ }
+
+ /** Returns the list of save entries to be displayed on the UI. */
+ public @NonNull List<SaveEntry> getSaveEntries() {
+ return mSaveEntries;
+ }
+
+ /**
+ * A builder for {@link CreateCredentialResponse}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+
+ private @Nullable CharSequence mHeader;
+ private @NonNull List<SaveEntry> mSaveEntries = new ArrayList<>();
+
+ /** Sets the header to be displayed on the UI. */
+ public @NonNull Builder setHeader(@Nullable CharSequence header) {
+ mHeader = header;
+ return this;
+ }
+
+ /**
+ * Sets the list of save entries to be shown on the UI.
+ *
+ * @throws IllegalArgumentException If {@code saveEntries} is empty.
+ * @throws NullPointerException If {@code saveEntries} is null, or any of its elements
+ * are null.
+ */
+ public @NonNull Builder setSaveEntries(@NonNull List<SaveEntry> saveEntries) {
+ Preconditions.checkCollectionNotEmpty(saveEntries, "saveEntries");
+ mSaveEntries = Preconditions.checkCollectionElementsNotNull(
+ saveEntries, "saveEntries");
+ return this;
+ }
+
+ /**
+ * Adds an entry to the list of save entries to be shown on the UI.
+ *
+ * @throws NullPointerException If {@code saveEntry} is null.
+ */
+ public @NonNull Builder addSaveEntry(@NonNull SaveEntry saveEntry) {
+ mSaveEntries.add(Objects.requireNonNull(saveEntry));
+ return this;
+ }
+
+ /**
+ * Builds the instance.
+ *
+ * @throws IllegalArgumentException If {@code saveEntries} is empty.
+ */
+ public @NonNull CreateCredentialResponse build() {
+ Preconditions.checkCollectionNotEmpty(mSaveEntries, "saveEntries must "
+ + "not be empty");
+ return new CreateCredentialResponse(
+ mHeader,
+ mSaveEntries);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/Credential.java b/core/java/android/service/credentials/Credential.java
new file mode 100644
index 000000000000..7d5da8a7c4e0
--- /dev/null
+++ b/core/java/android/service/credentials/Credential.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import static java.util.Objects.requireNonNull;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A Credential object that contains type specific data that is returned from the credential
+ * provider to the framework. Framework then converts it to an app facing representation and
+ * returns to the calling app.
+ *
+ * @hide
+ */
+public final class Credential implements Parcelable {
+ /** The type of this credential. */
+ private final @NonNull String mType;
+
+ /** The data associated with this credential. */
+ private final @NonNull Bundle mData;
+
+ /**
+ * Constructs a credential object.
+ *
+ * @param type The type of the credential.
+ * @param data The data of the credential that is passed back to the framework, and eventually
+ * to the calling app.
+ * @throws NullPointerException If {@code data} is null.
+ * @throws IllegalArgumentException If {@code type} is null or empty.
+ */
+ public Credential(@NonNull String type, @NonNull Bundle data) {
+ Preconditions.checkStringNotEmpty(type, "type must not be null, or empty");
+ requireNonNull(data, "data must not be null");
+ this.mType = type;
+ this.mData = data;
+ }
+
+ private Credential(@NonNull Parcel in) {
+ mType = in.readString16NoHelper();
+ mData = in.readBundle();
+ }
+
+ /**
+ * Returns the type of the credential.
+ */
+ public @NonNull String getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the data associated with the credential.
+ */
+ public @NonNull Bundle getData() {
+ return mData;
+ }
+
+ public static final @NonNull Creator<Credential> CREATOR = new Creator<Credential>() {
+ @Override
+ public Credential createFromParcel(@NonNull Parcel in) {
+ return new Credential(in);
+ }
+
+ @Override
+ public Credential[] newArray(int size) {
+ return new Credential[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mType);
+ dest.writeBundle(mData);
+ }
+}
diff --git a/core/java/android/service/credentials/CredentialEntry.java b/core/java/android/service/credentials/CredentialEntry.java
new file mode 100644
index 000000000000..b49215a08bfb
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialEntry.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * A credential entry that is displayed on the account selector UI. Each entry corresponds to
+ * something that the user can select.
+ *
+ * @hide
+ */
+public final class CredentialEntry implements Parcelable {
+ /** The type of the credential entry to be shown on the UI. */
+ private final @NonNull String mType;
+
+ /** The info to be displayed along with this credential entry on the UI. */
+ private final @NonNull Slice mInfo;
+
+ /** The pending intent to be invoked when this credential entry is selected. */
+ private final @Nullable PendingIntent mPendingIntent;
+
+ /**
+ * The underlying credential to be returned to the app when the user selects
+ * this credential entry.
+ */
+ private final @Nullable Credential mCredential;
+
+ /** A flag denoting whether auto-select is enabled for this entry. */
+ private final @NonNull boolean mAutoSelectAllowed;
+
+ private CredentialEntry(@NonNull String type, @NonNull Slice entryInfo,
+ @Nullable PendingIntent pendingIntent, @Nullable Credential credential,
+ @NonNull boolean autoSeletAllowed) {
+ mType = type;
+ mInfo = entryInfo;
+ mPendingIntent = pendingIntent;
+ mCredential = credential;
+ mAutoSelectAllowed = autoSeletAllowed;
+ }
+
+ private CredentialEntry(@NonNull Parcel in) {
+ mType = in.readString();
+ mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+ PendingIntent.class);
+ mCredential = in.readParcelable(Credential.class.getClassLoader(),
+ Credential.class);
+ mAutoSelectAllowed = in.readBoolean();
+ }
+
+ public static final @NonNull Creator<CredentialEntry> CREATOR =
+ new Creator<CredentialEntry>() {
+ @Override
+ public CredentialEntry createFromParcel(@NonNull Parcel in) {
+ return new CredentialEntry(in);
+ }
+
+ @Override
+ public CredentialEntry[] newArray(int size) {
+ return new CredentialEntry[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mType);
+ mInfo.writeToParcel(dest, flags);
+ mPendingIntent.writeToParcel(dest, flags);
+ mCredential.writeToParcel(dest, flags);
+ dest.writeBoolean(mAutoSelectAllowed);
+ }
+
+ /**
+ * Returns the specific credential type of the entry.
+ */
+ public @NonNull String getType() {
+ return mType;
+ }
+
+ /**
+ * Returns the UI info to be displayed for this entry.
+ */
+ public @NonNull Slice getInfo() {
+ return mInfo;
+ }
+
+ /**
+ * Returns the pending intent to be invoked if the user selects this entry.
+ */
+ public @Nullable PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
+ /**
+ * Returns the credential associated with this entry.
+ */
+ public @Nullable Credential getCredential() {
+ return mCredential;
+ }
+
+ /**
+ * Returns whether this entry can be auto selected if it is the only option for the user.
+ */
+ public @NonNull boolean isAutoSelectAllowed() {
+ return mAutoSelectAllowed;
+ }
+
+ /**
+ * Builder for {@link CredentialEntry}.
+ */
+ public static final class Builder {
+ private String mType;
+ private Slice mInfo;
+ private PendingIntent mPendingIntent;
+ private Credential mCredential;
+ private boolean mAutoSelectAllowed = false;
+
+ /**
+ * Builds the instance.
+ * @param type The type of credential underlying this credential entry.
+ * @param info The info to be displayed with this entry on the UI.
+ *
+ * @throws IllegalArgumentException If {@code type} is null or empty.
+ * @throws NullPointerException If {@code info} is null.
+ */
+ public Builder(@NonNull String type, @NonNull Slice info) {
+ mType = Preconditions.checkStringNotEmpty(type, "type must not be "
+ + "null, or empty");
+ mInfo = Objects.requireNonNull(info, "info must not be null");
+ }
+
+ /**
+ * Sets the pendingIntent to be invoked if the user selects this entry.
+ *
+ * @throws IllegalStateException If {@code credential} is already set. Must either set the
+ * {@code credential}, or the {@code pendingIntent}.
+ */
+ public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
+ Preconditions.checkState(pendingIntent != null && mCredential != null,
+ "credential is already set. Cannot set both the pendingIntent "
+ + "and the credential");
+ mPendingIntent = pendingIntent;
+ return this;
+ }
+
+ /**
+ * Sets the credential to be used, if the user selects this entry.
+ *
+ * @throws IllegalStateException If {@code pendingIntent} is already set. Must either set
+ * the {@code pendingIntent}, or the {@code credential}.
+ */
+ public @NonNull Builder setCredential(@Nullable Credential credential) {
+ Preconditions.checkState(credential != null && mPendingIntent != null,
+ "pendingIntent is already set. Cannot set both the "
+ + "pendingIntent and the credential");
+ mCredential = credential;
+ return this;
+ }
+
+ /**
+ * Sets whether the entry is allowed to be auto selected by the framework.
+ * The default value is set to false.
+ */
+ public @NonNull Builder setAutoSelectAllowed(@NonNull boolean autoSelectAllowed) {
+ mAutoSelectAllowed = autoSelectAllowed;
+ return this;
+ }
+
+ /**
+ * Creates a new {@link CredentialEntry} instance.
+ *
+ * @throws NullPointerException If {@code info} is null.
+ * @throws IllegalArgumentException If {@code type} is null, or empty.
+ * @throws IllegalStateException If neither {@code pendingIntent} nor {@code credential}
+ * is set, or if both are set.
+ */
+ public @NonNull CredentialEntry build() {
+ Preconditions.checkState(mPendingIntent == null && mCredential == null,
+ "Either pendingIntent or credential must be set");
+ Preconditions.checkState(mPendingIntent != null && mCredential != null,
+ "Cannot set both the pendingIntent and credential");
+ return new CredentialEntry(mType, mInfo, mPendingIntent,
+ mCredential, mAutoSelectAllowed);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
new file mode 100644
index 000000000000..1fe89dffaa0f
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.app.Service;
+import android.content.Intent;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.Objects;
+
+/**
+ * Main service to be extended by credential providers, in order to return user credentials
+ * to the framework.
+ *
+ * @hide
+ */
+public abstract class CredentialProviderService extends Service {
+ private static final String TAG = "CredProviderService";
+ private Handler mHandler;
+
+ public static final String SERVICE_INTERFACE =
+ "android.service.credentials.CredentialProviderService";
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ }
+
+ @Override
+ public final @NonNull IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Log.i(TAG, "Failed to bind with intent: " + intent);
+ return null;
+ }
+
+ private final ICredentialProviderService mInterface = new ICredentialProviderService.Stub() {
+ @Override
+ public void onGetCredentials(GetCredentialsRequest request, ICancellationSignal transport,
+ IGetCredentialsCallback callback) throws RemoteException {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(transport);
+ Objects.requireNonNull(callback);
+
+ mHandler.sendMessage(obtainMessage(
+ CredentialProviderService::onGetCredentials,
+ CredentialProviderService.this, request,
+ CancellationSignal.fromTransport(transport),
+ new GetCredentialsCallback(callback)
+ ));
+ }
+
+ @Override
+ public void onCreateCredential(CreateCredentialRequest request,
+ ICancellationSignal transport, ICreateCredentialCallback callback)
+ throws RemoteException {
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(transport);
+ Objects.requireNonNull(callback);
+
+ mHandler.sendMessage(obtainMessage(
+ CredentialProviderService::onCreateCredential,
+ CredentialProviderService.this, request,
+ CancellationSignal.fromTransport(transport),
+ new CreateCredentialCallback(callback)
+ ));
+ }
+ };
+
+ /**
+ * Called by the android system to retrieve user credentials from the connected provider
+ * service.
+ * @param request The credential request for the provider to handle.
+ * @param cancellationSignal Signal for providers to listen to any cancellation requests from
+ * the android system.
+ * @param callback Object used to relay the response of the credentials request.
+ */
+ public abstract void onGetCredentials(@NonNull GetCredentialsRequest request,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull GetCredentialsCallback callback);
+
+ /**
+ * Called by the android system to create a credential.
+ * @param request The credential creation request for the provider to handle.
+ * @param cancellationSignal Signal for providers to listen to any cancellation requests from
+ * the android system.
+ * @param callback Object used to relay the response of the credential creation request.
+ */
+ public abstract void onCreateCredential(@NonNull CreateCredentialRequest request,
+ @NonNull CancellationSignal cancellationSignal,
+ @NonNull CreateCredentialCallback callback);
+}
diff --git a/core/java/android/service/credentials/CredentialsDisplayContent.java b/core/java/android/service/credentials/CredentialsDisplayContent.java
new file mode 100644
index 000000000000..106f322c4ab4
--- /dev/null
+++ b/core/java/android/service/credentials/CredentialsDisplayContent.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Content to be displayed on the account selector UI, including credential entries,
+ * actions etc.
+ *
+ * @hide
+ */
+public final class CredentialsDisplayContent implements Parcelable {
+ /** Header to be displayed on the UI. */
+ private final @Nullable CharSequence mHeader;
+
+ /** List of credential entries to be displayed on the UI. */
+ private final @NonNull List<CredentialEntry> mCredentialEntries;
+
+ /** List of provider actions to be displayed on the UI. */
+ private final @NonNull List<Action> mActions;
+
+ private CredentialsDisplayContent(@Nullable CharSequence header,
+ @NonNull List<CredentialEntry> credentialEntries,
+ @NonNull List<Action> actions) {
+ mHeader = header;
+ mCredentialEntries = credentialEntries;
+ mActions = actions;
+ }
+
+ private CredentialsDisplayContent(@NonNull Parcel in) {
+ mHeader = in.readCharSequence();
+ mCredentialEntries = in.createTypedArrayList(CredentialEntry.CREATOR);
+ mActions = in.createTypedArrayList(Action.CREATOR);
+ }
+
+ public static final @NonNull Creator<CredentialsDisplayContent> CREATOR =
+ new Creator<CredentialsDisplayContent>() {
+ @Override
+ public CredentialsDisplayContent createFromParcel(@NonNull Parcel in) {
+ return new CredentialsDisplayContent(in);
+ }
+
+ @Override
+ public CredentialsDisplayContent[] newArray(int size) {
+ return new CredentialsDisplayContent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeCharSequence(mHeader);
+ dest.writeTypedList(mCredentialEntries);
+ dest.writeTypedList(mActions);
+ }
+
+ /**
+ * Returns the header to be displayed on the UI.
+ */
+ public @Nullable CharSequence getHeader() {
+ return mHeader;
+ }
+
+ /**
+ * Returns the list of credential entries to be displayed on the UI.
+ */
+ public @NonNull List<CredentialEntry> getCredentialEntries() {
+ return mCredentialEntries;
+ }
+
+ /**
+ * Returns the list of actions to be displayed on the UI.
+ */
+ public @NonNull List<Action> getActions() {
+ return mActions;
+ }
+
+ /**
+ * Builds an instance of {@link CredentialsDisplayContent}.
+ */
+ public static final class Builder {
+ private CharSequence mHeader = null;
+ private List<CredentialEntry> mCredentialEntries = new ArrayList<>();
+ private List<Action> mActions = new ArrayList<>();
+
+ /**
+ * Sets the header to be displayed on the UI.
+ */
+ public @NonNull Builder setHeader(@Nullable CharSequence header) {
+ mHeader = header;
+ return this;
+ }
+
+ /**
+ * Adds a {@link CredentialEntry} to the list of entries to be displayed on
+ * the UI.
+ *
+ * @throws NullPointerException If the {@code credentialEntry} is null.
+ */
+ public @NonNull Builder addCredentialEntry(@NonNull CredentialEntry credentialEntry) {
+ mCredentialEntries.add(Objects.requireNonNull(credentialEntry));
+ return this;
+ }
+
+ /**
+ * Adds an {@link Action} to the list of actions to be displayed on
+ * the UI.
+ *
+ * @throws NullPointerException If {@code action} is null.
+ */
+ public @NonNull Builder addAction(@NonNull Action action) {
+ mActions.add(Objects.requireNonNull(action, "action must not be null"));
+ return this;
+ }
+
+ /**
+ * Sets the list of actions to be displayed on the UI.
+ *
+ * @throws NullPointerException If {@code actions} is null, or any of its elements
+ * is null.
+ */
+ public @NonNull Builder setActions(@NonNull List<Action> actions) {
+ mActions = Preconditions.checkCollectionElementsNotNull(actions,
+ "actions");
+ return this;
+ }
+
+ /**
+ * Sets the list of credential entries to be displayed on the
+ * account selector UI.
+ *
+ * @throws NullPointerException If {@code credentialEntries} is null, or any of its
+ * elements is null.
+ */
+ public @NonNull Builder setCredentialEntries(
+ @NonNull List<CredentialEntry> credentialEntries) {
+ mCredentialEntries = Preconditions.checkCollectionElementsNotNull(
+ credentialEntries,
+ "credentialEntries");
+ return this;
+ }
+
+ /**
+ * Builds a {@link GetCredentialsResponse} instance.
+ *
+ * @throws NullPointerException If {@code credentialEntries} is null.
+ * @throws IllegalStateException if both {@code credentialEntries} and
+ * {@code actions} are empty.
+ */
+ public @NonNull CredentialsDisplayContent build() {
+ if (mCredentialEntries != null && mCredentialEntries.isEmpty()
+ && mActions != null && mActions.isEmpty()) {
+ throw new IllegalStateException("credentialEntries and actions must not both "
+ + "be empty");
+ }
+ return new CredentialsDisplayContent(mHeader, mCredentialEntries, mActions);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/GetCredentialOption.java b/core/java/android/service/credentials/GetCredentialOption.java
new file mode 100644
index 000000000000..c6cda1d1abf6
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialOption.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A type specific credential request, containing the associated data to be used for
+ * retrieving credentials.
+ *
+ * @hide
+ */
+public final class GetCredentialOption implements Parcelable {
+ /** The type of credential requested. */
+ private final @NonNull String mType;
+
+ /** The data associated with the request. */
+ private final @NonNull Bundle mData;
+
+ /**
+ * Constructs a new instance of {@link GetCredentialOption}
+ *
+ * @throws IllegalArgumentException If {@code type} string is null or empty.
+ * @throws NullPointerException If {@code data} is null.
+ */
+ public GetCredentialOption(@NonNull String type, @NonNull Bundle data) {
+ Preconditions.checkStringNotEmpty(type, "type must not be null, or empty");
+ requireNonNull(data, "data must not be null");
+ mType = type;
+ mData = data;
+ }
+
+ /**
+ * Returns the data associated with this credential request option.
+ */
+ public @NonNull Bundle getData() {
+ return mData;
+ }
+
+ /**
+ * Returns the type associated with this credential request option.
+ */
+ public @NonNull String getType() {
+ return mType;
+ }
+
+ private GetCredentialOption(@NonNull Parcel in) {
+ mType = in.readString16NoHelper();
+ mData = in.readBundle();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString16NoHelper(mType);
+ dest.writeBundle(mData);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Creator<GetCredentialOption> CREATOR =
+ new Creator<GetCredentialOption>() {
+ @Override
+ public GetCredentialOption createFromParcel(@NonNull Parcel in) {
+ return new GetCredentialOption(in);
+ }
+
+ @Override
+ public GetCredentialOption[] newArray(int size) {
+ return new GetCredentialOption[size];
+ }
+ };
+}
diff --git a/core/java/android/service/credentials/GetCredentialsCallback.java b/core/java/android/service/credentials/GetCredentialsCallback.java
new file mode 100644
index 000000000000..42a73946b5cc
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsCallback.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Callback to be invoked as a response to {@link GetCredentialsRequest}.
+ *
+ * @hide
+ */
+public final class GetCredentialsCallback {
+
+ private static final String TAG = "GetCredentialsCallback";
+
+ private final IGetCredentialsCallback mCallback;
+
+ /** @hide */
+ public GetCredentialsCallback(@NonNull IGetCredentialsCallback callback) {
+ mCallback = callback;
+ }
+
+ /**
+ * Invoked on a successful response for {@link GetCredentialsRequest}
+ * @param response The response from the credential provider.
+ */
+ public void onSuccess(@NonNull GetCredentialsResponse response) {
+ try {
+ mCallback.onSuccess(response);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+
+ /**
+ * Invoked on a failure response for {@link GetCredentialsRequest}
+ * @param errorCode The code defining the kind of error.
+ * @param message The message corresponding to the failure.
+ */
+ public void onFailure(int errorCode, @Nullable CharSequence message) {
+ Log.w(TAG, "onFailure: " + message);
+ try {
+ mCallback.onFailure(errorCode, message);
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/GetCredentialsRequest.aidl b/core/java/android/service/credentials/GetCredentialsRequest.aidl
new file mode 100644
index 000000000000..b309d698e7de
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsRequest.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable GetCredentialsRequest; \ No newline at end of file
diff --git a/core/java/android/service/credentials/GetCredentialsRequest.java b/core/java/android/service/credentials/GetCredentialsRequest.java
new file mode 100644
index 000000000000..cf7c2834f75f
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsRequest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Request for getting user's credentials from a given credential provider.
+ *
+ * @hide
+ */
+public final class GetCredentialsRequest implements Parcelable {
+ /** Calling package of the app requesting for credentials. */
+ private final @NonNull String mCallingPackage;
+
+ /**
+ * List of credential options. Each {@link GetCredentialOption} object holds parameters to
+ * be used for retrieving specific type of credentials.
+ */
+ private final @NonNull List<GetCredentialOption> mGetCredentialOptions;
+
+ private GetCredentialsRequest(@NonNull String callingPackage,
+ @NonNull List<GetCredentialOption> getCredentialOptions) {
+ this.mCallingPackage = callingPackage;
+ this.mGetCredentialOptions = getCredentialOptions;
+ }
+
+ private GetCredentialsRequest(@NonNull Parcel in) {
+ mCallingPackage = in.readString16NoHelper();
+ mGetCredentialOptions = in.createTypedArrayList(GetCredentialOption.CREATOR);
+ }
+
+ public static final @NonNull Creator<GetCredentialsRequest> CREATOR =
+ new Creator<GetCredentialsRequest>() {
+ @Override
+ public GetCredentialsRequest createFromParcel(Parcel in) {
+ return new GetCredentialsRequest(in);
+ }
+
+ @Override
+ public GetCredentialsRequest[] newArray(int size) {
+ return new GetCredentialsRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString16NoHelper(mCallingPackage);
+ dest.writeTypedList(mGetCredentialOptions);
+ }
+
+ /**
+ * Returns the calling package of the app requesting credentials.
+ */
+ public @NonNull String getCallingPackage() {
+ return mCallingPackage;
+ }
+
+ /**
+ * Returns the list of type specific credential options to return credentials for.
+ */
+ public @NonNull List<GetCredentialOption> getGetCredentialOptions() {
+ return mGetCredentialOptions;
+ }
+
+ /**
+ * Builder for {@link GetCredentialsRequest}.
+ */
+ public static final class Builder {
+ private String mCallingPackage;
+ private List<GetCredentialOption> mGetCredentialOptions = new ArrayList<>();
+
+ /**
+ * Creates a new builder.
+ * @param callingPackage The calling package of the app requesting credentials.
+ *
+ * @throws IllegalArgumentException If {@code callingPackag}e is null or empty.
+ */
+ public Builder(@NonNull String callingPackage) {
+ mCallingPackage = Preconditions.checkStringNotEmpty(callingPackage);
+ }
+
+ /**
+ * Sets the list of credential options.
+ *
+ * @throws NullPointerException If {@code getCredentialOptions} itself or any of its
+ * elements is null.
+ * @throws IllegalArgumentException If {@code getCredentialOptions} is empty.
+ */
+ public @NonNull Builder setGetCredentialOptions(
+ @NonNull List<GetCredentialOption> getCredentialOptions) {
+ Preconditions.checkCollectionNotEmpty(mGetCredentialOptions,
+ "getCredentialOptions");
+ Preconditions.checkCollectionElementsNotNull(mGetCredentialOptions,
+ "getCredentialOptions");
+ mGetCredentialOptions = getCredentialOptions;
+ return this;
+ }
+
+ /**
+ * Adds a single {@link GetCredentialOption} object to the list of credential options.
+ *
+ * @throws NullPointerException If {@code getCredentialOption} is null.
+ */
+ public @NonNull Builder addGetCredentialOption(
+ @NonNull GetCredentialOption getCredentialOption) {
+ Objects.requireNonNull(getCredentialOption,
+ "getCredentialOption must not be null");
+ mGetCredentialOptions.add(getCredentialOption);
+ return this;
+ }
+
+ /**
+ * Builds a new {@link GetCredentialsRequest} instance.
+ *
+ * @throws NullPointerException If {@code getCredentialOptions} is null.
+ * @throws IllegalArgumentException If {@code getCredentialOptions} is empty, or if
+ * {@code callingPackage} is null or empty.
+ */
+ public @NonNull GetCredentialsRequest build() {
+ Preconditions.checkStringNotEmpty(mCallingPackage,
+ "Must set the calling package");
+ Preconditions.checkCollectionNotEmpty(mGetCredentialOptions,
+ "getCredentialOptions");
+ return new GetCredentialsRequest(mCallingPackage, mGetCredentialOptions);
+ }
+ }
+}
diff --git a/core/java/android/service/credentials/GetCredentialsResponse.aidl b/core/java/android/service/credentials/GetCredentialsResponse.aidl
new file mode 100644
index 000000000000..0d8c6357a715
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsResponse.aidl
@@ -0,0 +1,3 @@
+package android.service.credentials;
+
+parcelable GetCredentialsResponse; \ No newline at end of file
diff --git a/core/java/android/service/credentials/GetCredentialsResponse.java b/core/java/android/service/credentials/GetCredentialsResponse.java
new file mode 100644
index 000000000000..293867ba55b2
--- /dev/null
+++ b/core/java/android/service/credentials/GetCredentialsResponse.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+
+/**
+ * Response from a credential provider, containing credential entries and other associated
+ * data to be shown on the account selector UI.
+ *
+ * @hide
+ */
+public final class GetCredentialsResponse implements Parcelable {
+ /** Content to be used for the UI. */
+ private final @Nullable CredentialsDisplayContent mCredentialsDisplayContent;
+
+ /**
+ * Authentication action that must be launched and completed before showing any content
+ * from the provider.
+ */
+ private final @Nullable Action mAuthenticationAction;
+
+ /**
+ * Creates a {@link GetCredentialsRequest} instance with an authentication action set.
+ * Providers must use this method when no content can be shown before authentication.
+ *
+ * @throws NullPointerException If {@code authenticationAction} is null.
+ */
+ public static @NonNull GetCredentialsResponse createWithAuthentication(
+ @NonNull Action authenticationAction) {
+ Objects.requireNonNull(authenticationAction,
+ "authenticationAction must not be null");
+ return new GetCredentialsResponse(null, authenticationAction);
+ }
+
+ /**
+ * Creates a {@link GetCredentialsRequest} instance with display content to be shown on the UI.
+ * Providers must use this method when there is content to be shown without top level
+ * authentication required.
+ *
+ * @throws NullPointerException If {@code credentialsDisplayContent} is null.
+ */
+ public static @NonNull GetCredentialsResponse createWithDisplayContent(
+ @NonNull CredentialsDisplayContent credentialsDisplayContent) {
+ Objects.requireNonNull(credentialsDisplayContent,
+ "credentialsDisplayContent must not be null");
+ return new GetCredentialsResponse(credentialsDisplayContent, null);
+ }
+
+ private GetCredentialsResponse(@Nullable CredentialsDisplayContent credentialsDisplayContent,
+ @Nullable Action authenticationAction) {
+ mCredentialsDisplayContent = credentialsDisplayContent;
+ mAuthenticationAction = authenticationAction;
+ }
+
+ private GetCredentialsResponse(@NonNull Parcel in) {
+ mCredentialsDisplayContent = in.readParcelable(CredentialsDisplayContent.class
+ .getClassLoader(), CredentialsDisplayContent.class);
+ mAuthenticationAction = in.readParcelable(Action.class.getClassLoader(), Action.class);
+ }
+
+ public static final @NonNull Creator<GetCredentialsResponse> CREATOR =
+ new Creator<GetCredentialsResponse>() {
+ @Override
+ public GetCredentialsResponse createFromParcel(Parcel in) {
+ return new GetCredentialsResponse(in);
+ }
+
+ @Override
+ public GetCredentialsResponse[] newArray(int size) {
+ return new GetCredentialsResponse[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeParcelable(mCredentialsDisplayContent, flags);
+ dest.writeParcelable(mAuthenticationAction, flags);
+ }
+
+ /**
+ * Returns whether the response contains a top level authentication action.
+ */
+ public @NonNull boolean isAuthenticationActionSet() {
+ return mAuthenticationAction != null;
+ }
+
+ /**
+ * Returns the authentication action to be invoked before any other content
+ * can be shown to the user.
+ */
+ public @NonNull Action getAuthenticationAction() {
+ return mAuthenticationAction;
+ }
+
+ /**
+ * Returns the credentialDisplayContent that does not require authentication, and
+ * can be shown to the user on the account selector UI.
+ */
+ public @NonNull CredentialsDisplayContent getCredentialsDisplayContent() {
+ return mCredentialsDisplayContent;
+ }
+}
diff --git a/core/java/android/service/credentials/ICreateCredentialCallback.aidl b/core/java/android/service/credentials/ICreateCredentialCallback.aidl
new file mode 100644
index 000000000000..4cc76a4a341b
--- /dev/null
+++ b/core/java/android/service/credentials/ICreateCredentialCallback.aidl
@@ -0,0 +1,13 @@
+package android.service.credentials;
+
+import android.service.credentials.CreateCredentialResponse;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface ICreateCredentialCallback {
+ void onSuccess(in CreateCredentialResponse request);
+ void onFailure(int errorCode, in CharSequence message);
+} \ No newline at end of file
diff --git a/core/java/android/service/credentials/ICredentialProviderService.aidl b/core/java/android/service/credentials/ICredentialProviderService.aidl
new file mode 100644
index 000000000000..c68430ce752e
--- /dev/null
+++ b/core/java/android/service/credentials/ICredentialProviderService.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.os.ICancellationSignal;
+import android.service.credentials.GetCredentialsRequest;
+import android.service.credentials.CreateCredentialRequest;
+import android.service.credentials.IGetCredentialsCallback;
+import android.service.credentials.ICreateCredentialCallback;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface ICredentialProviderService {
+ void onGetCredentials(in GetCredentialsRequest request, in ICancellationSignal transport, in IGetCredentialsCallback callback);
+ void onCreateCredential(in CreateCredentialRequest request, in ICancellationSignal transport, in ICreateCredentialCallback callback);
+}
diff --git a/core/java/android/service/credentials/IGetCredentialsCallback.aidl b/core/java/android/service/credentials/IGetCredentialsCallback.aidl
new file mode 100644
index 000000000000..6e20c555af60
--- /dev/null
+++ b/core/java/android/service/credentials/IGetCredentialsCallback.aidl
@@ -0,0 +1,13 @@
+package android.service.credentials;
+
+import android.service.credentials.GetCredentialsResponse;
+
+/**
+ * Interface from the system to a credential provider service.
+ *
+ * @hide
+ */
+oneway interface IGetCredentialsCallback {
+ void onSuccess(in GetCredentialsResponse response);
+ void onFailure(int errorCode, in CharSequence message);
+} \ No newline at end of file
diff --git a/core/java/android/service/credentials/SaveEntry.java b/core/java/android/service/credentials/SaveEntry.java
new file mode 100644
index 000000000000..28fec30caa89
--- /dev/null
+++ b/core/java/android/service/credentials/SaveEntry.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.credentials;
+
+import android.annotation.Nullable;
+import android.app.PendingIntent;
+import android.app.slice.Slice;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+
+/**
+ * An entry to be shown on the UI. This entry represents where the credential to be created will
+ * be stored. Examples include user's account, family group etc.
+ *
+ * @hide
+ */
+public final class SaveEntry implements Parcelable {
+ private final @NonNull Slice mInfo;
+ private final @Nullable PendingIntent mPendingIntent;
+ private final @Nullable Credential mCredential;
+
+ private SaveEntry(@NonNull Parcel in) {
+ mInfo = in.readParcelable(Slice.class.getClassLoader(), Slice.class);
+ mPendingIntent = in.readParcelable(PendingIntent.class.getClassLoader(),
+ PendingIntent.class);
+ mCredential = in.readParcelable(Credential.class.getClassLoader(), Credential.class);
+ }
+
+ public static final @NonNull Creator<SaveEntry> CREATOR = new Creator<SaveEntry>() {
+ @Override
+ public SaveEntry createFromParcel(@NonNull Parcel in) {
+ return new SaveEntry(in);
+ }
+
+ @Override
+ public SaveEntry[] newArray(int size) {
+ return new SaveEntry[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mInfo.writeToParcel(dest, flags);
+ mPendingIntent.writeToParcel(dest, flags);
+ mCredential.writeToParcel(dest, flags);
+ }
+
+ /* package-private */ SaveEntry(
+ @NonNull Slice info,
+ @Nullable PendingIntent pendingIntent,
+ @Nullable Credential credential) {
+ this.mInfo = info;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mInfo);
+ this.mPendingIntent = pendingIntent;
+ this.mCredential = credential;
+ }
+
+ /** Returns the info to be displayed with this save entry on the UI. */
+ public @NonNull Slice getInfo() {
+ return mInfo;
+ }
+
+ /** Returns the pendingIntent to be invoked when this save entry on the UI is selectcd. */
+ public @Nullable PendingIntent getPendingIntent() {
+ return mPendingIntent;
+ }
+
+ /** Returns the credential produced by the {@link CreateCredentialRequest}. */
+ public @Nullable Credential getCredential() {
+ return mCredential;
+ }
+
+ /**
+ * A builder for {@link SaveEntry}.
+ */
+ public static final class Builder {
+
+ private @NonNull Slice mInfo;
+ private @Nullable PendingIntent mPendingIntent;
+ private @Nullable Credential mCredential;
+
+ /**
+ * Builds the instance.
+ * @param info The info to be displayed with this save entry.
+ *
+ * @throws NullPointerException If {@code info} is null.
+ */
+ public Builder(@NonNull Slice info) {
+ mInfo = Objects.requireNonNull(info, "info must not be null");
+ }
+
+ /**
+ * Sets the pendingIntent to be invoked when this entry is selected by the user.
+ *
+ * @throws IllegalStateException If {@code credential} is already set. Must only set either
+ * {@code credential}, or the {@code pendingIntent}.
+ */
+ public @NonNull Builder setPendingIntent(@Nullable PendingIntent pendingIntent) {
+ Preconditions.checkState(pendingIntent != null
+ && mCredential != null, "credential is already set. Must only set "
+ + "either the pendingIntent or the credential");
+ mPendingIntent = pendingIntent;
+ return this;
+ }
+
+ /**
+ * Sets the credential to be returned when this entry is selected by the user.
+ *
+ * @throws IllegalStateException If {@code pendingIntent} is already set. Must only
+ * set either the {@code pendingIntent}, or {@code credential}.
+ */
+ public @NonNull Builder setCredential(@Nullable Credential credential) {
+ Preconditions.checkState(credential != null && mPendingIntent != null,
+ "pendingIntent is already set. Must only set either the credential "
+ + "or the pendingIntent");
+ mCredential = credential;
+ return this;
+ }
+
+ /**
+ * Builds the instance.
+ *
+ * @throws IllegalStateException if both {@code pendingIntent} and {@code credential}
+ * are null.
+ */
+ public @NonNull SaveEntry build() {
+ Preconditions.checkState(mPendingIntent == null && mCredential == null,
+ "pendingIntent and credential both must not be null. Must set "
+ + "either the pendingIntnet or the credential");
+ return new SaveEntry(
+ mInfo,
+ mPendingIntent,
+ mCredential);
+ }
+ }
+}
diff --git a/core/java/android/service/dreams/DreamOverlayService.java b/core/java/android/service/dreams/DreamOverlayService.java
index 163d6ed4b18b..432444211bce 100644
--- a/core/java/android/service/dreams/DreamOverlayService.java
+++ b/core/java/android/service/dreams/DreamOverlayService.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.Service;
+import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
@@ -36,6 +37,7 @@ public abstract class DreamOverlayService extends Service {
private static final String TAG = "DreamOverlayService";
private static final boolean DEBUG = false;
private boolean mShowComplications;
+ private ComponentName mDreamComponent;
private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
@Override
@@ -56,6 +58,8 @@ public abstract class DreamOverlayService extends Service {
public final IBinder onBind(@NonNull Intent intent) {
mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS,
DreamService.DEFAULT_SHOW_COMPLICATIONS);
+ mDreamComponent = intent.getParcelableExtra(DreamService.EXTRA_DREAM_COMPONENT,
+ ComponentName.class);
return mDreamOverlay.asBinder();
}
@@ -84,4 +88,12 @@ public abstract class DreamOverlayService extends Service {
public final boolean shouldShowComplications() {
return mShowComplications;
}
+
+ /**
+ * Returns the active dream component.
+ * @hide
+ */
+ public final ComponentName getDreamComponent() {
+ return mDreamComponent;
+ }
}
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 6f5212ce2968..13913268cad0 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -218,6 +218,12 @@ public class DreamService extends Service implements Window.Callback {
"android.service.dreams.SHOW_COMPLICATIONS";
/**
+ * Extra containing the component name for the active dream.
+ * @hide
+ */
+ public static final String EXTRA_DREAM_COMPONENT = "android.service.dreams.DREAM_COMPONENT";
+
+ /**
* The default value for whether to show complications on the overlay.
* @hide
*/
@@ -271,6 +277,7 @@ public class DreamService extends Service implements Window.Callback {
overlayIntent.setComponent(overlayService);
overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS,
fetchShouldShowComplications(context, serviceInfo));
+ overlayIntent.putExtra(EXTRA_DREAM_COMPONENT, dreamService);
context.bindService(overlayIntent,
this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);
diff --git a/core/java/android/service/notification/Condition.java b/core/java/android/service/notification/Condition.java
index 267b2ff818a6..4d33bfd1e94a 100644
--- a/core/java/android/service/notification/Condition.java
+++ b/core/java/android/service/notification/Condition.java
@@ -91,6 +91,12 @@ public final class Condition implements Parcelable {
public final int icon;
/**
+ * The maximum string length for any string contained in this condition.
+ * @hide
+ */
+ public static final int MAX_STRING_LENGTH = 1000;
+
+ /**
* An object representing the current state of a {@link android.app.AutomaticZenRule}.
* @param id the {@link android.app.AutomaticZenRule#getConditionId()} of the zen rule
* @param summary a user visible description of the rule state.
@@ -104,16 +110,19 @@ public final class Condition implements Parcelable {
if (id == null) throw new IllegalArgumentException("id is required");
if (summary == null) throw new IllegalArgumentException("summary is required");
if (!isValidState(state)) throw new IllegalArgumentException("state is invalid: " + state);
- this.id = id;
- this.summary = summary;
- this.line1 = line1;
- this.line2 = line2;
+ this.id = getTrimmedUri(id);
+ this.summary = getTrimmedString(summary);
+ this.line1 = getTrimmedString(line1);
+ this.line2 = getTrimmedString(line2);
this.icon = icon;
this.state = state;
this.flags = flags;
}
public Condition(Parcel source) {
+ // This constructor passes all fields directly into the constructor that takes all the
+ // fields as arguments; that constructor will trim each of the input strings to
+ // max length if necessary.
this((Uri)source.readParcelable(Condition.class.getClassLoader(), android.net.Uri.class),
source.readString(),
source.readString(),
@@ -240,4 +249,25 @@ public final class Condition implements Parcelable {
return new Condition[size];
}
};
+
+ /**
+ * Returns a truncated copy of the string if the string is longer than MAX_STRING_LENGTH.
+ */
+ private static String getTrimmedString(String input) {
+ if (input != null && input.length() > MAX_STRING_LENGTH) {
+ return input.substring(0, MAX_STRING_LENGTH);
+ }
+ return input;
+ }
+
+ /**
+ * Returns a truncated copy of the Uri by trimming the string representation to the maximum
+ * string length.
+ */
+ private static Uri getTrimmedUri(Uri input) {
+ if (input != null && input.toString().length() > MAX_STRING_LENGTH) {
+ return Uri.parse(getTrimmedString(input.toString()));
+ }
+ return input;
+ }
}
diff --git a/core/java/android/service/voice/HotwordDetectionService.java b/core/java/android/service/voice/HotwordDetectionService.java
index c6de8438bccb..df69cc00709c 100644
--- a/core/java/android/service/voice/HotwordDetectionService.java
+++ b/core/java/android/service/voice/HotwordDetectionService.java
@@ -88,6 +88,14 @@ public abstract class HotwordDetectionService extends Service {
public static final int MAXIMUM_NUMBER_OF_INITIALIZATION_STATUS_CUSTOM_ERROR = 2;
/**
+ * Feature flag for Attention Service.
+ *
+ * TODO(b/247920386): Add TestApi annotation
+ * @hide
+ */
+ public static final boolean ENABLE_PROXIMITY_RESULT = false;
+
+ /**
* Indicates that the updated status is successful.
*/
public static final int INITIALIZATION_STATUS_SUCCESS = 0;
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index 1b46107de02c..1285d1e2a4eb 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -345,6 +345,12 @@ public class VoiceInteractionService extends Service {
* Calling this a second time invalidates the previously created hotword detector
* which can no longer be used to manage recognition.
*
+ * <p>Note: If there are any active detectors that are created by using
+ * {@link #createAlwaysOnHotwordDetector(String, Locale, PersistableBundle, SharedMemory,
+ * AlwaysOnHotwordDetector.Callback)} or {@link #createHotwordDetector(PersistableBundle,
+ * SharedMemory, HotwordDetector.Callback)}, call this will throw an
+ * {@link IllegalArgumentException}.
+ *
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
* @param callback The callback to notify of detection events.
@@ -377,6 +383,10 @@ public class VoiceInteractionService extends Service {
* <p>Note: The system will trigger hotword detection service after calling this function when
* all conditions meet the requirements.
*
+ * <p>Note: If there are any active detectors that are created by using
+ * {@link #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)},
+ * call this will throw an {@link IllegalArgumentException}.
+ *
* @param keyphrase The keyphrase that's being used, for example "Hello Android".
* @param locale The locale for which the enrollment needs to be performed.
* @param options Application configuration data provided by the
@@ -420,6 +430,14 @@ public class VoiceInteractionService extends Service {
safelyShutdownAllHotwordDetectors();
}
+ for (HotwordDetector detector : mActiveHotwordDetectors) {
+ if (detector.isUsingHotwordDetectionService() != supportHotwordDetectionService) {
+ throw new IllegalArgumentException(
+ "It disallows to create trusted and non-trusted detectors "
+ + "at the same time.");
+ }
+ }
+
AlwaysOnHotwordDetector dspDetector = new AlwaysOnHotwordDetector(keyphrase, locale,
callback, mKeyphraseEnrollmentInfo, mSystemService,
getApplicationContext().getApplicationInfo().targetSdkVersion,
@@ -460,6 +478,10 @@ public class VoiceInteractionService extends Service {
* devices where hardware filtering is available (such as through a DSP), it's highly
* recommended to use {@link #createAlwaysOnHotwordDetector} instead.
*
+ * <p>Note: If there are any active detectors that are created by using
+ * {@link #createAlwaysOnHotwordDetector(String, Locale, AlwaysOnHotwordDetector.Callback)},
+ * call this will throw an {@link IllegalArgumentException}.
+ *
* @param options Application configuration data to be provided to the
* {@link HotwordDetectionService}. PersistableBundle does not allow any remotable objects or
* other contents that can be used to communicate with other processes.
@@ -490,7 +512,11 @@ public class VoiceInteractionService extends Service {
safelyShutdownAllHotwordDetectors();
} else {
for (HotwordDetector detector : mActiveHotwordDetectors) {
- if (detector instanceof SoftwareHotwordDetector) {
+ if (!detector.isUsingHotwordDetectionService()) {
+ throw new IllegalArgumentException(
+ "It disallows to create trusted and non-trusted detectors "
+ + "at the same time.");
+ } else if (detector instanceof SoftwareHotwordDetector) {
throw new IllegalArgumentException(
"There is already an active SoftwareHotwordDetector. "
+ "It must be destroyed to create a new one.");
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index be8c140e8770..e4c26e031738 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -93,6 +93,7 @@ import android.view.WindowLayout;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.window.ClientWindowFrames;
+import android.window.ScreenCapture;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.HandlerCaller;
@@ -1989,9 +1990,9 @@ public abstract class WallpaperService extends Service {
cleanUpScreenshotSurfaceControl();
}
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureLayers(
- new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+ ScreenCapture.captureLayers(
+ new ScreenCapture.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.
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index b5f7c545aa07..dbb41f47a495 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -1841,7 +1841,7 @@ public abstract class Layout {
// Find the first line whose vertical center is below the top of the area.
int startLine = getLineForVertical((int) area.top);
int startLineTop = getLineTop(startLine);
- int startLineBottom = getLineBottomWithoutSpacing(startLine);
+ int startLineBottom = getLineBottom(startLine, /* includeLineSpacing= */ false);
if (area.top > (startLineTop + startLineBottom) / 2f) {
startLine++;
if (startLine >= getLineCount()) {
@@ -1854,7 +1854,7 @@ public abstract class Layout {
// Find the last line whose vertical center is above the bottom of the area.
int endLine = getLineForVertical((int) area.bottom);
int endLineTop = getLineTop(endLine);
- int endLineBottom = getLineBottomWithoutSpacing(endLine);
+ int endLineBottom = getLineBottom(endLine, /* includeLineSpacing= */ false);
if (area.bottom < (endLineTop + endLineBottom) / 2f) {
endLine--;
}
@@ -2229,17 +2229,21 @@ public abstract class Layout {
* Return the vertical position of the bottom of the specified line.
*/
public final int getLineBottom(int line) {
- return getLineTop(line + 1);
+ return getLineBottom(line, /* includeLineSpacing= */ true);
}
/**
- * Return the vertical position of the bottom of the specified line without the line spacing
- * added.
+ * Return the vertical position of the bottom of the specified line.
*
- * @hide
+ * @param line index of the line
+ * @param includeLineSpacing whether to include the line spacing
*/
- public final int getLineBottomWithoutSpacing(int line) {
- return getLineTop(line + 1) - getLineExtra(line);
+ public int getLineBottom(int line, boolean includeLineSpacing) {
+ if (includeLineSpacing) {
+ return getLineTop(line + 1);
+ } else {
+ return getLineTop(line + 1) - getLineExtra(line);
+ }
}
/**
@@ -2394,7 +2398,7 @@ public abstract class Layout {
int line = getLineForOffset(point);
int top = getLineTop(line);
- int bottom = getLineBottomWithoutSpacing(line);
+ int bottom = getLineBottom(line, /* includeLineSpacing= */ false);
boolean clamped = shouldClampCursor(line);
float h1 = getPrimaryHorizontal(point, clamped) - 0.5f;
@@ -2530,7 +2534,7 @@ public abstract class Layout {
final int endline = getLineForOffset(end);
int top = getLineTop(startline);
- int bottom = getLineBottomWithoutSpacing(endline);
+ int bottom = getLineBottom(endline, /* includeLineSpacing= */ false);
if (startline == endline) {
addSelection(startline, start, end, top, bottom, consumer);
@@ -2559,7 +2563,7 @@ public abstract class Layout {
}
top = getLineTop(endline);
- bottom = getLineBottomWithoutSpacing(endline);
+ bottom = getLineBottom(endline, /* includeLineSpacing= */ false);
addSelection(endline, getLineStart(endline), end, top, bottom, consumer);
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 51e36657e769..596e4914896a 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -93,7 +93,9 @@ public class TextUtils {
private static final String ELLIPSIS_NORMAL = "\u2026"; // HORIZONTAL ELLIPSIS (…)
private static final String ELLIPSIS_TWO_DOTS = "\u2025"; // TWO DOT LEADER (‥)
- private static final int LINE_FEED_CODE_POINT = 10;
+ /** @hide */
+ public static final int LINE_FEED_CODE_POINT = 10;
+
private static final int NBSP_CODE_POINT = 160;
/**
@@ -2335,11 +2337,29 @@ public class TextUtils {
|| codePoint == LINE_FEED_CODE_POINT;
}
- private static boolean isWhiteSpace(int codePoint) {
+ /** @hide */
+ public static boolean isWhitespace(int codePoint) {
return Character.isWhitespace(codePoint) || codePoint == NBSP_CODE_POINT;
}
/** @hide */
+ public static boolean isWhitespaceExceptNewline(int codePoint) {
+ return isWhitespace(codePoint) && !isNewline(codePoint);
+ }
+
+ /** @hide */
+ public static boolean isPunctuation(int codePoint) {
+ int type = Character.getType(codePoint);
+ return type == Character.CONNECTOR_PUNCTUATION
+ || type == Character.DASH_PUNCTUATION
+ || type == Character.END_PUNCTUATION
+ || type == Character.FINAL_QUOTE_PUNCTUATION
+ || type == Character.INITIAL_QUOTE_PUNCTUATION
+ || type == Character.OTHER_PUNCTUATION
+ || type == Character.START_PUNCTUATION;
+ }
+
+ /** @hide */
@Nullable
public static String withoutPrefix(@Nullable String prefix, @Nullable String str) {
if (prefix == null || str == null) return str;
@@ -2430,7 +2450,7 @@ public class TextUtils {
gettingCleaned.removeRange(offset, offset + codePointLen);
} else if (type == Character.CONTROL && !isNewline) {
gettingCleaned.removeRange(offset, offset + codePointLen);
- } else if (trim && !isWhiteSpace(codePoint)) {
+ } else if (trim && !isWhitespace(codePoint)) {
// This is only executed if the code point is not removed
if (firstNonWhiteSpace == -1) {
firstNonWhiteSpace = offset;
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index 537dffc4abf1..d48d566fd860 100755
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -170,7 +170,7 @@ public class DateFormat {
* mean using 12-hour in some locales and, in this case, is duplicated as the 'a' field.
*/
@ChangeId
- @EnabledSince(targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT)
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
static final long DISALLOW_DUPLICATE_FIELD_IN_SKELETON = 170233598L;
/**
diff --git a/core/java/android/text/method/WordIterator.java b/core/java/android/text/method/WordIterator.java
index f427e1bd2b72..6d18d2c541b6 100644
--- a/core/java/android/text/method/WordIterator.java
+++ b/core/java/android/text/method/WordIterator.java
@@ -24,6 +24,7 @@ import android.icu.text.BreakIterator;
import android.os.Build;
import android.text.CharSequenceCharacterIterator;
import android.text.Selection;
+import android.text.TextUtils;
import java.util.Locale;
@@ -275,9 +276,9 @@ public class WordIterator implements Selection.PositionIterator {
}
/**
- * If <code>offset</code> is within a group of punctuation as defined
- * by {@link #isPunctuation(int)}, returns the index of the first character
- * of that group, otherwise returns BreakIterator.DONE.
+ * If <code>offset</code> is within a group of punctuation as defined by {@link
+ * TextUtils#isPunctuation(int)}, returns the index of the first character of that group,
+ * otherwise returns BreakIterator.DONE.
*
* @param offset the offset to search from.
*/
@@ -292,9 +293,9 @@ public class WordIterator implements Selection.PositionIterator {
}
/**
- * If <code>offset</code> is within a group of punctuation as defined
- * by {@link #isPunctuation(int)}, returns the index of the last character
- * of that group plus one, otherwise returns BreakIterator.DONE.
+ * If <code>offset</code> is within a group of punctuation as defined by {@link
+ * TextUtils#isPunctuation(int)}, returns the index of the last character of that group plus
+ * one, otherwise returns BreakIterator.DONE.
*
* @param offset the offset to search from.
*/
@@ -309,8 +310,8 @@ public class WordIterator implements Selection.PositionIterator {
}
/**
- * Indicates if the provided offset is after a punctuation character
- * as defined by {@link #isPunctuation(int)}.
+ * Indicates if the provided offset is after a punctuation character as defined by {@link
+ * TextUtils#isPunctuation(int)}.
*
* @param offset the offset to check from.
* @return Whether the offset is after a punctuation character.
@@ -319,14 +320,14 @@ public class WordIterator implements Selection.PositionIterator {
public boolean isAfterPunctuation(int offset) {
if (mStart < offset && offset <= mEnd) {
final int codePoint = Character.codePointBefore(mCharSeq, offset);
- return isPunctuation(codePoint);
+ return TextUtils.isPunctuation(codePoint);
}
return false;
}
/**
- * Indicates if the provided offset is at a punctuation character
- * as defined by {@link #isPunctuation(int)}.
+ * Indicates if the provided offset is at a punctuation character as defined by {@link
+ * TextUtils#isPunctuation(int)}.
*
* @param offset the offset to check from.
* @return Whether the offset is at a punctuation character.
@@ -335,7 +336,7 @@ public class WordIterator implements Selection.PositionIterator {
public boolean isOnPunctuation(int offset) {
if (mStart <= offset && offset < mEnd) {
final int codePoint = Character.codePointAt(mCharSeq, offset);
- return isPunctuation(codePoint);
+ return TextUtils.isPunctuation(codePoint);
}
return false;
}
@@ -369,17 +370,6 @@ public class WordIterator implements Selection.PositionIterator {
return !isOnPunctuation(offset) && isAfterPunctuation(offset);
}
- private static boolean isPunctuation(int cp) {
- final int type = Character.getType(cp);
- return (type == Character.CONNECTOR_PUNCTUATION
- || type == Character.DASH_PUNCTUATION
- || type == Character.END_PUNCTUATION
- || type == Character.FINAL_QUOTE_PUNCTUATION
- || type == Character.INITIAL_QUOTE_PUNCTUATION
- || type == Character.OTHER_PUNCTUATION
- || type == Character.START_PUNCTUATION);
- }
-
private boolean isAfterLetterOrDigit(int offset) {
if (mStart < offset && offset <= mEnd) {
final int codePoint = Character.codePointBefore(mCharSeq, offset);
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 20f01ae3ae0d..cb8f0af10c47 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -96,13 +96,6 @@ public class FeatureFlagUtils {
/** @hide */
public static final String SETTINGS_AUTO_TEXT_WRAPPING = "settings_auto_text_wrapping";
-
- /** Support Clear Calling feature.
- * @hide
- */
- public static final String SETTINGS_ENABLE_CLEAR_CALLING = "settings_enable_clear_calling";
-
-
/** Flag to enable / disable the Simple Cursor accessibility feature in
* Settings.
* @hide
@@ -122,6 +115,11 @@ public class FeatureFlagUtils {
*/
public static final String SETTINGS_ENABLE_SPA = "settings_enable_spa";
+ /** Flag to enable/disable adb log metrics
+ * @hide
+ */
+ public static final String SETTINGS_ADB_METRICS_WRITER = "settings_adb_metrics_writer";
+
private static final Map<String, String> DEFAULT_FLAGS;
static {
@@ -151,10 +149,10 @@ public class FeatureFlagUtils {
DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
DEFAULT_FLAGS.put(SETTINGS_HIDE_SECOND_LAYER_PAGE_NAVIGATE_UP_BUTTON_IN_TWO_PANE, "true");
DEFAULT_FLAGS.put(SETTINGS_AUTO_TEXT_WRAPPING, "false");
- DEFAULT_FLAGS.put(SETTINGS_ENABLE_CLEAR_CALLING, "false");
DEFAULT_FLAGS.put(SETTINGS_ACCESSIBILITY_SIMPLE_CURSOR, "false");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_UI, "false");
DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA, "false");
+ DEFAULT_FLAGS.put(SETTINGS_ADB_METRICS_WRITER, "false");
}
private static final Set<String> PERSISTENT_FLAGS;
diff --git a/core/java/android/util/SparseArrayMap.java b/core/java/android/util/SparseArrayMap.java
index e5bb9f453a88..1a2c4df96b36 100644
--- a/core/java/android/util/SparseArrayMap.java
+++ b/core/java/android/util/SparseArrayMap.java
@@ -34,14 +34,20 @@ import java.util.function.Consumer;
public class SparseArrayMap<K, V> {
private final SparseArray<ArrayMap<K, V>> mData = new SparseArray<>();
- /** Add an entry associating obj with the int-K pair. */
- public void add(int key, @NonNull K mapKey, @Nullable V obj) {
+ /**
+ * Add an entry associating obj with the int-K pair.
+ *
+ * @return the previous value associated with key, or null if there was no mapping for key.
+ * (A null return can also indicate that the map previously associated null with key, if the
+ * implementation supports null values.)
+ */
+ public V add(int key, @NonNull K mapKey, @Nullable V obj) {
ArrayMap<K, V> data = mData.get(key);
if (data == null) {
data = new ArrayMap<>();
mData.put(key, data);
}
- data.put(mapKey, obj);
+ return data.put(mapKey, obj);
}
/** Remove all entries from the map. */
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index f9bb880b1881..57ba7e9e816f 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -653,7 +653,7 @@ public class GestureDetector {
break;
case MotionEvent.ACTION_MOVE:
- if ((mCurrentDownEvent == null) || mInLongPress || mInContextClick) {
+ if (mInLongPress || mInContextClick) {
break;
}
@@ -736,9 +736,6 @@ public class GestureDetector {
break;
case MotionEvent.ACTION_UP:
- if (mCurrentDownEvent == null) {
- break;
- }
mStillDown = false;
MotionEvent currentUpEvent = MotionEvent.obtain(ev);
if (mIsDoubleTapping) {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 229de31ddcba..067946e90361 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -67,6 +67,7 @@ import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.window.ITaskFpsCallback;
+import android.window.ScreenCapture;
/**
* System private interface to the window manager.
@@ -114,6 +115,7 @@ interface IWindowManager
@UnsupportedAppUsage
int getInitialDisplayDensity(int displayId);
int getBaseDisplayDensity(int displayId);
+ int getDisplayIdByUniqueId(String uniqueId);
void setForcedDisplayDensityForUser(int displayId, int density, int userId);
void clearForcedDisplayDensityForUser(int displayId, int userId);
void setForcedDisplayScalingMode(int displayId, int mode); // 0 = auto, 1 = disable
@@ -967,4 +969,11 @@ interface IWindowManager
* treatment.
*/
boolean isLetterboxBackgroundMultiColored();
+
+ /**
+ * Captures the entire display specified by the displayId using the args provided. If the args
+ * are null or if the sourceCrop is invalid or null, the entire display bounds will be captured.
+ */
+ oneway void captureDisplay(int displayId, in @nullable ScreenCapture.CaptureArgs captureArgs,
+ in ScreenCapture.ScreenCaptureListener listener);
}
diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java
index a5ac9480d2cf..d4875d400c5b 100644
--- a/core/java/android/view/ImeFocusController.java
+++ b/core/java/android/view/ImeFocusController.java
@@ -43,8 +43,21 @@ public final class ImeFocusController {
private final ViewRootImpl mViewRootImpl;
private boolean mHasImeFocus = false;
+
+ /**
+ * This is the view that should currently be served by an input method,
+ * regardless of the state of setting that up.
+ * @see InputMethodManagerDelegate#getLockObject()
+ */
private View mServedView;
+
+ /**
+ * This is the next view that will be served by the input method, when
+ * we get around to updating things.
+ * @see InputMethodManagerDelegate#getLockObject()
+ */
private View mNextServedView;
+
private InputMethodManagerDelegate mDelegate;
@UiThread
@@ -123,45 +136,52 @@ public final class ImeFocusController {
boolean forceFocus = false;
final InputMethodManagerDelegate immDelegate = getImmDelegate();
- if (immDelegate.isRestartOnNextWindowFocus(true /* reset */)) {
- if (DEBUG) Log.v(TAG, "Restarting due to isRestartOnNextWindowFocus as true");
- forceFocus = true;
- }
+ synchronized (immDelegate.getLockObject()) {
+ // Update mNextServedView when focusedView changed.
+ onViewFocusChanged(viewForWindowFocus, true);
- // Update mNextServedView when focusedView changed.
- onViewFocusChanged(viewForWindowFocus, true);
+ // Starting new input when the next focused view is same as served view but the
+ // currently active connection (if any) is not associated with it.
+ final boolean nextFocusIsServedView = mServedView == viewForWindowFocus;
- // Starting new input when the next focused view is same as served view but the currently
- // active connection (if any) is not associated with it.
- final boolean nextFocusIsServedView = mServedView == viewForWindowFocus;
- if (nextFocusIsServedView && !immDelegate.hasActiveConnection(viewForWindowFocus)) {
- forceFocus = true;
+ if (nextFocusIsServedView && !immDelegate.hasActiveConnection(viewForWindowFocus)) {
+ forceFocus = true;
+ }
}
- immDelegate.startInputAsyncOnWindowFocusGain(viewForWindowFocus,
+ immDelegate.startInputOnWindowFocusGain(viewForWindowFocus,
windowAttribute.softInputMode, windowAttribute.flags, forceFocus);
}
+ /**
+ * @see InputMethodManager#checkFocus()
+ */
public boolean checkFocus(boolean forceNewFocus, boolean startInput) {
final InputMethodManagerDelegate immDelegate = getImmDelegate();
- if (!immDelegate.isCurrentRootView(mViewRootImpl)
- || (mServedView == mNextServedView && !forceNewFocus)) {
- return false;
- }
- if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
- + " next=" + mNextServedView
- + " force=" + forceNewFocus
- + " package="
- + (mServedView != null ? mServedView.getContext().getPackageName() : "<none>"));
-
- // Close the connection when no next served view coming.
- if (mNextServedView == null) {
- immDelegate.finishInput();
- immDelegate.closeCurrentIme();
- return false;
+ synchronized (immDelegate.getLockObject()) {
+ if (!immDelegate.isCurrentRootView(mViewRootImpl)) {
+ return false;
+ }
+ if (mServedView == mNextServedView && !forceNewFocus) {
+ return false;
+ }
+ if (DEBUG) {
+ Log.v(TAG, "checkFocus: view=" + mServedView
+ + " next=" + mNextServedView
+ + " force=" + forceNewFocus
+ + " package="
+ + (mServedView != null ? mServedView.getContext().getPackageName()
+ : "<none>"));
+ }
+ // Close the connection when no next served view coming.
+ if (mNextServedView == null) {
+ immDelegate.finishInput();
+ immDelegate.closeCurrentIme();
+ return false;
+ }
+ mServedView = mNextServedView;
+ immDelegate.finishComposingText();
}
- mServedView = mNextServedView;
- immDelegate.finishComposingText();
if (startInput) {
immDelegate.startInput(StartInputReason.CHECK_FOCUS, null /* focusedView */,
@@ -175,51 +195,61 @@ public final class ImeFocusController {
if (view == null || view.isTemporarilyDetached()) {
return;
}
- if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) {
- return;
- }
- if (!view.hasImeFocus() || !view.hasWindowFocus()) {
- return;
- }
- if (DEBUG) Log.d(TAG, "onViewFocusChanged, view=" + InputMethodDebug.dumpViewInfo(view)
- + ", mServedView=" + InputMethodDebug.dumpViewInfo(mServedView));
-
- // We don't need to track the next served view when the view lost focus here because:
- // 1) The current view focus may be cleared temporary when in touch mode, closing input
- // at this moment isn't the right way.
- // 2) We only care about the served view change when it focused, since changing input
- // connection when the focus target changed is reasonable.
- // 3) Setting the next served view as null when no more served view should be handled in
- // other special events (e.g. view detached from window or the window dismissed).
- if (hasFocus) {
- mNextServedView = view;
+ final InputMethodManagerDelegate immDelegate = getImmDelegate();
+ synchronized (immDelegate.getLockObject()) {
+ if (!immDelegate.isCurrentRootView(view.getViewRootImpl())) {
+ return;
+ }
+ if (!view.hasImeFocus() || !view.hasWindowFocus()) {
+ return;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "onViewFocusChanged, view=" + InputMethodDebug.dumpViewInfo(view)
+ + ", mServedView=" + InputMethodDebug.dumpViewInfo(mServedView));
+ }
+
+ // We don't need to track the next served view when the view lost focus here because:
+ // 1) The current view focus may be cleared temporary when in touch mode, closing input
+ // at this moment isn't the right way.
+ // 2) We only care about the served view change when it focused, since changing input
+ // connection when the focus target changed is reasonable.
+ // 3) Setting the next served view as null when no more served view should be handled in
+ // other special events (e.g. view detached from window or the window dismissed).
+ if (hasFocus) {
+ mNextServedView = view;
+ }
}
mViewRootImpl.dispatchCheckFocus();
}
@UiThread
void onViewDetachedFromWindow(View view) {
- if (!getImmDelegate().isCurrentRootView(view.getViewRootImpl())) {
- return;
- }
- if (mNextServedView == view) {
- mNextServedView = null;
- }
- if (mServedView == view) {
- mViewRootImpl.dispatchCheckFocus();
+ final InputMethodManagerDelegate immDelegate = getImmDelegate();
+ synchronized (immDelegate.getLockObject()) {
+ if (!immDelegate.isCurrentRootView(view.getViewRootImpl())) {
+ return;
+ }
+ if (mNextServedView == view) {
+ mNextServedView = null;
+ }
+ if (mServedView == view) {
+ mViewRootImpl.dispatchCheckFocus();
+ }
}
}
@UiThread
void onWindowDismissed() {
final InputMethodManagerDelegate immDelegate = getImmDelegate();
- if (!immDelegate.isCurrentRootView(mViewRootImpl)) {
- return;
+ synchronized (immDelegate.getLockObject()) {
+ if (!immDelegate.isCurrentRootView(mViewRootImpl)) {
+ return;
+ }
+ if (mServedView != null) {
+ immDelegate.finishInput();
+ }
+ immDelegate.setCurrentRootView(null);
}
- if (mServedView != null) {
- immDelegate.finishInput();
- }
- immDelegate.setCurrentRootView(null);
mHasImeFocus = false;
}
@@ -270,10 +300,22 @@ public final class ImeFocusController {
* @hide
*/
public interface InputMethodManagerDelegate {
+ /**
+ * Starts the input connection.
+ * Note that this method must not hold the {@link InputMethodManager} lock with
+ * {@link InputMethodManagerDelegate#getLockObject()} while {@link InputMethodManager}
+ * calling into app-code in different threads.
+ */
boolean startInput(@StartInputReason int startInputReason, View focusedView,
@StartInputFlags int startInputFlags,
@WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags);
- void startInputAsyncOnWindowFocusGain(View rootView,
+ /**
+ * Starts the input connection when gaining the window focus.
+ * Note that this method must not hold the {@link InputMethodManager} lock with
+ * {@link InputMethodManagerDelegate#getLockObject()} while {@link InputMethodManager}
+ * calling into app-code in different threads.
+ */
+ void startInputOnWindowFocusGain(View rootView,
@WindowManager.LayoutParams.SoftInputModeFlags int softInputMode, int windowFlags,
boolean forceNewFocus);
void finishInput();
@@ -282,24 +324,53 @@ public final class ImeFocusController {
void finishComposingText();
void setCurrentRootView(ViewRootImpl rootView);
boolean isCurrentRootView(ViewRootImpl rootView);
- boolean isRestartOnNextWindowFocus(boolean reset);
boolean hasActiveConnection(View view);
+
+ /**
+ * Returns the {@code InputMethodManager#mH} lock object.
+ * Used for {@link ImeFocusController} to guard the served view being accessed by
+ * {@link InputMethodManager} in different threads.
+ */
+ Object getLockObject();
}
- public View getServedView() {
+ /**
+ * Returns The current IME served view for {@link InputMethodManager}.
+ * Used to start input connection or check the caller's validity when calling
+ * {@link InputMethodManager} APIs.
+ * Note that this method requires to be called inside {@code InputMethodManager#mH} lock for
+ * data consistency.
+ */
+ public View getServedViewLocked() {
return mServedView;
}
- public View getNextServedView() {
+ /**
+ * Returns The next incoming IME served view for {@link InputMethodManager}.
+ * Note that this method requires to be called inside {@code InputMethodManager#mH} lock for
+ * data consistency.
+ */
+ public View getNextServedViewLocked() {
return mNextServedView;
}
- public void setServedView(View view) {
- mServedView = view;
- }
-
- public void setNextServedView(View view) {
- mNextServedView = view;
+ /**
+ * Clears the served & the next served view when the controller triggers
+ * {@link InputMethodManagerDelegate#finishInput()} or
+ * {@link InputMethodManagerDelegate#finishInputAndReportToIme()}.
+ * Note that this method requires to be called inside {@code InputMethodManager#mH} lock for
+ * data consistency.
+ *
+ * @return The {@code mServedView} that has cleared, or {@code null} means nothing to clear.
+ */
+ public View clearServedViewsLocked() {
+ View clearedView = null;
+ mNextServedView = null;
+ if (mServedView != null) {
+ clearedView = mServedView;
+ mServedView = null;
+ }
+ return clearedView;
}
/**
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 583252756b92..c8c941a220f0 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -22,6 +22,7 @@ import static android.view.InsetsSourceProto.VISIBLE;
import static android.view.InsetsSourceProto.VISIBLE_FRAME;
import static android.view.InsetsState.ITYPE_CAPTION_BAR;
import static android.view.InsetsState.ITYPE_IME;
+import static android.view.ViewRootImpl.CAPTION_ON_SHELL;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -148,7 +149,7 @@ public class InsetsSource implements Parcelable {
// During drag-move and drag-resizing, the caption insets position may not get updated
// before the app frame get updated. To layout the app content correctly during drag events,
// we always return the insets with the corresponding height covering the top.
- if (getType() == ITYPE_CAPTION_BAR) {
+ if (!CAPTION_ON_SHELL && getType() == ITYPE_CAPTION_BAR) {
return Insets.of(0, frame.height(), 0, 0);
}
// Checks for whether there is shared edge with insets for 0-width/height window.
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 6c238e5698c5..9789b5670fbb 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -872,13 +872,33 @@ public class KeyEvent extends InputEvent implements Parcelable {
public static final int KEYCODE_KEYBOARD_BACKLIGHT_UP = 306;
/** Key code constant: Keyboard backlight toggle */
public static final int KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE = 307;
+ /**
+ * Key code constant: The primary button on the barrel of a stylus.
+ * This is usually the button closest to the tip of the stylus.
+ */
+ public static final int KEYCODE_STYLUS_BUTTON_PRIMARY = 308;
+ /**
+ * Key code constant: The secondary button on the barrel of a stylus.
+ * This is usually the second button from the tip of the stylus.
+ */
+ public static final int KEYCODE_STYLUS_BUTTON_SECONDARY = 309;
+ /**
+ * Key code constant: The tertiary button on the barrel of a stylus.
+ * This is usually the third button from the tip of the stylus.
+ */
+ public static final int KEYCODE_STYLUS_BUTTON_TERTIARY = 310;
+ /**
+ * Key code constant: A button on the tail end of a stylus.
+ * The use of this button does not usually correspond to the function of an eraser.
+ */
+ public static final int KEYCODE_STYLUS_BUTTON_TAIL = 311;
/**
* Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent.
* @hide
*/
@TestApi
- public static final int LAST_KEYCODE = KEYCODE_KEYBOARD_BACKLIGHT_TOGGLE;
+ public static final int LAST_KEYCODE = KEYCODE_STYLUS_BUTTON_TAIL;
// NOTE: If you add a new keycode here you must also add it to:
// isSystem()
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index ea0012543ba9..5ec796245774 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1457,10 +1457,20 @@ public final class MotionEvent extends InputEvent implements Parcelable {
*/
public static final int CLASSIFICATION_DEEP_PRESS = 2;
+ /**
+ * Classification constant: touchpad scroll.
+ *
+ * The current event stream represents the user scrolling with two fingers on a touchpad.
+ *
+ * @see #getClassification
+ */
+ public static final int CLASSIFICATION_TWO_FINGER_SWIPE = 3;
+
/** @hide */
@Retention(SOURCE)
@IntDef(prefix = { "CLASSIFICATION" }, value = {
- CLASSIFICATION_NONE, CLASSIFICATION_AMBIGUOUS_GESTURE, CLASSIFICATION_DEEP_PRESS})
+ CLASSIFICATION_NONE, CLASSIFICATION_AMBIGUOUS_GESTURE, CLASSIFICATION_DEEP_PRESS,
+ CLASSIFICATION_TWO_FINGER_SWIPE})
public @interface Classification {};
/**
@@ -3862,9 +3872,11 @@ public final class MotionEvent extends InputEvent implements Parcelable {
return "AMBIGUOUS_GESTURE";
case CLASSIFICATION_DEEP_PRESS:
return "DEEP_PRESS";
+ case CLASSIFICATION_TWO_FINGER_SWIPE:
+ return "TWO_FINGER_SWIPE";
}
- return "NONE";
+ return "UNKNOWN";
}
/**
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index aef38eb2e2d2..2f7ae498b8b1 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -22,6 +22,7 @@ import static android.graphics.Matrix.MSKEW_X;
import static android.graphics.Matrix.MSKEW_Y;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
+import static android.view.Display.INVALID_DISPLAY;
import static android.view.SurfaceControlProto.HASH_CODE;
import static android.view.SurfaceControlProto.LAYER_ID;
import static android.view.SurfaceControlProto.NAME;
@@ -35,7 +36,7 @@ import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.graphics.Bitmap;
+import android.content.Context;
import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
import android.graphics.Matrix;
@@ -48,15 +49,23 @@ import android.hardware.DataSpace;
import android.hardware.HardwareBuffer;
import android.hardware.SyncFence;
import android.hardware.display.DeviceProductInfo;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerGlobal;
import android.hardware.display.DisplayedContentSample;
import android.hardware.display.DisplayedContentSamplingAttributes;
+import android.hardware.display.IDisplayManager;
+import android.hardware.display.IVirtualDisplayCallback;
+import android.hardware.display.VirtualDisplay;
import android.hardware.graphics.common.DisplayDecorationSupport;
+import android.media.projection.MediaProjectionGlobal;
import android.opengl.EGLDisplay;
import android.opengl.EGLSync;
import android.os.Build;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseIntArray;
@@ -80,9 +89,7 @@ import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
/**
@@ -107,10 +114,7 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeRelease(long nativeObject);
private static native void nativeDisconnect(long nativeObject);
private static native void nativeUpdateDefaultBufferSize(long nativeObject, int width, int height);
- private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
- ScreenCaptureListener captureListener);
- private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
- ScreenCaptureListener captureListener);
+
private static native long nativeMirrorSurface(long mirrorOfObject);
private static native long nativeCreateTransaction();
private static native long nativeGetNativeTransactionFinalizer();
@@ -172,8 +176,6 @@ public final class SurfaceControl implements Parcelable {
private static native long[] nativeGetPhysicalDisplayIds();
private static native IBinder nativeGetPhysicalDisplayToken(long physicalDisplayId);
- private static native IBinder nativeCreateDisplay(String name, boolean secure);
- private static native void nativeDestroyDisplay(IBinder displayToken);
private static native void nativeSetDisplaySurface(long transactionObj,
IBinder displayToken, long nativeSurfaceObject);
private static native void nativeSetDisplayLayerStack(long transactionObj,
@@ -223,8 +225,6 @@ public final class SurfaceControl implements Parcelable {
private static native void nativeSetDimmingEnabled(long transactionObj, long nativeObject,
boolean dimmingEnabled);
- private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
-
private static native void nativeSetInputWindowInfo(long transactionObj, long nativeObject,
InputWindowHandle handle);
@@ -447,6 +447,7 @@ public final class SurfaceControl implements Parcelable {
private String mName;
/**
+ * Note: do not rename, this field is used by native code.
* @hide
*/
public long mNativeObject;
@@ -521,7 +522,7 @@ public final class SurfaceControl implements Parcelable {
* be copied. In particular, screenshots and secondary, non-secure displays will render black
* content instead of the surface content.
*
- * @see #createDisplay(String, boolean)
+ * @see com.android.server.display.DisplayControl#createDisplay(String, boolean)
* @hide
*/
public static final int SECURE = 0x00000080;
@@ -781,412 +782,6 @@ public final class SurfaceControl implements Parcelable {
public static final int METADATA_GAME_MODE = 8;
/**
- * A wrapper around HardwareBuffer that contains extra information about how to
- * interpret the screenshot HardwareBuffer.
- *
- * @hide
- */
- public static class ScreenshotHardwareBuffer {
- private final HardwareBuffer mHardwareBuffer;
- private final ColorSpace mColorSpace;
- private final boolean mContainsSecureLayers;
- private final boolean mContainsHdrLayers;
-
- public ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace,
- boolean containsSecureLayers, boolean containsHdrLayers) {
- mHardwareBuffer = hardwareBuffer;
- mColorSpace = colorSpace;
- mContainsSecureLayers = containsSecureLayers;
- mContainsHdrLayers = containsHdrLayers;
- }
-
- /**
- * Create ScreenshotHardwareBuffer from an existing HardwareBuffer object.
- * @param hardwareBuffer The existing HardwareBuffer object
- * @param namedColorSpace Integer value of a named color space {@link ColorSpace.Named}
- * @param containsSecureLayers Indicates whether this graphic buffer contains captured
- * contents of secure layers, in which case the screenshot
- * should not be persisted.
- * @param containsHdrLayers Indicates whether this graphic buffer contains HDR content.
- */
- private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer,
- int namedColorSpace, boolean containsSecureLayers, boolean containsHdrLayers) {
- ColorSpace colorSpace = ColorSpace.get(ColorSpace.Named.values()[namedColorSpace]);
- return new ScreenshotHardwareBuffer(
- hardwareBuffer, colorSpace, containsSecureLayers, containsHdrLayers);
- }
-
- public ColorSpace getColorSpace() {
- return mColorSpace;
- }
-
- public HardwareBuffer getHardwareBuffer() {
- return mHardwareBuffer;
- }
-
- public boolean containsSecureLayers() {
- return mContainsSecureLayers;
- }
- /**
- * Returns whether the screenshot contains at least one HDR layer.
- * This information may be useful for informing the display whether this screenshot
- * is allowed to be dimmed to SDR white.
- */
- public boolean containsHdrLayers() {
- return mContainsHdrLayers;
- }
-
- /**
- * Copy content of ScreenshotHardwareBuffer into a hardware bitmap and return it.
- * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap
- * into
- * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
- *
- * CAVEAT: This can be extremely slow; avoid use unless absolutely necessary; prefer to
- * directly
- * use the {@link HardwareBuffer} directly.
- *
- * @return Bitmap generated from the {@link HardwareBuffer}
- */
- public Bitmap asBitmap() {
- if (mHardwareBuffer == null) {
- Log.w(TAG, "Failed to take screenshot. Null screenshot object");
- return null;
- }
- return Bitmap.wrapHardwareBuffer(mHardwareBuffer, mColorSpace);
- }
- }
-
- /**
- * @hide
- */
- public interface ScreenCaptureListener {
- /**
- * The callback invoked when the screen capture is complete.
- * @param hardwareBuffer Data containing info about the screen capture.
- */
- void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer);
- }
-
- private static class SyncScreenCaptureListener implements ScreenCaptureListener {
- private static final int SCREENSHOT_WAIT_TIME_S = 1;
- private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
- private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
-
- @Override
- public void onScreenCaptureComplete(ScreenshotHardwareBuffer hardwareBuffer) {
- mScreenshotHardwareBuffer = hardwareBuffer;
- mCountDownLatch.countDown();
- }
-
- private ScreenshotHardwareBuffer waitForScreenshot() {
- try {
- mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
- } catch (Exception e) {
- Log.e(TAG, "Failed to wait for screen capture result", e);
- }
-
- return mScreenshotHardwareBuffer;
- }
- }
-
- /**
- * A common arguments class used for various screenshot requests. This contains arguments that
- * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
- * @hide
- */
- private abstract static class CaptureArgs {
- private final int mPixelFormat;
- private final Rect mSourceCrop = new Rect();
- private final float mFrameScaleX;
- private final float mFrameScaleY;
- private final boolean mCaptureSecureLayers;
- private final boolean mAllowProtected;
- private final long mUid;
- private final boolean mGrayscale;
-
- private CaptureArgs(Builder<? extends Builder<?>> builder) {
- mPixelFormat = builder.mPixelFormat;
- mSourceCrop.set(builder.mSourceCrop);
- mFrameScaleX = builder.mFrameScaleX;
- mFrameScaleY = builder.mFrameScaleY;
- mCaptureSecureLayers = builder.mCaptureSecureLayers;
- mAllowProtected = builder.mAllowProtected;
- mUid = builder.mUid;
- mGrayscale = builder.mGrayscale;
- }
-
- /**
- * The Builder class used to construct {@link CaptureArgs}
- *
- * @param <T> A builder that extends {@link Builder}
- */
- abstract static class Builder<T extends Builder<T>> {
- private int mPixelFormat = PixelFormat.RGBA_8888;
- private final Rect mSourceCrop = new Rect();
- private float mFrameScaleX = 1;
- private float mFrameScaleY = 1;
- private boolean mCaptureSecureLayers;
- private boolean mAllowProtected;
- private long mUid = -1;
- private boolean mGrayscale;
-
- /**
- * The desired pixel format of the returned buffer.
- */
- public T setPixelFormat(int pixelFormat) {
- mPixelFormat = pixelFormat;
- return getThis();
- }
-
- /**
- * The portion of the screen to capture into the buffer. Caller may pass in
- * 'new Rect()' or null if no cropping is desired.
- */
- public T setSourceCrop(@Nullable Rect sourceCrop) {
- if (sourceCrop == null) {
- mSourceCrop.setEmpty();
- } else {
- mSourceCrop.set(sourceCrop);
- }
- return getThis();
- }
-
- /**
- * The desired scale of the returned buffer. The raw screen will be scaled up/down.
- */
- public T setFrameScale(float frameScale) {
- mFrameScaleX = frameScale;
- mFrameScaleY = frameScale;
- return getThis();
- }
-
- /**
- * The desired scale of the returned buffer, allowing separate values for x and y scale.
- * The raw screen will be scaled up/down.
- */
- public T setFrameScale(float frameScaleX, float frameScaleY) {
- mFrameScaleX = frameScaleX;
- mFrameScaleY = frameScaleY;
- return getThis();
- }
-
- /**
- * Whether to allow the screenshot of secure layers. Warning: This should only be done
- * if the content will be placed in a secure SurfaceControl.
- *
- * @see ScreenshotHardwareBuffer#containsSecureLayers()
- */
- public T setCaptureSecureLayers(boolean captureSecureLayers) {
- mCaptureSecureLayers = captureSecureLayers;
- return getThis();
- }
-
- /**
- * Whether to allow the screenshot of protected (DRM) content. Warning: The screenshot
- * cannot be read in unprotected space.
- *
- * @see HardwareBuffer#USAGE_PROTECTED_CONTENT
- */
- public T setAllowProtected(boolean allowProtected) {
- mAllowProtected = allowProtected;
- return getThis();
- }
-
- /**
- * Set the uid of the content that should be screenshot. The code will skip any surfaces
- * that don't belong to the specified uid.
- */
- public T setUid(long uid) {
- mUid = uid;
- return getThis();
- }
-
- /**
- * Set whether the screenshot should use grayscale or not.
- */
- public T setGrayscale(boolean grayscale) {
- mGrayscale = grayscale;
- return getThis();
- }
-
- /**
- * Each sub class should return itself to allow the builder to chain properly
- */
- abstract T getThis();
- }
- }
-
- /**
- * The arguments class used to make display capture requests.
- *
- * @see #nativeCaptureDisplay(DisplayCaptureArgs, ScreenCaptureListener)
- * @hide
- */
- public static class DisplayCaptureArgs extends CaptureArgs {
- private final IBinder mDisplayToken;
- private final int mWidth;
- private final int mHeight;
- private final boolean mUseIdentityTransform;
-
- private DisplayCaptureArgs(Builder builder) {
- super(builder);
- mDisplayToken = builder.mDisplayToken;
- mWidth = builder.mWidth;
- mHeight = builder.mHeight;
- mUseIdentityTransform = builder.mUseIdentityTransform;
- }
-
- /**
- * The Builder class used to construct {@link DisplayCaptureArgs}
- */
- public static class Builder extends CaptureArgs.Builder<Builder> {
- private IBinder mDisplayToken;
- private int mWidth;
- private int mHeight;
- private boolean mUseIdentityTransform;
-
- /**
- * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
- * remains valid.
- */
- public DisplayCaptureArgs build() {
- if (mDisplayToken == null) {
- throw new IllegalStateException(
- "Can't take screenshot with null display token");
- }
- return new DisplayCaptureArgs(this);
- }
-
- public Builder(IBinder displayToken) {
- setDisplayToken(displayToken);
- }
-
- /**
- * The display to take the screenshot of.
- */
- public Builder setDisplayToken(IBinder displayToken) {
- mDisplayToken = displayToken;
- return this;
- }
-
- /**
- * Set the desired size of the returned buffer. The raw screen will be scaled down to
- * this size
- *
- * @param width The desired width of the returned buffer. Caller may pass in 0 if no
- * scaling is desired.
- * @param height The desired height of the returned buffer. Caller may pass in 0 if no
- * scaling is desired.
- */
- public Builder setSize(int width, int height) {
- mWidth = width;
- mHeight = height;
- return this;
- }
-
- /**
- * Replace the rotation transform of the display with the identity transformation while
- * taking the screenshot. This ensures the screenshot is taken in the ROTATION_0
- * orientation. Set this value to false if the screenshot should be taken in the
- * current screen orientation.
- */
- public Builder setUseIdentityTransform(boolean useIdentityTransform) {
- mUseIdentityTransform = useIdentityTransform;
- return this;
- }
-
- @Override
- Builder getThis() {
- return this;
- }
- }
- }
-
- /**
- * The arguments class used to make layer capture requests.
- *
- * @see #nativeCaptureLayers(LayerCaptureArgs, ScreenCaptureListener)
- * @hide
- */
- public static class LayerCaptureArgs extends CaptureArgs {
- private final long mNativeLayer;
- private final long[] mNativeExcludeLayers;
- private final boolean mChildrenOnly;
-
- private LayerCaptureArgs(Builder builder) {
- super(builder);
- mChildrenOnly = builder.mChildrenOnly;
- mNativeLayer = builder.mLayer.mNativeObject;
- if (builder.mExcludeLayers != null) {
- mNativeExcludeLayers = new long[builder.mExcludeLayers.length];
- for (int i = 0; i < builder.mExcludeLayers.length; i++) {
- mNativeExcludeLayers[i] = builder.mExcludeLayers[i].mNativeObject;
- }
- } else {
- mNativeExcludeLayers = null;
- }
- }
-
- /**
- * The Builder class used to construct {@link LayerCaptureArgs}
- */
- public static class Builder extends CaptureArgs.Builder<Builder> {
- private SurfaceControl mLayer;
- private SurfaceControl[] mExcludeLayers;
- private boolean mChildrenOnly = true;
-
- /**
- * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
- * remains valid.
- */
- public LayerCaptureArgs build() {
- if (mLayer == null) {
- throw new IllegalStateException(
- "Can't take screenshot with null layer");
- }
- return new LayerCaptureArgs(this);
- }
-
- public Builder(SurfaceControl layer) {
- setLayer(layer);
- }
-
- /**
- * The root layer to capture.
- */
- public Builder setLayer(SurfaceControl layer) {
- mLayer = layer;
- return this;
- }
-
-
- /**
- * An array of layer handles to exclude.
- */
- public Builder setExcludeLayers(@Nullable SurfaceControl[] excludeLayers) {
- mExcludeLayers = excludeLayers;
- return this;
- }
-
- /**
- * Whether to include the layer itself in the screenshot or just the children and their
- * descendants.
- */
- public Builder setChildrenOnly(boolean childrenOnly) {
- mChildrenOnly = childrenOnly;
- return this;
- }
-
- @Override
- Builder getThis() {
- return this;
- }
-
- }
- }
-
- /**
* Builder class for {@link SurfaceControl} objects.
*
* By default the surface will be hidden, and have "unset" bounds, meaning it can
@@ -2355,37 +1950,89 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * Overrides HDR modes for a display device.
+ * Because this API is now going through {@link DisplayManager}, orientation and displayRect
+ * will automatically be computed based on configuration changes. Because of this, the params
+ * orientation and displayRect are ignored
*
- * If the caller does not have ACCESS_SURFACE_FLINGER permission, this will throw a Security
- * Exception.
* @hide
*/
- @TestApi
- public static void overrideHdrTypes(@NonNull IBinder displayToken, @NonNull int[] modes) {
- nativeOverrideHdrTypes(displayToken, modes);
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+ publicAlternatives = "Use {@code VirtualDisplay#resize(int, int, int)} instead.",
+ trackingBug = 247078497)
+ public static void setDisplayProjection(IBinder displayToken, int orientation,
+ Rect layerStackRect, Rect displayRect) {
+ DisplayManagerGlobal.getInstance().resizeVirtualDisplay(
+ IVirtualDisplayCallback.Stub.asInterface(displayToken), layerStackRect.width(),
+ layerStackRect.height(), 1);
}
/**
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+ publicAlternatives = "Use {@code MediaProjection#createVirtualDisplay()} with flag "
+ + " {@code VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} for mirroring instead.",
+ trackingBug = 247078497)
+ public static void setDisplayLayerStack(IBinder displayToken, int layerStack) {
+ IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
+ if (b == null) {
+ throw new UnsupportedOperationException();
+ }
+
+ IDisplayManager dm = IDisplayManager.Stub.asInterface(b);
+ try {
+ dm.setDisplayIdToMirror(displayToken, layerStack);
+ } catch (RemoteException e) {
+ throw new UnsupportedOperationException(e);
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+ publicAlternatives = "Use {@code VirtualDisplay#setSurface(Surface)} instead.",
+ trackingBug = 247078497)
+ public static void setDisplaySurface(IBinder displayToken, Surface surface) {
+ IVirtualDisplayCallback virtualDisplayCallback =
+ IVirtualDisplayCallback.Stub.asInterface(displayToken);
+ DisplayManagerGlobal dm = DisplayManagerGlobal.getInstance();
+ dm.setVirtualDisplaySurface(virtualDisplayCallback, surface);
+ }
+
+ /**
+ * Secure is no longer supported because this is only called from outside system which cannot
+ * create secure displays.
+ * @hide
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+ publicAlternatives = "Use {@code MediaProjection#createVirtualDisplay()} or "
+ + "{@code DisplayManager#createVirtualDisplay()} instead.",
+ trackingBug = 247078497)
public static IBinder createDisplay(String name, boolean secure) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
- return nativeCreateDisplay(name, secure);
+
+ // We don't have a size yet so pass in 1 for width and height since 0 is invalid
+ VirtualDisplay vd = MediaProjectionGlobal.getInstance().createVirtualDisplay(name,
+ 1 /* width */, 1 /* height */, INVALID_DISPLAY, null /* Surface */);
+ return vd == null ? null : vd.getToken().asBinder();
}
/**
* @hide
*/
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.TIRAMISU,
+ publicAlternatives = "Use {@code VirtualDisplay#release()} instead.",
+ trackingBug = 247078497)
public static void destroyDisplay(IBinder displayToken) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
}
- nativeDestroyDisplay(displayToken);
+
+ DisplayManagerGlobal.getInstance().releaseVirtualDisplay(
+ IVirtualDisplayCallback.Stub.asInterface(displayToken));
}
/**
@@ -2418,119 +2065,6 @@ public final class SurfaceControl implements Parcelable {
}
/**
- * @param captureArgs Arguments about how to take the screenshot
- * @param captureListener A listener to receive the screenshot callback
- * @hide
- */
- public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
- @NonNull ScreenCaptureListener captureListener) {
- return nativeCaptureDisplay(captureArgs, captureListener);
- }
-
- /**
- * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with
- * the content.
- *
- * @hide
- */
- public static ScreenshotHardwareBuffer captureDisplay(DisplayCaptureArgs captureArgs) {
- SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener();
-
- int status = captureDisplay(captureArgs, screenCaptureListener);
- if (status != 0) {
- return null;
- }
-
- return screenCaptureListener.waitForScreenshot();
- }
-
- /**
- * Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
- *
- * @param layer The root layer to capture.
- * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
- * Rect()' or null if no cropping is desired. If the root layer does not
- * have a buffer or a crop set, then a non-empty source crop must be
- * specified.
- * @param frameScale The desired scale of the returned buffer; the raw
- * screen will be scaled up/down.
- *
- * @return Returns a HardwareBuffer that contains the layer capture.
- * @hide
- */
- public static ScreenshotHardwareBuffer captureLayers(SurfaceControl layer, Rect sourceCrop,
- float frameScale) {
- return captureLayers(layer, sourceCrop, frameScale, PixelFormat.RGBA_8888);
- }
-
- /**
- * Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
- *
- * @param layer The root layer to capture.
- * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
- * Rect()' or null if no cropping is desired. If the root layer does not
- * have a buffer or a crop set, then a non-empty source crop must be
- * specified.
- * @param frameScale The desired scale of the returned buffer; the raw
- * screen will be scaled up/down.
- * @param format The desired pixel format of the returned buffer.
- *
- * @return Returns a HardwareBuffer that contains the layer capture.
- * @hide
- */
- public static ScreenshotHardwareBuffer captureLayers(@NonNull SurfaceControl layer,
- @Nullable Rect sourceCrop, float frameScale, int format) {
- LayerCaptureArgs captureArgs = new LayerCaptureArgs.Builder(layer)
- .setSourceCrop(sourceCrop)
- .setFrameScale(frameScale)
- .setPixelFormat(format)
- .build();
-
- return captureLayers(captureArgs);
- }
-
- /**
- * @hide
- */
- public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
- SyncScreenCaptureListener screenCaptureListener = new SyncScreenCaptureListener();
-
- int status = captureLayers(captureArgs, screenCaptureListener);
- if (status != 0) {
- return null;
- }
-
- return screenCaptureListener.waitForScreenshot();
- }
-
- /**
- * Like {@link #captureLayers(SurfaceControl, Rect, float, int)} but with an array of layer
- * handles to exclude.
- * @hide
- */
- public static ScreenshotHardwareBuffer captureLayersExcluding(SurfaceControl layer,
- Rect sourceCrop, float frameScale, int format, SurfaceControl[] exclude) {
- LayerCaptureArgs captureArgs = new LayerCaptureArgs.Builder(layer)
- .setSourceCrop(sourceCrop)
- .setFrameScale(frameScale)
- .setPixelFormat(format)
- .setExcludeLayers(exclude)
- .build();
-
- return captureLayers(captureArgs);
- }
-
- /**
- * @param captureArgs Arguments about how to take the screenshot
- * @param captureListener A listener to receive the screenshot callback
- * @hide
- */
- public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
- @NonNull ScreenCaptureListener captureListener) {
- return nativeCaptureLayers(captureArgs, captureListener);
- }
-
- /**
* Returns whether protected content is supported in GPU composition.
* @hide
*/
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 7acf319a5049..9075de13c4d8 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -107,6 +107,14 @@ import java.util.function.Consumer;
* and scaling a SurfaceView on screen will not cause rendering artifacts. Such
* artifacts may occur on previous versions of the platform when its window is
* positioned asynchronously.</p>
+ *
+ * <p class="note"><strong>Note:</strong> Starting in platform version
+ * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, SurfaceView will support arbitrary
+ * alpha blending. Prior platform versions ignored alpha values on the SurfaceView if they were
+ * between 0 and 1. If the SurfaceView is configured with Z-above, then the alpha is applied
+ * directly to the Surface. If the SurfaceView is configured with Z-below, then the alpha is
+ * applied to the hole punch directly. Note that when using Z-below, overlapping SurfaceViews
+ * may not blend properly as a consequence of not applying alpha to the surface content directly.
*/
public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCallback {
private static final String TAG = "SurfaceView";
@@ -146,6 +154,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
Paint mRoundedViewportPaint;
int mSubLayer = APPLICATION_MEDIA_SUBLAYER;
+ int mRequestedSubLayer = APPLICATION_MEDIA_SUBLAYER;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
boolean mIsCreating = false;
@@ -177,8 +186,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
@UnsupportedAppUsage
int mRequestedFormat = PixelFormat.RGB_565;
- boolean mUseAlpha = false;
- float mSurfaceAlpha = 1f;
+ float mAlpha = 1f;
boolean mClipSurfaceToBounds;
int mBackgroundColor = Color.BLACK;
@@ -335,58 +343,25 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* @hide
*/
public void setUseAlpha() {
- if (!mUseAlpha) {
- mUseAlpha = true;
- updateSurfaceAlpha();
- }
+ // TODO(b/241474646): Remove me
+ return;
}
@Override
public void setAlpha(float alpha) {
- // Sets the opacity of the view to a value, where 0 means the view is completely transparent
- // and 1 means the view is completely opaque.
- //
- // Note: Alpha value of this view is ignored by default. To enable alpha blending, you need
- // to call setUseAlpha() as well.
- // This view doesn't support translucent opacity if the view is located z-below, since the
- // logic to punch a hole in the view hierarchy cannot handle such case. See also
- // #clearSurfaceViewPort(Canvas)
if (DEBUG) {
Log.d(TAG, System.identityHashCode(this)
- + " setAlpha: mUseAlpha = " + mUseAlpha + " alpha=" + alpha);
+ + " setAlpha: alpha=" + alpha);
}
super.setAlpha(alpha);
- updateSurfaceAlpha();
- }
-
- private float getFixedAlpha() {
- // Compute alpha value to be set on the underlying surface.
- final float alpha = getAlpha();
- return mUseAlpha && (mSubLayer > 0 || alpha == 0f) ? alpha : 1f;
}
- private void updateSurfaceAlpha() {
- if (!mUseAlpha || !mHaveFrame || mSurfaceControl == null) {
- return;
- }
- final float viewAlpha = getAlpha();
- if (mSubLayer < 0 && 0f < viewAlpha && viewAlpha < 1f) {
- Log.w(TAG, System.identityHashCode(this)
- + " updateSurfaceAlpha:"
- + " translucent color is not supported for a surface placed z-below.");
- }
- final ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot == null) {
- return;
- }
- final float alpha = getFixedAlpha();
- if (alpha != mSurfaceAlpha) {
- final Transaction transaction = new Transaction();
- transaction.setAlpha(mSurfaceControl, alpha);
- viewRoot.applyTransactionOnDraw(transaction);
- damageInParent();
- mSurfaceAlpha = alpha;
+ @Override
+ protected boolean onSetAlpha(int alpha) {
+ if (Math.round(mAlpha * 255) != alpha) {
+ updateSurface();
}
+ return true;
}
private void performDrawFinished() {
@@ -534,7 +509,15 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
invalidate();
}
+ @Override
+ public boolean hasOverlappingRendering() {
+ // SurfaceViews only alpha composite by modulating the Surface alpha for Z-above, or
+ // applying alpha on the hole punch for Z-below - no deferral to a layer is necessary.
+ return false;
+ }
+
private void clearSurfaceViewPort(Canvas canvas) {
+ final float alpha = getAlpha();
if (mCornerRadius > 0f) {
canvas.getClipBounds(mTmpRect);
if (mClipSurfaceToBounds && mClipBounds != null) {
@@ -546,10 +529,11 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mTmpRect.right,
mTmpRect.bottom,
mCornerRadius,
- mCornerRadius
+ mCornerRadius,
+ alpha
);
} else {
- canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f);
+ canvas.punchHole(0f, 0f, getWidth(), getHeight(), 0f, 0f, alpha);
}
}
@@ -626,7 +610,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
* @hide
*/
public boolean isZOrderedOnTop() {
- return mSubLayer > 0;
+ return mRequestedSubLayer > 0;
}
/**
@@ -652,10 +636,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
} else {
subLayer = APPLICATION_MEDIA_SUBLAYER;
}
- if (mSubLayer == subLayer) {
+ if (mRequestedSubLayer == subLayer) {
return false;
}
- mSubLayer = subLayer;
+ mRequestedSubLayer = subLayer;
if (!allowDynamicChange) {
return false;
@@ -667,9 +651,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
if (viewRoot == null) {
return true;
}
- final Transaction transaction = new SurfaceControl.Transaction();
- updateRelativeZ(transaction);
- viewRoot.applyTransactionOnDraw(transaction);
+
+ updateSurface();
invalidate();
return true;
}
@@ -722,8 +705,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
private void releaseSurfaces(boolean releaseSurfacePackage) {
- mSurfaceAlpha = 1f;
-
+ mAlpha = 1f;
synchronized (mSurfaceControlLock) {
mSurface.destroy();
if (mBlastBufferQueue != null) {
@@ -770,7 +752,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator,
- boolean creating, boolean sizeChanged, boolean hintChanged,
+ boolean creating, boolean sizeChanged, boolean hintChanged, boolean relativeZChanged,
Transaction surfaceUpdateTransaction) {
boolean realSizeChanged = false;
@@ -800,14 +782,20 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
surfaceUpdateTransaction.hide(mSurfaceControl);
}
-
-
updateBackgroundVisibility(surfaceUpdateTransaction);
updateBackgroundColor(surfaceUpdateTransaction);
- if (mUseAlpha) {
- float alpha = getFixedAlpha();
+ if (isAboveParent()) {
+ float alpha = getAlpha();
surfaceUpdateTransaction.setAlpha(mSurfaceControl, alpha);
- mSurfaceAlpha = alpha;
+ }
+
+ if (relativeZChanged) {
+ if (!isAboveParent()) {
+ // If we're moving from z-above to z-below, then restore the surface alpha back to 1
+ // and let the holepunch drive visibility and blending.
+ surfaceUpdateTransaction.setAlpha(mSurfaceControl, 1.f);
+ }
+ updateRelativeZ(surfaceUpdateTransaction);
}
surfaceUpdateTransaction.setCornerRadius(mSurfaceControl, mCornerRadius);
@@ -873,6 +861,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
} finally {
mSurfaceLock.unlock();
}
+
return realSizeChanged;
}
@@ -906,10 +895,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
int myHeight = mRequestedHeight;
if (myHeight <= 0) myHeight = getHeight();
- final float alpha = getFixedAlpha();
+ final float alpha = getAlpha();
final boolean formatChanged = mFormat != mRequestedFormat;
final boolean visibleChanged = mVisible != mRequestedVisible;
- final boolean alphaChanged = mSurfaceAlpha != alpha;
+ final boolean alphaChanged = mAlpha != alpha;
final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
&& mRequestedVisible;
final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
@@ -921,17 +910,17 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
|| getHeight() != mScreenRect.height();
final boolean hintChanged = (viewRoot.getBufferTransformHint() != mTransformHint)
&& mRequestedVisible;
+ final boolean relativeZChanged = mSubLayer != mRequestedSubLayer;
- if (creating || formatChanged || sizeChanged || visibleChanged ||
- (mUseAlpha && alphaChanged) || windowVisibleChanged ||
- positionChanged || layoutSizeChanged || hintChanged) {
+ if (creating || formatChanged || sizeChanged || visibleChanged
+ || alphaChanged || windowVisibleChanged || positionChanged
+ || layoutSizeChanged || hintChanged || relativeZChanged) {
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "Changes: creating=" + creating
+ " format=" + formatChanged + " size=" + sizeChanged
+ " visible=" + visibleChanged + " alpha=" + alphaChanged
+ " hint=" + hintChanged
- + " mUseAlpha=" + mUseAlpha
+ " visible=" + visibleChanged
+ " left=" + (mWindowSpaceLeft != mLocation[0])
+ " top=" + (mWindowSpaceTop != mLocation[1]));
@@ -943,8 +932,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
mSurfaceWidth = myWidth;
mSurfaceHeight = myHeight;
mFormat = mRequestedFormat;
+ mAlpha = alpha;
mLastWindowVisibility = mWindowVisibility;
mTransformHint = viewRoot.getBufferTransformHint();
+ mSubLayer = mRequestedSubLayer;
mScreenRect.left = mWindowSpaceLeft;
mScreenRect.top = mWindowSpaceTop;
@@ -968,7 +959,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
}
final boolean redrawNeeded = sizeChanged || creating || hintChanged
- || (mVisible && !mDrawFinished);
+ || (mVisible && !mDrawFinished) || alphaChanged || relativeZChanged;
boolean shouldSyncBuffer =
redrawNeeded && viewRoot.wasRelayoutRequested() && viewRoot.isInLocalSync();
SyncBufferTransactionCallback syncBufferTransactionCallback = null;
@@ -979,8 +970,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall
syncBufferTransactionCallback::onTransactionReady);
}
- final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
- translator, creating, sizeChanged, hintChanged, surfaceUpdateTransaction);
+ final boolean realSizeChanged = performSurfaceTransaction(viewRoot, translator,
+ creating, sizeChanged, hintChanged, relativeZChanged,
+ surfaceUpdateTransaction);
try {
SurfaceHolder.Callback[] callbacks = null;
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index c97eb73c120b..d77e8825e462 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -196,8 +196,6 @@ public final class ThreadedRenderer extends HardwareRenderer {
*/
public static boolean sRendererEnabled = true;
- public static boolean sTrimForeground = false;
-
/**
* Controls whether or not the renderer should aggressively trim
* memory. Note that this must not be set for any process that uses
@@ -205,9 +203,10 @@ public final class ThreadedRenderer extends HardwareRenderer {
* that do not go into the background.
*/
public static void enableForegroundTrimming() {
- sTrimForeground = true;
+ // TODO: Remove
}
+
/**
* Initialize HWUI for being in a system process like system_server
* Should not be called in non-system processes
@@ -218,9 +217,8 @@ public final class ThreadedRenderer extends HardwareRenderer {
// process.
if (!ActivityManager.isHighEndGfx()) {
sRendererEnabled = false;
- } else {
- enableForegroundTrimming();
}
+ setIsSystemOrPersistent();
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 94ea2060a770..4a59bfb45a0d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3095,7 +3095,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* <p>
* Accessibility interactions from services without {@code isAccessibilityTool} set to true are
* disallowed for any of the following conditions:
- * <li>this view's window sets {@link WindowManager.LayoutParams#FLAG_SECURE}.</li>
* <li>this view sets {@link #getFilterTouchesWhenObscured()}.</li>
* <li>any parent of this view returns true from {@link #isAccessibilityDataPrivate()}.</li>
* </p>
@@ -8304,7 +8303,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// We have not been laid out yet, hence cannot evaluate
// whether this view is visible to the user, we will do
// the evaluation once layout is complete.
- if (!isLaidOut()) {
+ // Sometimes, views are already laid out, but it's still
+ // not visible to the user, we also do the evaluation once
+ // the view is visible. ex: There is a fade-in animation
+ // for the activity, the view will be laid out when the
+ // animation beginning. On the time, the view is not visible
+ // to the user. And then as the animation progresses, the view
+ // becomes visible to the user.
+ if (!isLaidOut() || !isVisibleToUser()) {
mPrivateFlags3 |= PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;
} else if (isVisibleToUser()) {
if (isFocused()) {
@@ -8326,10 +8332,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Visually distinct portion of a window with window-like semantics are considered panes for
- * accessibility purposes. One example is the content view of a fragment that is replaced.
+ * accessibility purposes. One example is the content view of a large fragment that is replaced.
* In order for accessibility services to understand a pane's window-like behavior, panes
- * should have descriptive titles. Views with pane titles produce {@link AccessibilityEvent}s
- * when they appear, disappear, or change title.
+ * should have descriptive titles. Views with pane titles produce
+ * {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}s when they appear, disappear, or change
+ * title.
+ *
+ * <p>
+ * When transitioning from one Activity to another, instead of using
+ * setAccessibilityPaneTitle(), set a descriptive title for its window by using android:label
+ * for the matching <activity> entry in your application’s manifest or updating the title at
+ * runtime with{@link android.app.Activity#setTitle(CharSequence)}.
*
* @param accessibilityPaneTitle The pane's title. Setting to {@code null} indicates that this
* View is not a pane.
@@ -14524,9 +14537,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
// Use the explicit value if set.
if (mExplicitAccessibilityDataPrivate != ACCESSIBILITY_DATA_PRIVATE_AUTO) {
mInferredAccessibilityDataPrivate = mExplicitAccessibilityDataPrivate;
- } else if (mAttachInfo != null && mAttachInfo.mWindowSecure) {
- // Views inside FLAG_SECURE windows default to accessibilityDataPrivate.
- mInferredAccessibilityDataPrivate = ACCESSIBILITY_DATA_PRIVATE_YES;
} else if (getFilterTouchesWhenObscured()) {
// Views that set filterTouchesWhenObscured default to accessibilityDataPrivate.
mInferredAccessibilityDataPrivate = ACCESSIBILITY_DATA_PRIVATE_YES;
@@ -24646,7 +24656,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int viewStateIndex = 0;
if ((privateFlags & PFLAG_PRESSED) != 0) viewStateIndex |= StateSet.VIEW_STATE_PRESSED;
if ((mViewFlags & ENABLED_MASK) == ENABLED) viewStateIndex |= StateSet.VIEW_STATE_ENABLED;
- if (isFocused()) viewStateIndex |= StateSet.VIEW_STATE_FOCUSED;
+ if (isFocused() && hasWindowFocus()) viewStateIndex |= StateSet.VIEW_STATE_FOCUSED;
if ((privateFlags & PFLAG_SELECTED) != 0) viewStateIndex |= StateSet.VIEW_STATE_SELECTED;
if (hasWindowFocus()) viewStateIndex |= StateSet.VIEW_STATE_WINDOW_FOCUSED;
if ((privateFlags & PFLAG_ACTIVATED) != 0) viewStateIndex |= StateSet.VIEW_STATE_ACTIVATED;
@@ -30254,11 +30264,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
int mWindowVisibility;
/**
- * Indicates whether the view's window sets {@link WindowManager.LayoutParams#FLAG_SECURE}.
- */
- boolean mWindowSecure;
-
- /**
* Indicates the time at which drawing started to occur.
*/
@UnsupportedAppUsage
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 074cbe5a6947..28fa77c0a4de 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1766,9 +1766,6 @@ public final class ViewRootImpl implements ViewParent,
mAppVisibilityChanged = true;
scheduleTraversals();
}
- if (!mAppVisible) {
- WindowManagerGlobal.trimForeground();
- }
AnimationHandler.requestAnimatorsEnabled(mAppVisible, this);
}
}
@@ -2838,7 +2835,6 @@ public final class ViewRootImpl implements ViewParent,
// However, windows are now always 32 bits by default, so choose 32 bits
mAttachInfo.mUse32BitDrawingCache = true;
mAttachInfo.mWindowVisibility = viewVisibility;
- mAttachInfo.mWindowSecure = (lp.flags & WindowManager.LayoutParams.FLAG_SECURE) != 0;
mAttachInfo.mRecomputeGlobalAttributes = false;
mLastConfigurationFromResources.setTo(config);
mLastSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
@@ -3099,7 +3095,8 @@ public final class ViewRootImpl implements ViewParent,
// WindowManagerService has reported back a frame from a configuration not yet
// handled by the client. In this case, we need to accept the configuration so we
// do not lay out and draw with the wrong configuration.
- if (!mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) {
+ if (mRelayoutRequested
+ && !mPendingMergedConfiguration.equals(mLastReportedMergedConfiguration)) {
if (DEBUG_CONFIGURATION) Log.v(mTag, "Visible with new config: "
+ mPendingMergedConfiguration.getMergedConfiguration());
performConfigurationChange(new MergedConfiguration(mPendingMergedConfiguration),
@@ -8146,7 +8143,8 @@ public final class ViewRootImpl implements ViewParent,
final int measuredWidth = mView.getMeasuredWidth();
final int measuredHeight = mView.getMeasuredHeight();
final boolean relayoutAsync;
- if (LOCAL_LAYOUT && !mFirst && viewVisibility == mViewVisibility
+ if (LOCAL_LAYOUT
+ && (mViewFrameInfo.flags & FrameInfo.FLAG_WINDOW_VISIBILITY_CHANGED) == 0
&& mWindowAttributes.type != TYPE_APPLICATION_STARTING
&& mSyncSeqId <= mLastSyncSeqId
&& winConfigFromAm.diff(winConfigFromWm, false /* compareUndefined */) == 0) {
@@ -8213,6 +8211,7 @@ public final class ViewRootImpl implements ViewParent,
mLastSyncSeqId, mTmpFrames, mPendingMergedConfiguration, mSurfaceControl,
mTempInsets, mTempControls, mRelayoutBundle);
mRelayoutRequested = true;
+
final int maybeSyncSeqId = mRelayoutBundle.getInt("seqid");
if (maybeSyncSeqId > 0) {
mSyncSeqId = maybeSyncSeqId;
@@ -8253,6 +8252,12 @@ public final class ViewRootImpl implements ViewParent,
}
}
+ if (mSurfaceControl.isValid() && !HardwareRenderer.isDrawingEnabled()) {
+ // When drawing is disabled the window layer won't have a valid buffer.
+ // Set a window crop so input can get delivered to the window.
+ mTransaction.setWindowCrop(mSurfaceControl, mSurfaceSize.x, mSurfaceSize.y).apply();
+ }
+
mLastTransformHint = transformHint;
mSurfaceControl.setTransformHint(transformHint);
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index d37756551db3..4a9dc5b14a71 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -19,9 +19,7 @@ package android.view;
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.res.Configuration;
@@ -524,9 +522,6 @@ public final class WindowManagerGlobal {
}
allViewsRemoved = mRoots.isEmpty();
}
- if (ThreadedRenderer.sTrimForeground) {
- doTrimForeground();
- }
// If we don't have any views anymore in our process, we no longer need the
// InsetsAnimationThread to save some resources.
@@ -543,65 +538,9 @@ public final class WindowManagerGlobal {
return index;
}
- public static boolean shouldDestroyEglContext(int trimLevel) {
- // On low-end gfx devices we trim when memory is moderate;
- // on high-end devices we do this when low.
- if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
- return true;
- }
- if (trimLevel >= ComponentCallbacks2.TRIM_MEMORY_MODERATE
- && !ActivityManager.isHighEndGfx()) {
- return true;
- }
- return false;
- }
-
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
public void trimMemory(int level) {
-
- if (shouldDestroyEglContext(level)) {
- // Destroy all hardware surfaces and resources associated to
- // known windows
- synchronized (mLock) {
- for (int i = mRoots.size() - 1; i >= 0; --i) {
- mRoots.get(i).destroyHardwareResources();
- }
- }
- // Force a full memory flush
- level = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
- }
-
ThreadedRenderer.trimMemory(level);
-
- if (ThreadedRenderer.sTrimForeground) {
- doTrimForeground();
- }
- }
-
- public static void trimForeground() {
- if (ThreadedRenderer.sTrimForeground) {
- WindowManagerGlobal wm = WindowManagerGlobal.getInstance();
- wm.doTrimForeground();
- }
- }
-
- private void doTrimForeground() {
- boolean hasVisibleWindows = false;
- synchronized (mLock) {
- for (int i = mRoots.size() - 1; i >= 0; --i) {
- final ViewRootImpl root = mRoots.get(i);
- if (root.mView != null && root.getHostVisibility() == View.VISIBLE
- && root.mAttachInfo.mThreadedRenderer != null) {
- hasVisibleWindows = true;
- } else {
- root.destroyHardwareResources();
- }
- }
- }
- if (!hasVisibleWindows) {
- ThreadedRenderer.trimMemory(
- ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
- }
}
public void dumpGfxInfo(FileDescriptor fd, String[] args) {
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index f2c8355adb84..f86f51fc0022 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -24,7 +24,6 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
-import android.widget.TextView;
import com.android.internal.util.BitUtils;
@@ -688,15 +687,27 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
/**
* Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
- * It means the content is invalid or associated with an error.
- * For example, text that sets an error message, such as when input isn't in a valid format,
- * should send this event and use {@link AccessibilityNodeInfo#setError} to
- * provide more context.
+ * The source node changed its content validity returned by
+ * {@link AccessibilityNodeInfo#isContentInvalid}.
+ * The view changing content validity should call
+ * {@link AccessibilityNodeInfo#setContentInvalid} and then send this event.
*
+ * @see AccessibilityNodeInfo#isContentInvalid
+ * @see AccessibilityNodeInfo#setContentInvalid
+ */
+ public static final int CONTENT_CHANGE_TYPE_CONTENT_INVALID = 0x0000400;
+
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
+ * The source node changed its erroneous content's error message returned by
+ * {@link AccessibilityNodeInfo#getError}.
+ * The view changing erroneous content's error message should call
+ * {@link AccessibilityNodeInfo#setError} and then send this event.
+ *
+ * @see AccessibilityNodeInfo#getError
* @see AccessibilityNodeInfo#setError
- * @see TextView#setError
*/
- public static final int CONTENT_CHANGE_TYPE_INVALID = 0x0000400;
+ public static final int CONTENT_CHANGE_TYPE_ERROR = 0x0000800;
/** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
public static final int SPEECH_STATE_SPEAKING_START = 0x00000001;
@@ -823,7 +834,8 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
CONTENT_CHANGE_TYPE_DRAG_STARTED,
CONTENT_CHANGE_TYPE_DRAG_DROPPED,
CONTENT_CHANGE_TYPE_DRAG_CANCELLED,
- CONTENT_CHANGE_TYPE_INVALID,
+ CONTENT_CHANGE_TYPE_CONTENT_INVALID,
+ CONTENT_CHANGE_TYPE_ERROR,
})
public @interface ContentChangeTypes {}
@@ -1090,7 +1102,9 @@ public final class AccessibilityEvent extends AccessibilityRecord implements Par
case CONTENT_CHANGE_TYPE_DRAG_STARTED: return "CONTENT_CHANGE_TYPE_DRAG_STARTED";
case CONTENT_CHANGE_TYPE_DRAG_DROPPED: return "CONTENT_CHANGE_TYPE_DRAG_DROPPED";
case CONTENT_CHANGE_TYPE_DRAG_CANCELLED: return "CONTENT_CHANGE_TYPE_DRAG_CANCELLED";
- case CONTENT_CHANGE_TYPE_INVALID: return "CONTENT_CHANGE_TYPE_INVALID";
+ case CONTENT_CHANGE_TYPE_CONTENT_INVALID:
+ return "CONTENT_CHANGE_TYPE_CONTENT_INVALID";
+ case CONTENT_CHANGE_TYPE_ERROR: return "CONTENT_CHANGE_TYPE_ERROR";
default: return Integer.toHexString(type);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index e89f836aaac1..7528e2a66926 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -585,6 +585,18 @@ public final class AccessibilityManager {
/**
* Returns if the accessibility in the system is enabled.
+ * <p>
+ * <b>Note:</b> This query is used for sending {@link AccessibilityEvent}s, since events are
+ * only needed if accessibility is on. Avoid changing UI or app behavior based on the state of
+ * accessibility. While well-intentioned, doing this creates brittle, less
+ * well-maintained code that works for some users but not others. Shared code leads to more
+ * equitable experiences and less technical debt.
+ *
+ *<p>
+ * For example, if you want to expose a unique interaction with your app, use
+ * ViewCompat#addAccessibilityAction in AndroidX to make this interaction - ideally
+ * with the same code path used for non-accessibility users - available to accessibility
+ * services. Services can then expose this action in the way best fit for their users.
*
* @return True if accessibility is enabled, false otherwise.
*/
@@ -597,6 +609,13 @@ public final class AccessibilityManager {
/**
* Returns if the touch exploration in the system is enabled.
+ * <p>
+ * <b>Note:</b> This query is used for dispatching hover events, such as
+ * {@link android.view.MotionEvent#ACTION_HOVER_ENTER}, to accessibility services to manage
+ * touch exploration. Avoid changing UI or app behavior based on the state of accessibility.
+ * While well-intentioned, doing this creates brittle, less well-maintained code that works for
+ * som users but not others. Shared code leads to more equitable experiences and less technical
+ * debt.
*
* @return True if touch exploration is enabled, false otherwise.
*/
diff --git a/core/java/android/view/contentcapture/MainContentCaptureSession.java b/core/java/android/view/contentcapture/MainContentCaptureSession.java
index 90384b520315..c32ca9e2e215 100644
--- a/core/java/android/view/contentcapture/MainContentCaptureSession.java
+++ b/core/java/android/view/contentcapture/MainContentCaptureSession.java
@@ -528,7 +528,12 @@ public final class MainContentCaptureSession extends ContentCaptureSession {
@Override
@UiThread
void flush(@FlushReason int reason) {
- if (mEvents == null) return;
+ if (mEvents == null || mEvents.size() == 0) {
+ if (sVerbose) {
+ Log.v(TAG, "Don't flush for empty event buffer.");
+ }
+ return;
+ }
if (mDisabled.get()) {
Log.e(TAG, "handleForceFlush(" + getDebugState(reason) + "): should not be when "
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index 624937ff1d99..3733c3f2efcc 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -20,6 +20,7 @@ import static android.view.ContentInfo.SOURCE_INPUT_METHOD;
import android.annotation.CallSuper;
import android.annotation.IntRange;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -61,8 +62,15 @@ public class BaseInputConnection implements InputConnection {
static final Object COMPOSING = new ComposingText();
/** @hide */
- protected final InputMethodManager mIMM;
- final View mTargetView;
+ @NonNull protected final InputMethodManager mIMM;
+
+ /**
+ * Target view for the input connection.
+ *
+ * <p>This could be null for a fallback input connection.
+ */
+ @Nullable final View mTargetView;
+
final boolean mFallbackMode;
private Object[] mDefaultComposingSpans;
@@ -70,20 +78,25 @@ public class BaseInputConnection implements InputConnection {
Editable mEditable;
KeyCharacterMap mKeyCharacterMap;
- BaseInputConnection(InputMethodManager mgr, boolean fullEditor) {
+ BaseInputConnection(@NonNull InputMethodManager mgr, boolean fullEditor) {
mIMM = mgr;
mTargetView = null;
mFallbackMode = !fullEditor;
}
- public BaseInputConnection(View targetView, boolean fullEditor) {
+ public BaseInputConnection(@NonNull View targetView, boolean fullEditor) {
mIMM = (InputMethodManager)targetView.getContext().getSystemService(
Context.INPUT_METHOD_SERVICE);
mTargetView = targetView;
mFallbackMode = !fullEditor;
}
- public static final void removeComposingSpans(Spannable text) {
+ /**
+ * Removes the composing spans from the given text if any.
+ *
+ * @param text the spannable text to remove composing spans
+ */
+ public static final void removeComposingSpans(@NonNull Spannable text) {
text.removeSpan(COMPOSING);
Object[] sps = text.getSpans(0, text.length(), Object.class);
if (sps != null) {
@@ -96,12 +109,17 @@ public class BaseInputConnection implements InputConnection {
}
}
- public static void setComposingSpans(Spannable text) {
+ /**
+ * Removes the composing spans from the given text if any.
+ *
+ * @param text the spannable text to remove composing spans
+ */
+ public static void setComposingSpans(@NonNull Spannable text) {
setComposingSpans(text, 0, text.length());
}
/** @hide */
- public static void setComposingSpans(Spannable text, int start, int end) {
+ public static void setComposingSpans(@NonNull Spannable text, int start, int end) {
final Object[] sps = text.getSpans(start, end, Object.class);
if (sps != null) {
for (int i=sps.length-1; i>=0; i--) {
@@ -114,7 +132,10 @@ public class BaseInputConnection implements InputConnection {
final int fl = text.getSpanFlags(o);
if ((fl & (Spanned.SPAN_COMPOSING | Spanned.SPAN_POINT_MARK_MASK))
!= (Spanned.SPAN_COMPOSING | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
- text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
+ text.setSpan(
+ o,
+ text.getSpanStart(o),
+ text.getSpanEnd(o),
(fl & ~Spanned.SPAN_POINT_MARK_MASK)
| Spanned.SPAN_COMPOSING
| Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -126,20 +147,24 @@ public class BaseInputConnection implements InputConnection {
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
}
- public static int getComposingSpanStart(Spannable text) {
+ /** Return the beginning of the range of composing text, or -1 if there's no composing text. */
+ public static int getComposingSpanStart(@NonNull Spannable text) {
return text.getSpanStart(COMPOSING);
}
- public static int getComposingSpanEnd(Spannable text) {
+ /** Return the end of the range of composing text, or -1 if there's no composing text. */
+ public static int getComposingSpanEnd(@NonNull Spannable text) {
return text.getSpanEnd(COMPOSING);
}
/**
- * Return the target of edit operations. The default implementation
- * returns its own fake editable that is just used for composing text;
- * subclasses that are real text editors should override this and
- * supply their own.
+ * Return the target of edit operations. The default implementation returns its own fake
+ * editable that is just used for composing text; subclasses that are real text editors should
+ * override this and supply their own.
+ *
+ * <p>Subclasses could override this method to turn null.
*/
+ @Nullable
public Editable getEditable() {
if (mEditable == null) {
mEditable = Editable.Factory.getInstance().newEditable("");
@@ -148,16 +173,14 @@ public class BaseInputConnection implements InputConnection {
return mEditable;
}
- /**
- * Default implementation does nothing.
- */
+ /** Default implementation does nothing. */
+ @Override
public boolean beginBatchEdit() {
return false;
}
- /**
- * Default implementation does nothing.
- */
+ /** Default implementation does nothing. */
+ @Override
public boolean endBatchEdit() {
return false;
}
@@ -165,29 +188,29 @@ public class BaseInputConnection implements InputConnection {
/**
* Called after only the composing region is modified (so it isn't called if the text also
* changes).
- * <p>
- * Default implementation does nothing.
+ *
+ * <p>Default implementation does nothing.
*
* @hide
*/
- public void endComposingRegionEditInternal() {
- }
+ public void endComposingRegionEditInternal() {}
/**
- * Default implementation calls {@link #finishComposingText()} and
- * {@code setImeConsumesInput(false)}.
+ * Default implementation calls {@link #finishComposingText()} and {@code
+ * setImeConsumesInput(false)}.
*/
@CallSuper
+ @Override
public void closeConnection() {
finishComposingText();
setImeConsumesInput(false);
}
/**
- * Default implementation uses
- * {@link MetaKeyKeyListener#clearMetaKeyState(long, int)
+ * Default implementation uses {@link MetaKeyKeyListener#clearMetaKeyState(long, int)
* MetaKeyKeyListener.clearMetaKeyState(long, int)} to clear the state.
*/
+ @Override
public boolean clearMetaKeyStates(int states) {
final Editable content = getEditable();
if (content == null) return false;
@@ -195,25 +218,24 @@ public class BaseInputConnection implements InputConnection {
return true;
}
- /**
- * Default implementation does nothing and returns false.
- */
+ /** Default implementation does nothing and returns false. */
+ @Override
public boolean commitCompletion(CompletionInfo text) {
return false;
}
- /**
- * Default implementation does nothing and returns false.
- */
+ /** Default implementation does nothing and returns false. */
+ @Override
public boolean commitCorrection(CorrectionInfo correctionInfo) {
return false;
}
/**
- * Default implementation replaces any existing composing text with
- * the given text. In addition, only if fallback mode, a key event is
- * sent for the new text and the current editable buffer cleared.
+ * Default implementation replaces any existing composing text with the given text. In addition,
+ * only if fallback mode, a key event is sent for the new text and the current editable buffer
+ * cleared.
*/
+ @Override
public boolean commitText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "commitText " + text);
replaceText(text, newCursorPosition, false);
@@ -226,21 +248,19 @@ public class BaseInputConnection implements InputConnection {
* editable text.
*
* @param beforeLength The number of characters before the cursor to be deleted, in code unit.
- * If this is greater than the number of existing characters between the beginning of the
- * text and the cursor, then this method does not fail but deletes all the characters in
- * that range.
- * @param afterLength The number of characters after the cursor to be deleted, in code unit.
- * If this is greater than the number of existing characters between the cursor and
- * the end of the text, then this method does not fail but deletes all the characters in
- * that range.
- *
- * @return {@code true} when selected text is deleted, {@code false} when either the
- * selection is invalid or not yet attached (i.e. selection start or end is -1),
- * or the editable text is {@code null}.
+ * If this is greater than the number of existing characters between the beginning of the
+ * text and the cursor, then this method does not fail but deletes all the characters in
+ * that range.
+ * @param afterLength The number of characters after the cursor to be deleted, in code unit. If
+ * this is greater than the number of existing characters between the cursor and the end of
+ * the text, then this method does not fail but deletes all the characters in that range.
+ * @return {@code true} when selected text is deleted, {@code false} when either the selection
+ * is invalid or not yet attached (i.e. selection start or end is -1), or the editable text
+ * is {@code null}.
*/
+ @Override
public boolean deleteSurroundingText(int beforeLength, int afterLength) {
- if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
- + " / " + afterLength);
+ if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength + " / " + afterLength);
final Editable content = getEditable();
if (content == null) return false;
@@ -389,7 +409,7 @@ public class BaseInputConnection implements InputConnection {
continue;
}
if (java.lang.Character.isLowSurrogate(c)) {
- return INVALID_INDEX; // A invalid surrogate pair is found.
+ return INVALID_INDEX; // A invalid surrogate pair is found.
}
waitingLowSurrogate = true;
++currentIndex;
@@ -399,18 +419,18 @@ public class BaseInputConnection implements InputConnection {
/**
* The default implementation performs the deletion around the current selection position of the
* editable text.
+ *
* @param beforeLength The number of characters before the cursor to be deleted, in code points.
- * If this is greater than the number of existing characters between the beginning of the
- * text and the cursor, then this method does not fail but deletes all the characters in
- * that range.
+ * If this is greater than the number of existing characters between the beginning of the
+ * text and the cursor, then this method does not fail but deletes all the characters in
+ * that range.
* @param afterLength The number of characters after the cursor to be deleted, in code points.
- * If this is greater than the number of existing characters between the cursor and
- * the end of the text, then this method does not fail but deletes all the characters in
- * that range.
+ * If this is greater than the number of existing characters between the cursor and the end
+ * of the text, then this method does not fail but deletes all the characters in that range.
*/
+ @Override
public boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength) {
- if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength
- + " / " + afterLength);
+ if (DEBUG) Log.v(TAG, "deleteSurroundingText " + beforeLength + " / " + afterLength);
final Editable content = getEditable();
if (content == null) return false;
@@ -466,10 +486,11 @@ public class BaseInputConnection implements InputConnection {
}
/**
- * The default implementation removes the composing state from the
- * current editable text. In addition, only if fallback mode, a key event is
- * sent for the new text and the current editable buffer cleared.
+ * The default implementation removes the composing state from the current editable text. In
+ * addition, only if fallback mode, a key event is sent for the new text and the current
+ * editable buffer cleared.
*/
+ @Override
public boolean finishComposingText() {
if (DEBUG) Log.v(TAG, "finishComposingText");
final Editable content = getEditable();
@@ -485,10 +506,11 @@ public class BaseInputConnection implements InputConnection {
}
/**
- * The default implementation uses TextUtils.getCapsMode to get the
- * cursor caps mode for the current selection position in the editable
- * text, unless in fallback mode in which case 0 is always returned.
+ * The default implementation uses TextUtils.getCapsMode to get the cursor caps mode for the
+ * current selection position in the editable text, unless in fallback mode in which case 0 is
+ * always returned.
*/
+ @Override
public int getCursorCapsMode(int reqModes) {
if (mFallbackMode) return 0;
@@ -507,17 +529,18 @@ public class BaseInputConnection implements InputConnection {
return TextUtils.getCapsMode(content, a, reqModes);
}
- /**
- * The default implementation always returns null.
- */
+ /** The default implementation always returns null. */
+ @Override
+ @Nullable
public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
return null;
}
/**
- * The default implementation returns the given amount of text from the
- * current cursor position in the buffer.
+ * The default implementation returns the given amount of text from the current cursor position
+ * in the buffer.
*/
+ @Override
@Nullable
public CharSequence getTextBeforeCursor(@IntRange(from = 0) int length, int flags) {
Preconditions.checkArgumentNonnegative(length);
@@ -549,9 +572,10 @@ public class BaseInputConnection implements InputConnection {
}
/**
- * The default implementation returns the text currently selected, or null if none is
- * selected.
+ * The default implementation returns the text currently selected, or null if none is selected.
*/
+ @Override
+ @Nullable
public CharSequence getSelectedText(int flags) {
final Editable content = getEditable();
if (content == null) return null;
@@ -574,9 +598,10 @@ public class BaseInputConnection implements InputConnection {
}
/**
- * The default implementation returns the given amount of text from the
- * current cursor position in the buffer.
+ * The default implementation returns the given amount of text from the current cursor position
+ * in the buffer.
*/
+ @Override
@Nullable
public CharSequence getTextAfterCursor(@IntRange(from = 0) int length, int flags) {
Preconditions.checkArgumentNonnegative(length);
@@ -602,7 +627,6 @@ public class BaseInputConnection implements InputConnection {
length = content.length() - b;
}
-
if ((flags&GET_TEXT_WITH_STYLES) != 0) {
return content.subSequence(b, b + length);
}
@@ -613,9 +637,10 @@ public class BaseInputConnection implements InputConnection {
* The default implementation returns the given amount of text around the current cursor
* position in the buffer.
*/
+ @Override
@Nullable
public SurroundingText getSurroundingText(
- @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
+ @IntRange(from = 0) int beforeLength, @IntRange(from = 0) int afterLength, int flags) {
Preconditions.checkArgumentNonnegative(beforeLength);
Preconditions.checkArgumentNonnegative(afterLength);
@@ -659,60 +684,75 @@ public class BaseInputConnection implements InputConnection {
surroundingText, selStart - startPos, selEnd - startPos, startPos);
}
- /**
- * The default implementation turns this into the enter key.
- */
+ /** The default implementation turns this into the enter key. */
+ @Override
public boolean performEditorAction(int actionCode) {
long eventTime = SystemClock.uptimeMillis();
- sendKeyEvent(new KeyEvent(eventTime, eventTime,
- KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
- KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
- | KeyEvent.FLAG_EDITOR_ACTION));
- sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
- KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0,
- KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
- KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE
- | KeyEvent.FLAG_EDITOR_ACTION));
+ sendKeyEvent(
+ new KeyEvent(
+ eventTime,
+ eventTime,
+ KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_ENTER,
+ 0,
+ 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD,
+ 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD
+ | KeyEvent.FLAG_KEEP_TOUCH_MODE
+ | KeyEvent.FLAG_EDITOR_ACTION));
+ sendKeyEvent(
+ new KeyEvent(
+ SystemClock.uptimeMillis(),
+ eventTime,
+ KeyEvent.ACTION_UP,
+ KeyEvent.KEYCODE_ENTER,
+ 0,
+ 0,
+ KeyCharacterMap.VIRTUAL_KEYBOARD,
+ 0,
+ KeyEvent.FLAG_SOFT_KEYBOARD
+ | KeyEvent.FLAG_KEEP_TOUCH_MODE
+ | KeyEvent.FLAG_EDITOR_ACTION));
return true;
}
- /**
- * The default implementation does nothing.
- */
+ /** The default implementation does nothing. */
+ @Override
public boolean performContextMenuAction(int id) {
return false;
}
- /**
- * The default implementation does nothing.
- */
+ /** The default implementation does nothing. */
+ @Override
public boolean performPrivateCommand(String action, Bundle data) {
return false;
}
- /**
- * The default implementation does nothing.
- */
+ /** The default implementation does nothing. */
+ @Override
public boolean requestCursorUpdates(int cursorUpdateMode) {
return false;
}
+ @Override
+ @Nullable
public Handler getHandler() {
return null;
}
/**
- * The default implementation places the given text into the editable,
- * replacing any existing composing text. The new text is marked as
- * in a composing state with the composing style.
+ * The default implementation places the given text into the editable, replacing any existing
+ * composing text. The new text is marked as in a composing state with the composing style.
*/
+ @Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "setComposingText " + text);
replaceText(text, newCursorPosition, true);
return true;
}
+ @Override
public boolean setComposingRegion(int start, int end) {
final Editable content = getEditable();
if (content != null) {
@@ -735,7 +775,10 @@ public class BaseInputConnection implements InputConnection {
ensureDefaultComposingSpans();
if (mDefaultComposingSpans != null) {
for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
- content.setSpan(mDefaultComposingSpans[i], a, b,
+ content.setSpan(
+ mDefaultComposingSpans[i],
+ a,
+ b,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
}
}
@@ -751,10 +794,8 @@ public class BaseInputConnection implements InputConnection {
return true;
}
- /**
- * The default implementation changes the selection position in the
- * current editable text.
- */
+ /** The default implementation changes the selection position in the current editable text. */
+ @Override
public boolean setSelection(int start, int end) {
if (DEBUG) Log.v(TAG, "setSelection " + start + ", " + end);
final Editable content = getEditable();
@@ -779,17 +820,17 @@ public class BaseInputConnection implements InputConnection {
}
/**
- * Provides standard implementation for sending a key event to the window
- * attached to the input connection's view.
+ * Provides standard implementation for sending a key event to the window attached to the input
+ * connection's view.
*/
+ @Override
public boolean sendKeyEvent(KeyEvent event) {
mIMM.dispatchKeyEventFromInputMethod(mTargetView, event);
return false;
}
- /**
- * Updates InputMethodManager with the current fullscreen mode.
- */
+ /** Updates InputMethodManager with the current fullscreen mode. */
+ @Override
public boolean reportFullscreenMode(boolean enabled) {
return true;
}
@@ -838,11 +879,8 @@ public class BaseInputConnection implements InputConnection {
Context context;
if (mTargetView != null) {
context = mTargetView.getContext();
- } else if (mIMM.mCurRootView != null) {
- final View servedView = mIMM.mCurRootView.getImeFocusController().getServedView();
- context = servedView != null ? servedView.getContext() : null;
} else {
- context = null;
+ context = mIMM.getFallbackContextFromServedView();
}
if (context != null) {
TypedArray ta = context.getTheme()
@@ -859,8 +897,43 @@ public class BaseInputConnection implements InputConnection {
}
}
- private void replaceText(CharSequence text, int newCursorPosition,
- boolean composing) {
+ @Override
+ public boolean replaceText(
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull CharSequence text,
+ int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ Preconditions.checkArgumentNonnegative(start);
+ Preconditions.checkArgumentNonnegative(end);
+
+ if (DEBUG) {
+ Log.v(
+ TAG,
+ "replaceText " + start + ", " + end + ", " + text + ", " + newCursorPosition);
+ }
+
+ final Editable content = getEditable();
+ if (content == null) {
+ return false;
+ }
+ beginBatchEdit();
+ removeComposingSpans(content);
+
+ int len = content.length();
+ start = Math.min(start, len);
+ end = Math.min(end, len);
+ if (end < start) {
+ int tmp = start;
+ start = end;
+ end = tmp;
+ }
+ replaceTextInternal(start, end, text, newCursorPosition, /*composing=*/ false);
+ endBatchEdit();
+ return true;
+ }
+
+ private void replaceText(CharSequence text, int newCursorPosition, boolean composing) {
final Editable content = getEditable();
if (content == null) {
return;
@@ -893,6 +966,16 @@ public class BaseInputConnection implements InputConnection {
b = tmp;
}
}
+ replaceTextInternal(a, b, text, newCursorPosition, composing);
+ endBatchEdit();
+ }
+
+ private void replaceTextInternal(
+ int a, int b, CharSequence text, int newCursorPosition, boolean composing) {
+ final Editable content = getEditable();
+ if (content == null) {
+ return;
+ }
if (composing) {
Spannable sp = null;
@@ -934,10 +1017,8 @@ public class BaseInputConnection implements InputConnection {
newCursorPosition += a;
}
if (newCursorPosition < 0) newCursorPosition = 0;
- if (newCursorPosition > content.length())
- newCursorPosition = content.length();
+ if (newCursorPosition > content.length()) newCursorPosition = content.length();
Selection.setSelection(content, newCursorPosition);
-
content.replace(a, b, text);
if (DEBUG) {
@@ -945,16 +1026,19 @@ public class BaseInputConnection implements InputConnection {
lp.println("Final text:");
TextUtils.dumpSpans(content, lp, " ");
}
-
- endBatchEdit();
}
/**
- * Default implementation which invokes {@link View#performReceiveContent} on the target
- * view if the view {@link View#getReceiveContentMimeTypes allows} content insertion;
- * otherwise returns false without any side effects.
+ * Default implementation which invokes {@link View#performReceiveContent} on the target view if
+ * the view {@link View#getReceiveContentMimeTypes allows} content insertion; otherwise returns
+ * false without any side effects.
*/
+ @Override
public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
+ if (mTargetView == null) {
+ return false;
+ }
+
ClipDescription description = inputContentInfo.getDescription();
if (mTargetView.getReceiveContentMimeTypes() == null) {
if (DEBUG) {
diff --git a/core/java/android/view/inputmethod/DeleteGesture.java b/core/java/android/view/inputmethod/DeleteGesture.java
index 9fadabfee512..c88158f3b39a 100644
--- a/core/java/android/view/inputmethod/DeleteGesture.java
+++ b/core/java/android/view/inputmethod/DeleteGesture.java
@@ -27,9 +27,11 @@ import android.widget.TextView;
import java.util.Objects;
/**
- * A sub-class of {@link HandwritingGesture} for deleting an area of text.
+ * A sub-class of {@link HandwritingGesture} for deleting an area of text using single rectangle.
* This class holds the information required for deletion of text in
* toolkit widgets like {@link TextView}.
+ * <p>Note: This deletes all text <em>within</em> the given area. To delete a range <em>between</em>
+ * two areas, use {@link DeleteRangeGesture}.</p>
*/
public final class DeleteGesture extends HandwritingGesture implements Parcelable {
diff --git a/core/java/android/app/cloudsearch/SearchResult.aidl b/core/java/android/view/inputmethod/DeleteRangeGesture.aidl
index daebfbf3307f..0ae889974fc7 100644
--- a/core/java/android/app/cloudsearch/SearchResult.aidl
+++ b/core/java/android/view/inputmethod/DeleteRangeGesture.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2022, The Android Open Source Project
+/*
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app.cloudsearch;
+package android.view.inputmethod;
-parcelable SearchResult; \ No newline at end of file
+parcelable DeleteRangeGesture; \ No newline at end of file
diff --git a/core/java/android/view/inputmethod/DeleteRangeGesture.java b/core/java/android/view/inputmethod/DeleteRangeGesture.java
new file mode 100644
index 000000000000..53b42092c181
--- /dev/null
+++ b/core/java/android/view/inputmethod/DeleteRangeGesture.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.widget.TextView;
+
+import java.util.Objects;
+
+/**
+ * A subclass of {@link HandwritingGesture} for deleting a range of text by defining start and end
+ * rectangles. This can be useful when the range cannot be defined with a single rectangle.
+ * This class holds the information required for deletion of text in
+ * toolkit widgets like {@link TextView}.
+ * <p>Note: this deletes text within a range <em>between</em> two given areas. To delete all text
+ * <em>within</em> a single area, use {@link DeleteGesture}.</p>
+ */
+public final class DeleteRangeGesture extends HandwritingGesture implements Parcelable {
+
+ private @Granularity int mGranularity;
+ private RectF mStartArea;
+ private RectF mEndArea;
+
+ private DeleteRangeGesture(
+ int granularity, RectF startArea, RectF endArea, String fallbackText) {
+ mType = GESTURE_TYPE_DELETE_RANGE;
+ mStartArea = startArea;
+ mEndArea = endArea;
+ mGranularity = granularity;
+ mFallbackText = fallbackText;
+ }
+
+ private DeleteRangeGesture(@NonNull Parcel source) {
+ mType = GESTURE_TYPE_DELETE_RANGE;
+ mFallbackText = source.readString8();
+ mGranularity = source.readInt();
+ mStartArea = source.readTypedObject(RectF.CREATOR);
+ mEndArea = source.readTypedObject(RectF.CREATOR);
+ }
+
+ /**
+ * Returns Granular level on which text should be operated.
+ * @see #GRANULARITY_CHARACTER
+ * @see #GRANULARITY_WORD
+ */
+ @Granularity
+ public int getGranularity() {
+ return mGranularity;
+ }
+
+ /**
+ * Returns the Deletion start area {@link RectF} in screen coordinates.
+ *
+ * Getter for deletion area set with {@link Builder#setDeletionStartArea(RectF)}.
+ */
+ @NonNull
+ public RectF getDeletionStartArea() {
+ return mStartArea;
+ }
+
+ /**
+ * Returns the Deletion end area {@link RectF} in screen coordinates.
+ *
+ * Getter for deletion area set with {@link Builder#setDeletionEndArea(RectF)}.
+ */
+ @NonNull
+ public RectF getDeletionEndArea() {
+ return mEndArea;
+ }
+
+ /**
+ * Builder for {@link DeleteRangeGesture}. This class is not designed to be thread-safe.
+ */
+ public static final class Builder {
+ private int mGranularity;
+ private RectF mStartArea;
+ private RectF mEndArea;
+ private String mFallbackText;
+
+ /**
+ * Define text deletion granularity. Intersecting words/characters will be
+ * included in the operation.
+ * @param granularity {@link HandwritingGesture#GRANULARITY_WORD} or
+ * {@link HandwritingGesture#GRANULARITY_CHARACTER}.
+ * @return {@link Builder}.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setGranularity(@Granularity int granularity) {
+ mGranularity = granularity;
+ return this;
+ }
+
+ /**
+ * Set rectangular single/multiline start of text deletion area intersecting with text.
+ *
+ * The resulting deletion is performed from the start of first word/character in the start
+ * rectangle to the end of the last word/character in the end rectangle
+ * {@link #setDeletionEndArea(RectF)}.
+ * <br/>
+ * <img src="{@docRoot}reference/android/images/input_method/stylus_handwriting
+ * /delete_range_gesture_rects.png"
+ * height="300" alt="Deletion strategy using two rectangles"/>
+ * <br/>
+ *
+ * Intersection is determined using
+ * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes
+ * all the words with their width/height center included in the deletion rectangle.
+ * @param startArea {@link RectF} (in screen coordinates) for start of deletion. This
+ * rectangle belongs to first line where deletion should start.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setDeletionStartArea(@NonNull RectF startArea) {
+ mStartArea = startArea;
+ return this;
+ }
+
+ /**
+ * Set rectangular single/multiline end of text deletion area intersecting with text.
+ *
+ * The resulting deletion is performed from the start of first word/character in the start
+ * rectangle {@link #setDeletionStartArea(RectF)} to the end of the last word/character in
+ * the end rectangle.
+ * <br/>
+ * <img src="{@docRoot}reference/android/images/input_method/stylus_handwriting
+ * /delete_range_gesture_rects.png"
+ * height="300" alt="Deletion strategy using two rectangles"/>
+ *
+ * Intersection is determined using
+ * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes
+ * all the words with their width/height center included in the deletion rectangle.
+ * @param endArea {@link RectF} (in screen coordinates) for start of deletion. This
+ * rectangle belongs to the last line where deletion should end.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setDeletionEndArea(@NonNull RectF endArea) {
+ mEndArea = endArea;
+ return this;
+ }
+
+ /**
+ * Set fallback text that will be committed at current cursor position if there is no
+ * applicable text beneath the area of gesture.
+ * @param fallbackText text to set
+ */
+ @NonNull
+ public Builder setFallbackText(@Nullable String fallbackText) {
+ mFallbackText = fallbackText;
+ return this;
+ }
+
+ /**
+ * @return {@link DeleteRangeGesture} using parameters in this
+ * {@link DeleteRangeGesture.Builder}.
+ * @throws IllegalArgumentException if one or more positional parameters are not specified.
+ */
+ @NonNull
+ public DeleteRangeGesture build() {
+ if (mStartArea == null || mStartArea.isEmpty() || mEndArea == null
+ || mEndArea.isEmpty()) {
+ throw new IllegalArgumentException("Deletion area must be set.");
+ }
+ if (mGranularity <= GRANULARITY_UNDEFINED) {
+ throw new IllegalArgumentException("Deletion granularity must be set.");
+ }
+ return new DeleteRangeGesture(mGranularity, mStartArea, mEndArea, mFallbackText);
+ }
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ @NonNull
+ public static final Creator<DeleteRangeGesture> CREATOR =
+ new Creator<DeleteRangeGesture>() {
+ @Override
+ public DeleteRangeGesture createFromParcel(Parcel source) {
+ return new DeleteRangeGesture(source);
+ }
+
+ @Override
+ public DeleteRangeGesture[] newArray(int size) {
+ return new DeleteRangeGesture[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGranularity, mStartArea, mEndArea, mFallbackText);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof DeleteRangeGesture)) return false;
+
+ DeleteRangeGesture that = (DeleteRangeGesture) o;
+
+ if (mGranularity != that.mGranularity) return false;
+ if (!Objects.equals(mFallbackText, that.mFallbackText)) return false;
+ if (!Objects.equals(mStartArea, that.mStartArea)) return false;
+ return Objects.equals(mEndArea, that.mEndArea);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mFallbackText);
+ dest.writeInt(mGranularity);
+ dest.writeTypedObject(mStartArea, flags);
+ dest.writeTypedObject(mEndArea, flags);
+ }
+}
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index c3b32c9f1d54..4a79ba62de69 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -557,10 +557,14 @@ public class EditorInfo implements InputType, Parcelable {
Objects.requireNonNull(gesture);
if (gesture.equals(SelectGesture.class)) {
supportedTypes |= HandwritingGesture.GESTURE_TYPE_SELECT;
+ } else if (gesture.equals(SelectRangeGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_SELECT_RANGE;
} else if (gesture.equals(InsertGesture.class)) {
supportedTypes |= HandwritingGesture.GESTURE_TYPE_INSERT;
} else if (gesture.equals(DeleteGesture.class)) {
supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE;
+ } else if (gesture.equals(DeleteRangeGesture.class)) {
+ supportedTypes |= HandwritingGesture.GESTURE_TYPE_DELETE_RANGE;
} else if (gesture.equals(RemoveSpaceGesture.class)) {
supportedTypes |= HandwritingGesture.GESTURE_TYPE_REMOVE_SPACE;
} else if (gesture.equals(JoinOrSplitGesture.class)) {
diff --git a/core/java/android/view/inputmethod/HandwritingGesture.java b/core/java/android/view/inputmethod/HandwritingGesture.java
index 494aaaa32f57..07b1e1f0468e 100644
--- a/core/java/android/view/inputmethod/HandwritingGesture.java
+++ b/core/java/android/view/inputmethod/HandwritingGesture.java
@@ -25,6 +25,7 @@ import android.view.MotionEvent;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.IntConsumer;
@@ -106,14 +107,26 @@ public abstract class HandwritingGesture {
public static final int GESTURE_TYPE_JOIN_OR_SPLIT = 1 << 4;
/**
+ * Gesture of type {@link SelectRangeGesture} to select range of text.
+ */
+ public static final int GESTURE_TYPE_SELECT_RANGE = 1 << 5;
+
+ /**
+ * Gesture of type {@link DeleteRangeGesture} to delete range of text.
+ */
+ public static final int GESTURE_TYPE_DELETE_RANGE = 1 << 6;
+
+ /**
* Type of gesture like {@link #GESTURE_TYPE_SELECT}, {@link #GESTURE_TYPE_INSERT},
* or {@link #GESTURE_TYPE_DELETE}.
*/
@IntDef(prefix = {"GESTURE_TYPE_"}, value = {
GESTURE_TYPE_NONE,
GESTURE_TYPE_SELECT,
+ GESTURE_TYPE_SELECT_RANGE,
GESTURE_TYPE_INSERT,
GESTURE_TYPE_DELETE,
+ GESTURE_TYPE_DELETE_RANGE,
GESTURE_TYPE_REMOVE_SPACE,
GESTURE_TYPE_JOIN_OR_SPLIT})
@Retention(RetentionPolicy.SOURCE)
@@ -123,13 +136,15 @@ public abstract class HandwritingGesture {
* Flags which can be any combination of {@link #GESTURE_TYPE_SELECT},
* {@link #GESTURE_TYPE_INSERT}, or {@link #GESTURE_TYPE_DELETE}.
* {@link GestureTypeFlags} can be used by editors to declare what gestures are supported
- * and report them in {@link EditorInfo#setSupportedHandwritingGestureTypes(int)}.
+ * and report them in {@link EditorInfo#setSupportedHandwritingGestures(List)}.
* @hide
*/
@IntDef(flag = true, prefix = {"GESTURE_TYPE_"}, value = {
GESTURE_TYPE_SELECT,
+ GESTURE_TYPE_SELECT_RANGE,
GESTURE_TYPE_INSERT,
GESTURE_TYPE_DELETE,
+ GESTURE_TYPE_DELETE_RANGE,
GESTURE_TYPE_REMOVE_SPACE,
GESTURE_TYPE_JOIN_OR_SPLIT})
@Retention(RetentionPolicy.SOURCE)
@@ -140,7 +155,7 @@ public abstract class HandwritingGesture {
/**
* Returns the gesture type {@link GestureType}.
* {@link GestureType} can be used by editors to declare what gestures are supported and report
- * them in {@link EditorInfo#setSupportedHandwritingGestureTypes(int)}.
+ * them in {@link EditorInfo#setSupportedHandwritingGestures(List)}.
* @hide
*/
@TestApi
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 2f834c9e819f..7d268a925f60 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1329,4 +1329,44 @@ public interface InputConnection {
// existing APIs.
return null;
}
+
+ /**
+ * Replace the specific range in the editor with suggested text.
+ *
+ * <p>This method finishes whatever composing text is currently active and leaves the text
+ * as-it, replaces the specific range of text with the passed CharSequence, and then moves the
+ * cursor according to {@code newCursorPosition}. This behaves like calling {@link
+ * #finishComposingText()}, {@link #setSelection(int, int) setSelection(start, end)}, and then
+ * {@link #commitText(CharSequence, int, TextAttribute) commitText(text, newCursorPosition,
+ * textAttribute)}.
+ *
+ * <p>Similar to {@link #setSelection(int, int)}, the order of start and end is not important.
+ * In effect, the region from start to end and the region from end to start is the same. Editor
+ * authors, be ready to accept a start that is greater than end.
+ *
+ * @param start the character index where the replacement should start.
+ * @param end the character index where the replacement should end.
+ * @param newCursorPosition the new cursor position around the text. If > 0, this is relative to
+ * the end of the text - 1; if <= 0, this is relative to the start of the text. So a value
+ * of 1 will always advance you to the position after the full text being inserted. Note
+ * that this means you can't position the cursor within the text.
+ * @param text the text to replace. This may include styles.
+ * @param textAttribute The extra information about the text. This value may be null.
+ */
+ default boolean replaceText(
+ @IntRange(from = 0) int start,
+ @IntRange(from = 0) int end,
+ @NonNull CharSequence text,
+ int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ Preconditions.checkArgumentNonnegative(start);
+ Preconditions.checkArgumentNonnegative(end);
+
+ beginBatchEdit();
+ finishComposingText();
+ setSelection(start, end);
+ commitText(text, newCursorPosition, textAttribute);
+ endBatchEdit();
+ return true;
+ }
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 0c7c1639e6c7..7794b7c90303 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -677,6 +677,21 @@ public final class InputMethodManager {
return fallbackImm;
}
+ /**
+ * An internal API that returns the {@link Context} of the current served view connected to
+ * an input method.
+ * @hide
+ */
+ Context getFallbackContextFromServedView() {
+ synchronized (mH) {
+ if (mCurRootView == null) {
+ return null;
+ }
+ final View servedView = mCurRootView.getImeFocusController().getServedViewLocked();
+ return servedView != null ? servedView.getContext() : null;
+ }
+ }
+
private static boolean canStartInput(View servedView) {
// We can start input ether the servedView has window focus
// or the activity is showing autofill ui.
@@ -756,10 +771,10 @@ public final class InputMethodManager {
}
/**
- * For {@link ImeFocusController} to start input asynchronously when focus gain.
+ * For {@link ImeFocusController} to start input when gaining the window focus.
*/
@Override
- public void startInputAsyncOnWindowFocusGain(View focusedView,
+ public void startInputOnWindowFocusGain(View focusedView,
@SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) {
int startInputFlags = getStartInputFlags(focusedView, 0);
startInputFlags |= StartInputFlags.WINDOW_GAINED_FOCUS;
@@ -772,6 +787,15 @@ public final class InputMethodManager {
if (controller == null) {
return;
}
+
+ synchronized (mH) {
+ if (mRestartOnNextWindowFocus) {
+ if (DEBUG) Log.v(TAG, "Restarting due to mRestartOnNextWindowFocus as true");
+ mRestartOnNextWindowFocus = false;
+ forceNewFocus = true;
+ }
+ }
+
if (controller.checkFocus(forceNewFocus, false)) {
// We need to restart input on the current focus view. This
// should be done in conjunction with telling the system service
@@ -786,9 +810,9 @@ public final class InputMethodManager {
synchronized (mH) {
// For some reason we didn't do a startInput + windowFocusGain, so
// we'll just do a window focus gain and call it a day.
- View servedView = controller.getServedView();
+ View servedView = controller.getServedViewLocked();
boolean nextFocusHasConnection = servedView != null && servedView == focusedView
- && hasActiveConnection(focusedView);
+ && hasActiveInputConnectionInternal(focusedView);
if (DEBUG) {
Log.v(TAG, "Reporting focus gain, without startInput"
+ ", nextFocusIsServedView=" + nextFocusHasConnection);
@@ -845,38 +869,23 @@ public final class InputMethodManager {
}
/**
- * For {@link ImeFocusController#checkFocus} if needed to force check new focus.
+ * Checks whether the active input connection (if any) is for the given view.
+ *
+ * @see #hasActiveInputConnectionInternal(View)}
*/
@Override
- public boolean isRestartOnNextWindowFocus(boolean reset) {
- synchronized (mH) {
- final boolean result = mRestartOnNextWindowFocus;
- if (reset) {
- mRestartOnNextWindowFocus = false;
- }
- return result;
- }
+ public boolean hasActiveConnection(View view) {
+ return hasActiveInputConnectionInternal(view);
}
/**
- * Checks whether the active input connection (if any) is for the given view.
- *
- * TODO(b/182259171): Clean-up hasActiveConnection to simplify the logic.
- *
- * Note that this method is only intended for restarting input after focus gain
- * (e.g. b/160391516), DO NOT leverage this method to do another check.
+ * Returns the {@link InputMethodManager#mH} lock object.
+ * Used for {@link ImeFocusController} to guard the served view being accessed by
+ * {@link InputMethodManager} in different threads.
*/
@Override
- public boolean hasActiveConnection(View view) {
- synchronized (mH) {
- if (!hasServedByInputMethodLocked(view) || !isImeSessionAvailableLocked()) {
- return false;
- }
-
- return mServedInputConnection != null
- && mServedInputConnection.isActive()
- && mServedInputConnection.getServedView() == view;
- }
+ public Object getLockObject() {
+ return mH;
}
}
@@ -889,36 +898,43 @@ public final class InputMethodManager {
* Checks whether the active input connection (if any) is for the given view.
*
* @hide
- * @see ImeFocusController#getImmDelegate()#hasActiveInputConnection(View)
+ * @see #hasActiveInputConnectionInternal(View)}
*/
@TestApi
public boolean hasActiveInputConnection(@Nullable View view) {
- return mDelegate.hasActiveConnection(view);
+ return hasActiveInputConnectionInternal(view);
}
- @GuardedBy("mH")
- private View getServedViewLocked() {
- return mCurRootView != null ? mCurRootView.getImeFocusController().getServedView() : null;
- }
+ /**
+ * Checks whether the active input connection (if any) is for the given view.
+ *
+ * TODO(b/182259171): Clean-up hasActiveConnection to simplify the logic.
+ *
+ * Note that this method is only intended for restarting input after focus gain
+ * (e.g. b/160391516), DO NOT leverage this method to do another check.
+ */
+ private boolean hasActiveInputConnectionInternal(@Nullable View view) {
+ synchronized (mH) {
+ if (!hasServedByInputMethodLocked(view) || !isImeSessionAvailableLocked()) {
+ return false;
+ }
- @GuardedBy("mH")
- private View getNextServedViewLocked() {
- return mCurRootView != null ? mCurRootView.getImeFocusController().getNextServedView()
- : null;
+ return mServedInputConnection != null
+ && mServedInputConnection.isActive()
+ && mServedInputConnection.getServedView() == view;
+ }
}
@GuardedBy("mH")
- private void setServedViewLocked(View view) {
- if (mCurRootView != null) {
- mCurRootView.getImeFocusController().setServedView(view);
- }
+ private View getServedViewLocked() {
+ return mCurRootView != null ? mCurRootView.getImeFocusController().getServedViewLocked()
+ : null;
}
@GuardedBy("mH")
- private void setNextServedViewLocked(View view) {
- if (mCurRootView != null) {
- mCurRootView.getImeFocusController().setNextServedView(view);
- }
+ private View getNextServedViewLocked() {
+ return mCurRootView != null ? mCurRootView.getImeFocusController().getNextServedViewLocked()
+ : null;
}
private ImeFocusController getFocusController() {
@@ -1767,13 +1783,13 @@ public final class InputMethodManager {
@GuardedBy("mH")
void finishInputLocked() {
mVirtualDisplayToScreenMatrix = null;
- setNextServedViewLocked(null);
- if (getServedViewLocked() != null) {
+ final ImeFocusController controller = getFocusController();
+ final View clearedView = controller != null ? controller.clearServedViewsLocked() : null;
+ if (clearedView != null) {
if (DEBUG) {
Log.v(TAG, "FINISH INPUT: mServedView="
- + InputMethodDebug.dumpViewInfo(getServedViewLocked()));
+ + InputMethodDebug.dumpViewInfo(clearedView));
}
- setServedViewLocked(null);
mCompletions = null;
mServedConnecting = false;
clearConnectionLocked();
diff --git a/core/java/android/view/inputmethod/InsertGesture.java b/core/java/android/view/inputmethod/InsertGesture.java
index 8b8359ea5cb2..9f0328909190 100644
--- a/core/java/android/view/inputmethod/InsertGesture.java
+++ b/core/java/android/view/inputmethod/InsertGesture.java
@@ -53,7 +53,7 @@ public final class InsertGesture extends HandwritingGesture implements Parcelabl
}
/** Returns the text that will be inserted at {@link #getInsertionPoint()} **/
- @Nullable
+ @NonNull
public String getTextToInsert() {
return mTextToInsert;
}
@@ -62,7 +62,7 @@ public final class InsertGesture extends HandwritingGesture implements Parcelabl
* Returns the insertion point {@link PointF} (in screen coordinates) where
* {@link #getTextToInsert()} will be inserted.
*/
- @Nullable
+ @NonNull
public PointF getInsertionPoint() {
return mPoint;
}
diff --git a/core/java/android/view/inputmethod/SelectGesture.java b/core/java/android/view/inputmethod/SelectGesture.java
index 2f02e66d4295..6dc4ed295669 100644
--- a/core/java/android/view/inputmethod/SelectGesture.java
+++ b/core/java/android/view/inputmethod/SelectGesture.java
@@ -27,9 +27,11 @@ import android.widget.TextView;
import java.util.Objects;
/**
- * A sub-class of {@link HandwritingGesture} for selecting an area of text.
+ * A sub-class of {@link HandwritingGesture} for selecting an area of text using single rectangle.
* This class holds the information required for selection of text in
* toolkit widgets like {@link TextView}.
+ * <p>Note: This selects all text <em>within</em> the given area. To select a range <em>between</em>
+ * two areas, use {@link SelectRangeGesture}.</p>
*/
public final class SelectGesture extends HandwritingGesture implements Parcelable {
diff --git a/core/java/android/app/cloudsearch/SearchRequest.aidl b/core/java/android/view/inputmethod/SelectRangeGesture.aidl
index 9f2cdb888bdf..a7bce0adddf0 100644
--- a/core/java/android/app/cloudsearch/SearchRequest.aidl
+++ b/core/java/android/view/inputmethod/SelectRangeGesture.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2022, The Android Open Source Project
+/*
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app.cloudsearch;
+package android.view.inputmethod;
-parcelable SearchRequest;
+parcelable SelectRangeGesture; \ No newline at end of file
diff --git a/core/java/android/view/inputmethod/SelectRangeGesture.java b/core/java/android/view/inputmethod/SelectRangeGesture.java
new file mode 100644
index 000000000000..7cb60023bd31
--- /dev/null
+++ b/core/java/android/view/inputmethod/SelectRangeGesture.java
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inputmethod;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SuppressLint;
+import android.graphics.RectF;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.widget.TextView;
+
+import java.util.Objects;
+
+/**
+ * A subclass of {@link HandwritingGesture} for selecting a range of text by defining start and end
+ * rectangles. This can be useful when the range cannot be defined with a single rectangle.
+ * This class holds the information required for selection of text in
+ * toolkit widgets like {@link TextView}.
+ * <p>Note: this selects text within a range <em>between</em> two given areas. To select all text
+ * <em>within</em> a single area, use {@link SelectGesture}</p>
+ */
+public final class SelectRangeGesture extends HandwritingGesture implements Parcelable {
+
+ private @Granularity int mGranularity;
+ private RectF mStartArea;
+ private RectF mEndArea;
+
+ private SelectRangeGesture(
+ int granularity, RectF startArea, RectF endArea, String fallbackText) {
+ mType = GESTURE_TYPE_SELECT_RANGE;
+ mStartArea = startArea;
+ mEndArea = endArea;
+ mGranularity = granularity;
+ mFallbackText = fallbackText;
+ }
+
+ private SelectRangeGesture(@NonNull Parcel source) {
+ mType = GESTURE_TYPE_SELECT_RANGE;
+ mFallbackText = source.readString8();
+ mGranularity = source.readInt();
+ mStartArea = source.readTypedObject(RectF.CREATOR);
+ mEndArea = source.readTypedObject(RectF.CREATOR);
+ }
+
+ /**
+ * Returns Granular level on which text should be operated.
+ * @see #GRANULARITY_CHARACTER
+ * @see #GRANULARITY_WORD
+ */
+ @Granularity
+ public int getGranularity() {
+ return mGranularity;
+ }
+
+ /**
+ * Returns the Selection start area {@link RectF} in screen coordinates.
+ *
+ * Getter for selection area set with {@link Builder#setSelectionStartArea(RectF)}.
+ */
+ @NonNull
+ public RectF getSelectionStartArea() {
+ return mStartArea;
+ }
+
+ /**
+ * Returns the Selection end area {@link RectF} in screen coordinates.
+ *
+ * Getter for selection area set with {@link Builder#setSelectionEndArea(RectF)}.
+ */
+ @NonNull
+ public RectF getSelectionEndArea() {
+ return mEndArea;
+ }
+
+
+ /**
+ * Builder for {@link SelectRangeGesture}. This class is not designed to be thread-safe.
+ */
+ public static final class Builder {
+ private int mGranularity;
+ private RectF mStartArea;
+ private RectF mEndArea;
+ private String mFallbackText;
+
+ /**
+ * Define text selection granularity. Intersecting words/characters will be
+ * included in the operation.
+ * @param granularity {@link HandwritingGesture#GRANULARITY_WORD} or
+ * {@link HandwritingGesture#GRANULARITY_CHARACTER}.
+ * @return {@link Builder}.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setGranularity(@HandwritingGesture.Granularity int granularity) {
+ mGranularity = granularity;
+ return this;
+ }
+
+ /**
+ * Set rectangular single/multiline start of text selection area intersecting with text.
+ *
+ * The resulting selection is performed from the start of first word/character in the start
+ * rectangle to the end of the last word/character in the end rectangle
+ * {@link #setSelectionEndArea(RectF)}.
+ * <br/>
+ * <img src="{@docRoot}reference/android/images/input_method/stylus_handwriting
+ * /select_range_gesture_rects.png"
+ * height="300" alt="Selection strategy using two rectangles"/>
+ * <br/>
+ *
+ * Intersection is determined using
+ * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes
+ * all the words with their width/height center included in the selection rectangle.
+ * @param startArea {@link RectF} (in screen coordinates) for start of selection. This
+ * rectangle belongs to first line where selection should start.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setSelectionStartArea(@NonNull RectF startArea) {
+ mStartArea = startArea;
+ return this;
+ }
+
+ /**
+ * Set rectangular single/multiline end of text selection area intersecting with text.
+ *
+ * The resulting selection is performed from the start of first word/character in the start
+ * rectangle {@link #setSelectionStartArea(RectF)} to the end of the last word/character in
+ * the end rectangle.
+ * <br/>
+ * <img src="{@docRoot}reference/android/images/input_method/stylus_handwriting
+ * /select_range_gesture_rects.png"
+ * height="300" alt="Selection strategy using two rectangles"/>
+ * <br/>
+ *
+ * The selection includes the first word/character in the rectangle, the last
+ * word/character in the rectangle, and everything in between even if it's not in the
+ * rectangle.
+ *
+ * Intersection is determined using
+ * {@link #setGranularity(int)}. e.g. {@link HandwritingGesture#GRANULARITY_WORD} includes
+ * all the words with their width/height center included in the selection rectangle.
+ * @param endArea {@link RectF} (in screen coordinates) for start of selection. This
+ * rectangle belongs to the last line where selection should end.
+ */
+ @NonNull
+ @SuppressLint("MissingGetterMatchingBuilder")
+ public Builder setSelectionEndArea(@NonNull RectF endArea) {
+ mEndArea = endArea;
+ return this;
+ }
+
+ /**
+ * Set fallback text that will be committed at current cursor position if there is no
+ * applicable text beneath the area of gesture.
+ * @param fallbackText text to set
+ */
+ @NonNull
+ public Builder setFallbackText(@Nullable String fallbackText) {
+ mFallbackText = fallbackText;
+ return this;
+ }
+
+ /**
+ * @return {@link SelectRangeGesture} using parameters in this
+ * {@link SelectRangeGesture.Builder}.
+ * @throws IllegalArgumentException if one or more positional parameters are not specified.
+ */
+ @NonNull
+ public SelectRangeGesture build() {
+ if (mStartArea == null || mStartArea.isEmpty() || mEndArea == null
+ || mEndArea.isEmpty()) {
+ throw new IllegalArgumentException("Selection area must be set.");
+ }
+ if (mGranularity <= GRANULARITY_UNDEFINED) {
+ throw new IllegalArgumentException("Selection granularity must be set.");
+ }
+ return new SelectRangeGesture(mGranularity, mStartArea, mEndArea, mFallbackText);
+ }
+ }
+
+ /**
+ * Used to make this class parcelable.
+ */
+ @NonNull
+ public static final Parcelable.Creator<SelectRangeGesture> CREATOR =
+ new Parcelable.Creator<SelectRangeGesture>() {
+ @Override
+ public SelectRangeGesture createFromParcel(Parcel source) {
+ return new SelectRangeGesture(source);
+ }
+
+ @Override
+ public SelectRangeGesture[] newArray(int size) {
+ return new SelectRangeGesture[size];
+ }
+ };
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mGranularity, mStartArea, mEndArea, mFallbackText);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof SelectRangeGesture)) return false;
+
+ SelectRangeGesture that = (SelectRangeGesture) o;
+
+ if (mGranularity != that.mGranularity) return false;
+ if (!Objects.equals(mFallbackText, that.mFallbackText)) return false;
+ if (!Objects.equals(mStartArea, that.mStartArea)) return false;
+ return Objects.equals(mEndArea, that.mEndArea);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Used to package this object into a {@link Parcel}.
+ *
+ * @param dest The {@link Parcel} to be written.
+ * @param flags The flags used for parceling.
+ */
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(mFallbackText);
+ dest.writeInt(mGranularity);
+ dest.writeTypedObject(mStartArea, flags);
+ dest.writeTypedObject(mEndArea, flags);
+ }
+}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 6bf2474beb17..514df59f1989 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -175,10 +175,7 @@ public class UiTranslationController implements Dumpable {
*/
public void onActivityDestroyed() {
synchronized (mLock) {
- if (DEBUG) {
- Log.i(TAG,
- "onActivityDestroyed(): mCurrentState is " + stateToString(mCurrentState));
- }
+ Log.i(TAG, "onActivityDestroyed(): mCurrentState is " + stateToString(mCurrentState));
if (mCurrentState != STATE_UI_TRANSLATION_FINISHED) {
notifyTranslationFinished(/* activityDestroyed= */ true);
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index cebaa76e7406..8f590f89fa64 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -572,8 +572,8 @@ public class Editor {
final Layout layout = mTextView.getLayout();
final int line = layout.getLineForOffset(mTextView.getSelectionStart());
- final int sourceHeight =
- layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line);
+ final int sourceHeight = layout.getLineBottom(line, /* includeLineSpacing= */ false)
+ - layout.getLineTop(line);
final int height = (int)(sourceHeight * zoom);
final int width = (int)(aspectRatio * Math.max(sourceHeight, mMinLineHeightForMagnifier));
@@ -2340,7 +2340,7 @@ public class Editor {
final int offset = mTextView.getSelectionStart();
final int line = layout.getLineForOffset(offset);
final int top = layout.getLineTop(line);
- final int bottom = layout.getLineBottomWithoutSpacing(line);
+ final int bottom = layout.getLineBottom(line, /* includeLineSpacing= */ false);
final boolean clamped = layout.shouldClampCursor(line);
updateCursorPosition(top, bottom, layout.getPrimaryHorizontal(offset, clamped));
@@ -2875,6 +2875,17 @@ public class Editor {
}
}
+ /**
+ *
+ * @return whether the Blink runnable is blinking or not, if null return false.
+ * @hide
+ */
+ @VisibleForTesting
+ public boolean isBlinking() {
+ if (mBlink == null) return false;
+ return !mBlink.mCancelled;
+ }
+
private class Blink implements Runnable {
private boolean mCancelled;
@@ -3432,7 +3443,7 @@ public class Editor {
@Override
protected int getVerticalLocalPosition(int line) {
final Layout layout = mTextView.getLayout();
- return layout.getLineBottomWithoutSpacing(line);
+ return layout.getLineBottom(line, /* includeLineSpacing= */ false);
}
@Override
@@ -4098,7 +4109,8 @@ public class Editor {
@Override
protected int getVerticalLocalPosition(int line) {
final Layout layout = mTextView.getLayout();
- return layout.getLineBottomWithoutSpacing(line) - mContainerMarginTop;
+ return layout.getLineBottom(line, /* includeLineSpacing= */ false)
+ - mContainerMarginTop;
}
@Override
@@ -4583,7 +4595,6 @@ public class Editor {
*/
private final class CursorAnchorInfoNotifier implements TextViewPositionListener {
final CursorAnchorInfo.Builder mSelectionInfoBuilder = new CursorAnchorInfo.Builder();
- final int[] mTmpIntOffset = new int[2];
final Matrix mViewToScreenMatrix = new Matrix();
@Override
@@ -4635,9 +4646,8 @@ public class Editor {
builder.setSelectionRange(selectionStart, mTextView.getSelectionEnd());
// Construct transformation matrix from view local coordinates to screen coordinates.
- mViewToScreenMatrix.set(mTextView.getMatrix());
- mTextView.getLocationOnScreen(mTmpIntOffset);
- mViewToScreenMatrix.postTranslate(mTmpIntOffset[0], mTmpIntOffset[1]);
+ mViewToScreenMatrix.reset();
+ mTextView.transformMatrixToGlobal(mViewToScreenMatrix);
builder.setMatrix(mViewToScreenMatrix);
if (includeEditorBounds) {
@@ -4697,8 +4707,9 @@ public class Editor {
+ viewportToContentVerticalOffset;
final float insertionMarkerBaseline = layout.getLineBaseline(line)
+ viewportToContentVerticalOffset;
- final float insertionMarkerBottom = layout.getLineBottomWithoutSpacing(line)
- + viewportToContentVerticalOffset;
+ final float insertionMarkerBottom =
+ layout.getLineBottom(line, /* includeLineSpacing= */ false)
+ + viewportToContentVerticalOffset;
final boolean isTopVisible = mTextView
.isPositionVisible(insertionMarkerX, insertionMarkerTop);
final boolean isBottomVisible = mTextView
@@ -5128,7 +5139,7 @@ public class Editor {
mPositionX = getCursorHorizontalPosition(layout, offset) - mHotspotX
- getHorizontalOffset() + getCursorOffset();
- mPositionY = layout.getLineBottomWithoutSpacing(line);
+ mPositionY = layout.getLineBottom(line, /* includeLineSpacing= */ false);
// Take TextView's padding and scroll into account.
mPositionX += mTextView.viewportToContentHorizontalOffset();
@@ -5224,8 +5235,8 @@ public class Editor {
if (mNewMagnifierEnabled) {
Layout layout = mTextView.getLayout();
final int line = layout.getLineForOffset(getCurrentCursorOffset());
- return layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line)
- >= mMaxLineHeightForMagnifier;
+ return layout.getLineBottom(line, /* includeLineSpacing= */ false)
+ - layout.getLineTop(line) >= mMaxLineHeightForMagnifier;
}
final float magnifierContentHeight = Math.round(
mMagnifierAnimator.mMagnifier.getHeight()
@@ -5380,7 +5391,8 @@ public class Editor {
// Vertically snap to middle of current line.
showPosInView.y = ((mTextView.getLayout().getLineTop(lineNumber)
- + mTextView.getLayout().getLineBottomWithoutSpacing(lineNumber)) / 2.0f
+ + mTextView.getLayout()
+ .getLineBottom(lineNumber, /* includeLineSpacing= */ false)) / 2.0f
+ mTextView.getTotalPaddingTop() - mTextView.getScrollY()) * mTextViewScaleY;
return true;
}
@@ -5464,7 +5476,8 @@ public class Editor {
updateCursorPosition();
}
final int lineHeight =
- layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line);
+ layout.getLineBottom(line, /* includeLineSpacing= */ false)
+ - layout.getLineTop(line);
float zoom = mInitialZoom;
if (lineHeight < mMinLineHeightForMagnifier) {
zoom = zoom * mMinLineHeightForMagnifier / lineHeight;
@@ -5814,8 +5827,8 @@ public class Editor {
private MotionEvent transformEventForTouchThrough(MotionEvent ev) {
final Layout layout = mTextView.getLayout();
final int line = layout.getLineForOffset(getCurrentCursorOffset());
- final int textHeight =
- layout.getLineBottomWithoutSpacing(line) - layout.getLineTop(line);
+ final int textHeight = layout.getLineBottom(line, /* includeLineSpacing= */ false)
+ - layout.getLineTop(line);
// Transforms the touch events to screen coordinates.
// And also shift up to make the hit point is on the text.
// Note:
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 68b902f14079..d11fa5f01ac1 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -195,6 +195,8 @@ import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.JoinOrSplitGesture;
+import android.view.inputmethod.RemoveSpaceGesture;
import android.view.inputmethod.SelectGesture;
import android.view.inspector.InspectableProperty;
import android.view.inspector.InspectableProperty.EnumEntry;
@@ -238,6 +240,8 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* A user interface element that displays text to the user.
@@ -1033,6 +1037,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
//
// End of autofill-related attributes
+ private Pattern mWhitespacePattern;
+
/**
* Kick-start the font cache for the zygote process (to pay the cost of
* initializing freetype for our default font only once).
@@ -7644,7 +7650,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
createEditorIfNeeded();
mEditor.setError(error, icon);
notifyViewAccessibilityStateChangedIfNeeded(
- AccessibilityEvent.CONTENT_CHANGE_TYPE_INVALID);
+ AccessibilityEvent.CONTENT_CHANGE_TYPE_ERROR
+ | AccessibilityEvent.CONTENT_CHANGE_TYPE_CONTENT_INVALID);
}
@Override
@@ -9090,6 +9097,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
gestures.add(SelectGesture.class);
gestures.add(DeleteGesture.class);
gestures.add(InsertGesture.class);
+ gestures.add(RemoveSpaceGesture.class);
+ gestures.add(JoinOrSplitGesture.class);
outAttrs.setSupportedHandwritingGestures(gestures);
return ic;
}
@@ -9303,7 +9312,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/** @hide */
public int performHandwritingSelectGesture(@NonNull SelectGesture gesture) {
- int[] range = getRangeForRect(gesture.getSelectionArea(), gesture.getGranularity());
+ int[] range = getRangeForRect(
+ convertFromScreenToContentCoordinates(gesture.getSelectionArea()),
+ gesture.getGranularity());
if (range == null) {
return handleGestureFailure(gesture);
}
@@ -9314,28 +9325,73 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
/** @hide */
public int performHandwritingDeleteGesture(@NonNull DeleteGesture gesture) {
- int[] range = getRangeForRect(gesture.getDeletionArea(), gesture.getGranularity());
+ int[] range = getRangeForRect(
+ convertFromScreenToContentCoordinates(gesture.getDeletionArea()),
+ gesture.getGranularity());
if (range == null) {
return handleGestureFailure(gesture);
}
- getEditableText().delete(range[0], range[1]);
- Selection.setSelection(getEditableText(), range[0]);
- // TODO(b/243983058): Delete extra spaces.
+ int start = range[0];
+ int end = range[1];
+
+ // For word granularity, adjust the start and end offsets to remove extra whitespace around
+ // the deleted text.
+ if (gesture.getGranularity() == HandwritingGesture.GRANULARITY_WORD) {
+ // If the deleted text is at the start of the text, the behavior is the same as the case
+ // where the deleted text follows a new line character.
+ int codePointBeforeStart = start > 0
+ ? Character.codePointBefore(mText, start) : TextUtils.LINE_FEED_CODE_POINT;
+ // If the deleted text is at the end of the text, the behavior is the same as the case
+ // where the deleted text precedes a new line character.
+ int codePointAtEnd = end < mText.length()
+ ? Character.codePointAt(mText, end) : TextUtils.LINE_FEED_CODE_POINT;
+ if (TextUtils.isWhitespaceExceptNewline(codePointBeforeStart)
+ && (TextUtils.isWhitespace(codePointAtEnd)
+ || TextUtils.isPunctuation(codePointAtEnd))) {
+ // Remove whitespace (except new lines) before the deleted text, in these cases:
+ // - There is whitespace following the deleted text
+ // e.g. "one [deleted] three" -> "one | three" -> "one| three"
+ // - There is punctuation following the deleted text
+ // e.g. "one [deleted]!" -> "one |!" -> "one|!"
+ // - There is a new line following the deleted text
+ // e.g. "one [deleted]\n" -> "one |\n" -> "one|\n"
+ // - The deleted text is at the end of the text
+ // e.g. "one [deleted]" -> "one |" -> "one|"
+ // (The pipe | indicates the cursor position.)
+ while (start > 0 && TextUtils.isWhitespaceExceptNewline(codePointBeforeStart)) {
+ start -= Character.charCount(codePointBeforeStart);
+ codePointBeforeStart = Character.codePointBefore(mText, start);
+ }
+ } else if (TextUtils.isWhitespaceExceptNewline(codePointAtEnd)
+ && (TextUtils.isWhitespace(codePointBeforeStart)
+ || TextUtils.isPunctuation(codePointBeforeStart))) {
+ // Remove whitespace (except new lines) after the deleted text, in these cases:
+ // - There is punctuation preceding the deleted text
+ // e.g. "([deleted] two)" -> "(| two)" -> "(|two)"
+ // - There is a new line preceding the deleted text
+ // e.g. "\n[deleted] two" -> "\n| two" -> "\n|two"
+ // - The deleted text is at the start of the text
+ // e.g. "[deleted] two" -> "| two" -> "|two"
+ // (The pipe | indicates the cursor position.)
+ while (end < mText.length()
+ && TextUtils.isWhitespaceExceptNewline(codePointAtEnd)) {
+ end += Character.charCount(codePointAtEnd);
+ codePointAtEnd = Character.codePointAt(mText, end);
+ }
+ }
+ }
+
+ getEditableText().delete(start, end);
+ Selection.setSelection(getEditableText(), start);
return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
}
/** @hide */
public int performHandwritingInsertGesture(@NonNull InsertGesture gesture) {
- PointF point = gesture.getInsertionPoint();
- // The coordinates provided are screen coordinates - transform to content coordinates.
- int[] screenToViewport = getLocationOnScreen();
- point.offset(
- -(screenToViewport[0] + viewportToContentHorizontalOffset()),
- -(screenToViewport[1] + viewportToContentVerticalOffset()));
-
+ PointF point = convertFromScreenToContentCoordinates(gesture.getInsertionPoint());
int line = mLayout.getLineForVertical((int) point.y);
if (point.y < mLayout.getLineTop(line)
- || point.y > mLayout.getLineBottomWithoutSpacing(line)) {
+ || point.y > mLayout.getLineBottom(line, /* includeLineSpacing= */ false)) {
return handleGestureFailure(gesture);
}
if (point.x < mLayout.getLineLeft(line) || point.x > mLayout.getLineRight(line)) {
@@ -9349,6 +9405,105 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
}
+ /** @hide */
+ public int performHandwritingRemoveSpaceGesture(@NonNull RemoveSpaceGesture gesture) {
+ PointF startPoint = convertFromScreenToContentCoordinates(gesture.getStartPoint());
+ PointF endPoint = convertFromScreenToContentCoordinates(gesture.getEndPoint());
+
+ // The operation should be applied to the first line of text touched by the line joining
+ // the points.
+ int yMin = (int) Math.min(startPoint.y, endPoint.y);
+ int yMax = (int) Math.max(startPoint.y, endPoint.y);
+ int line = mLayout.getLineForVertical(yMin);
+ if (yMax < mLayout.getLineTop(line)) {
+ // Both points are above the top of the first line.
+ return handleGestureFailure(gesture);
+ }
+ if (yMin > mLayout.getLineBottom(line, /* includeLineSpacing= */ false)) {
+ if (line == mLayout.getLineCount() - 1 || yMax < mLayout.getLineTop(line + 1)) {
+ // The points are below the last line, or they are between two lines.
+ return handleGestureFailure(gesture);
+ } else {
+ // Apply the operation to the next line.
+ line++;
+ }
+ }
+ if (Math.max(startPoint.x, endPoint.x) < mLayout.getLineLeft(line)
+ || Math.min(startPoint.x, endPoint.x) > mLayout.getLineRight(line)) {
+ return handleGestureFailure(gesture);
+ }
+
+ // The operation should be applied to all characters touched by the line joining the points.
+ int startOffset = mLayout.getOffsetForHorizontal(line, startPoint.x);
+ int endOffset = mLayout.getOffsetForHorizontal(line, endPoint.x);
+ if (startOffset == endOffset) {
+ return handleGestureFailure(gesture);
+ } else if (startOffset > endOffset) {
+ int tmp = startOffset;
+ startOffset = endOffset;
+ endOffset = tmp;
+ }
+ // TODO(b/247557062): The boundary offsets might be off by one. We should check which side
+ // of the offset the point is on, and adjust if necessary.
+ // TODO(b/247557062): This doesn't handle bidirectional text correctly.
+
+ Pattern whitespacePattern = getWhitespacePattern();
+ Matcher matcher = whitespacePattern.matcher(mText.subSequence(startOffset, endOffset));
+ int lastRemoveOffset = -1;
+ while (matcher.find()) {
+ lastRemoveOffset = startOffset + matcher.start();
+ getEditableText().delete(lastRemoveOffset, startOffset + matcher.end());
+ startOffset = lastRemoveOffset;
+ endOffset -= matcher.end() - matcher.start();
+ if (startOffset == endOffset) {
+ break;
+ }
+ matcher = whitespacePattern.matcher(mText.subSequence(startOffset, endOffset));
+ }
+ if (lastRemoveOffset == -1) {
+ return handleGestureFailure(gesture);
+ }
+ Selection.setSelection(getEditableText(), lastRemoveOffset);
+ return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
+ }
+
+ /** @hide */
+ public int performHandwritingJoinOrSplitGesture(@NonNull JoinOrSplitGesture gesture) {
+ PointF point = convertFromScreenToContentCoordinates(gesture.getJoinOrSplitPoint());
+
+ int line = mLayout.getLineForVertical((int) point.y);
+ if (point.y < mLayout.getLineTop(line)
+ || point.y > mLayout.getLineBottom(line, /* includeLineSpacing= */ false)) {
+ return handleGestureFailure(gesture);
+ }
+ if (point.x < mLayout.getLineLeft(line) || point.x > mLayout.getLineRight(line)) {
+ return handleGestureFailure(gesture);
+ }
+
+ int startOffset = mLayout.getOffsetForHorizontal(line, point.x);
+ if (mLayout.isLevelBoundary(startOffset)) {
+ // TODO(b/247551937): Support gesture at level boundaries.
+ return handleGestureFailure(gesture);
+ }
+
+ int endOffset = startOffset;
+ while (startOffset > 0 && Character.isWhitespace(mText.charAt(startOffset - 1))) {
+ startOffset--;
+ }
+ while (endOffset < mText.length() && Character.isWhitespace(mText.charAt(endOffset))) {
+ endOffset++;
+ }
+ if (startOffset < endOffset) {
+ getEditableText().delete(startOffset, endOffset);
+ Selection.setSelection(getEditableText(), startOffset);
+ } else {
+ // No whitespace found, so insert a space.
+ getEditableText().insert(startOffset, " ");
+ Selection.setSelection(getEditableText(), startOffset + 1);
+ }
+ return InputConnection.HANDWRITING_GESTURE_RESULT_SUCCESS;
+ }
+
private int handleGestureFailure(HandwritingGesture gesture) {
if (!TextUtils.isEmpty(gesture.getFallbackText())) {
getEditableText()
@@ -9360,13 +9515,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
@Nullable
private int[] getRangeForRect(@NonNull RectF area, int granularity) {
- // The coordinates provided are screen coordinates - transform to content coordinates.
- int[] screenToViewport = getLocationOnScreen();
- area = new RectF(area);
- area.offset(
- -(screenToViewport[0] + viewportToContentHorizontalOffset()),
- -(screenToViewport[1] + viewportToContentVerticalOffset()));
-
SegmentIterator segmentIterator;
if (granularity == HandwritingGesture.GRANULARITY_WORD) {
WordIterator wordIterator = getWordIterator();
@@ -9379,6 +9527,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return mLayout.getRangeForRect(area, segmentIterator);
}
+ private Pattern getWhitespacePattern() {
+ if (mWhitespacePattern == null) {
+ mWhitespacePattern = Pattern.compile("\\s+");
+ }
+ return mWhitespacePattern;
+ }
+
/** @hide */
@VisibleForTesting
@UnsupportedAppUsage
@@ -10631,6 +10786,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
r.bottom += verticalOffset;
}
+ private PointF convertFromScreenToContentCoordinates(PointF point) {
+ int[] screenToViewport = getLocationOnScreen();
+ PointF copy = new PointF(point);
+ copy.offset(
+ -(screenToViewport[0] + viewportToContentHorizontalOffset()),
+ -(screenToViewport[1] + viewportToContentVerticalOffset()));
+ return copy;
+ }
+
+ private RectF convertFromScreenToContentCoordinates(RectF rect) {
+ int[] screenToViewport = getLocationOnScreen();
+ RectF copy = new RectF(rect);
+ copy.offset(
+ -(screenToViewport[0] + viewportToContentHorizontalOffset()),
+ -(screenToViewport[1] + viewportToContentVerticalOffset()));
+ return copy;
+ }
+
int viewportToContentHorizontalOffset() {
return getCompoundPaddingLeft() - mScrollX;
}
@@ -12049,6 +12222,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
}
+ @Override
+ public boolean isAutoHandwritingEnabled() {
+ return super.isAutoHandwritingEnabled() && !isAnyPasswordInputType();
+ }
+
/** @hide */
@Override
public boolean isStylusHandwritingAvailable() {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/Apps.kt b/core/java/android/window/ScreenCapture.aidl
index 6e1afd94fe29..267a7c60b60d 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/Apps.kt
+++ b/core/java/android/window/ScreenCapture.aidl
@@ -14,12 +14,13 @@
* limitations under the License.
*/
-package com.android.settingslib.spaprivileged.model.app
+package android.window;
-import android.content.pm.ApplicationInfo
-import android.os.UserHandle
+/** @hide */
+parcelable ScreenCapture.CaptureArgs;
-val ApplicationInfo.userId: Int
- get() = UserHandle.getUserId(uid)
+/** @hide */
+parcelable ScreenCapture.ScreenshotHardwareBuffer;
-fun ApplicationInfo.toRoute() = "$packageName/$userId"
+/** @hide */
+parcelable ScreenCapture.ScreenCaptureListener; \ No newline at end of file
diff --git a/core/java/android/window/ScreenCapture.java b/core/java/android/window/ScreenCapture.java
new file mode 100644
index 000000000000..8a7efb93d961
--- /dev/null
+++ b/core/java/android/window/ScreenCapture.java
@@ -0,0 +1,718 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.hardware.HardwareBuffer;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import android.util.Pair;
+import android.view.SurfaceControl;
+
+import libcore.util.NativeAllocationRegistry;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+
+/**
+ * Handles display and layer captures for the system.
+ *
+ * @hide
+ */
+public class ScreenCapture {
+ private static final String TAG = "ScreenCapture";
+ private static final int SCREENSHOT_WAIT_TIME_S = 1;
+
+ private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs,
+ long captureListener);
+ private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs,
+ long captureListener);
+ private static native long nativeCreateScreenCaptureListener(
+ Consumer<ScreenshotHardwareBuffer> consumer);
+ private static native void nativeWriteListenerToParcel(long nativeObject, Parcel out);
+ private static native long nativeReadListenerFromParcel(Parcel in);
+ private static native long getNativeListenerFinalizer();
+
+ /**
+ * @param captureArgs Arguments about how to take the screenshot
+ * @param captureListener A listener to receive the screenshot callback
+ * @hide
+ */
+ public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs,
+ @NonNull ScreenCaptureListener captureListener) {
+ return nativeCaptureDisplay(captureArgs, captureListener.mNativeObject);
+ }
+
+ /**
+ * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with
+ * the content.
+ *
+ * @hide
+ */
+ public static ScreenshotHardwareBuffer captureDisplay(
+ DisplayCaptureArgs captureArgs) {
+ Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
+ int status = captureDisplay(captureArgs, syncScreenCapture.first);
+ if (status != 0) {
+ return null;
+ }
+
+ try {
+ return syncScreenCapture.second.get();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
+ *
+ * @param layer The root layer to capture.
+ * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
+ * Rect()' or null if no cropping is desired. If the root layer does not
+ * have a buffer or a crop set, then a non-empty source crop must be
+ * specified.
+ * @param frameScale The desired scale of the returned buffer; the raw screen will be scaled
+ * up/down.
+ * @return Returns a HardwareBuffer that contains the layer capture.
+ * @hide
+ */
+ public static ScreenshotHardwareBuffer captureLayers(SurfaceControl layer, Rect sourceCrop,
+ float frameScale) {
+ return captureLayers(layer, sourceCrop, frameScale, PixelFormat.RGBA_8888);
+ }
+
+ /**
+ * Captures a layer and its children and returns a {@link HardwareBuffer} with the content.
+ *
+ * @param layer The root layer to capture.
+ * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new
+ * Rect()' or null if no cropping is desired. If the root layer does not
+ * have a buffer or a crop set, then a non-empty source crop must be
+ * specified.
+ * @param frameScale The desired scale of the returned buffer; the raw screen will be scaled
+ * up/down.
+ * @param format The desired pixel format of the returned buffer.
+ * @return Returns a HardwareBuffer that contains the layer capture.
+ * @hide
+ */
+ public static ScreenshotHardwareBuffer captureLayers(@NonNull SurfaceControl layer,
+ @Nullable Rect sourceCrop, float frameScale, int format) {
+ LayerCaptureArgs captureArgs = new LayerCaptureArgs.Builder(layer)
+ .setSourceCrop(sourceCrop)
+ .setFrameScale(frameScale)
+ .setPixelFormat(format)
+ .build();
+
+ return captureLayers(captureArgs);
+ }
+
+ /**
+ * @hide
+ */
+ public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) {
+ Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture = createSyncCaptureListener();
+ int status = captureLayers(captureArgs, syncScreenCapture.first);
+ if (status != 0) {
+ return null;
+ }
+
+ try {
+ return syncScreenCapture.second.get();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * Like {@link #captureLayers(SurfaceControl, Rect, float, int)} but with an array of layer
+ * handles to exclude.
+ *
+ * @hide
+ */
+ public static ScreenshotHardwareBuffer captureLayersExcluding(SurfaceControl layer,
+ Rect sourceCrop, float frameScale, int format, SurfaceControl[] exclude) {
+ LayerCaptureArgs captureArgs = new LayerCaptureArgs.Builder(layer)
+ .setSourceCrop(sourceCrop)
+ .setFrameScale(frameScale)
+ .setPixelFormat(format)
+ .setExcludeLayers(exclude)
+ .build();
+
+ return captureLayers(captureArgs);
+ }
+
+ /**
+ * @param captureArgs Arguments about how to take the screenshot
+ * @param captureListener A listener to receive the screenshot callback
+ * @hide
+ */
+ public static int captureLayers(@NonNull LayerCaptureArgs captureArgs,
+ @NonNull ScreenCaptureListener captureListener) {
+ return nativeCaptureLayers(captureArgs, captureListener.mNativeObject);
+ }
+
+ /**
+ * A wrapper around HardwareBuffer that contains extra information about how to
+ * interpret the screenshot HardwareBuffer.
+ *
+ * @hide
+ */
+ public static class ScreenshotHardwareBuffer {
+ private final HardwareBuffer mHardwareBuffer;
+ private final ColorSpace mColorSpace;
+ private final boolean mContainsSecureLayers;
+ private final boolean mContainsHdrLayers;
+
+ public ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace,
+ boolean containsSecureLayers, boolean containsHdrLayers) {
+ mHardwareBuffer = hardwareBuffer;
+ mColorSpace = colorSpace;
+ mContainsSecureLayers = containsSecureLayers;
+ mContainsHdrLayers = containsHdrLayers;
+ }
+
+ /**
+ * Create ScreenshotHardwareBuffer from an existing HardwareBuffer object.
+ *
+ * @param hardwareBuffer The existing HardwareBuffer object
+ * @param namedColorSpace Integer value of a named color space {@link ColorSpace.Named}
+ * @param containsSecureLayers Indicates whether this graphic buffer contains captured
+ * contents of secure layers, in which case the screenshot
+ * should not be persisted.
+ * @param containsHdrLayers Indicates whether this graphic buffer contains HDR content.
+ */
+ private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer,
+ int namedColorSpace, boolean containsSecureLayers, boolean containsHdrLayers) {
+ ColorSpace colorSpace = ColorSpace.get(ColorSpace.Named.values()[namedColorSpace]);
+ return new ScreenshotHardwareBuffer(
+ hardwareBuffer, colorSpace, containsSecureLayers, containsHdrLayers);
+ }
+
+ public ColorSpace getColorSpace() {
+ return mColorSpace;
+ }
+
+ public HardwareBuffer getHardwareBuffer() {
+ return mHardwareBuffer;
+ }
+
+ /**
+ * Whether this screenshot contains secure layers
+ */
+ public boolean containsSecureLayers() {
+ return mContainsSecureLayers;
+ }
+
+ /**
+ * Returns whether the screenshot contains at least one HDR layer.
+ * This information may be useful for informing the display whether this screenshot
+ * is allowed to be dimmed to SDR white.
+ */
+ public boolean containsHdrLayers() {
+ return mContainsHdrLayers;
+ }
+
+ /**
+ * Copy content of ScreenshotHardwareBuffer into a hardware bitmap and return it.
+ * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap
+ * into
+ * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
+ * <p>
+ * CAVEAT: This can be extremely slow; avoid use unless absolutely necessary; prefer to
+ * directly
+ * use the {@link HardwareBuffer} directly.
+ *
+ * @return Bitmap generated from the {@link HardwareBuffer}
+ */
+ public Bitmap asBitmap() {
+ if (mHardwareBuffer == null) {
+ Log.w(TAG, "Failed to take screenshot. Null screenshot object");
+ return null;
+ }
+ return Bitmap.wrapHardwareBuffer(mHardwareBuffer, mColorSpace);
+ }
+ }
+
+ /**
+ * A common arguments class used for various screenshot requests. This contains arguments that
+ * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
+ *
+ * @hide
+ */
+ public static class CaptureArgs implements Parcelable {
+ public final int mPixelFormat;
+ public final Rect mSourceCrop = new Rect();
+ public final float mFrameScaleX;
+ public final float mFrameScaleY;
+ public final boolean mCaptureSecureLayers;
+ public final boolean mAllowProtected;
+ public final long mUid;
+ public final boolean mGrayscale;
+
+ private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) {
+ mPixelFormat = builder.mPixelFormat;
+ mSourceCrop.set(builder.mSourceCrop);
+ mFrameScaleX = builder.mFrameScaleX;
+ mFrameScaleY = builder.mFrameScaleY;
+ mCaptureSecureLayers = builder.mCaptureSecureLayers;
+ mAllowProtected = builder.mAllowProtected;
+ mUid = builder.mUid;
+ mGrayscale = builder.mGrayscale;
+ }
+
+ private CaptureArgs(Parcel in) {
+ mPixelFormat = in.readInt();
+ mSourceCrop.readFromParcel(in);
+ mFrameScaleX = in.readFloat();
+ mFrameScaleY = in.readFloat();
+ mCaptureSecureLayers = in.readBoolean();
+ mAllowProtected = in.readBoolean();
+ mUid = in.readLong();
+ mGrayscale = in.readBoolean();
+ }
+
+ /**
+ * The Builder class used to construct {@link CaptureArgs}
+ *
+ * @param <T> A builder that extends {@link CaptureArgs.Builder}
+ */
+ public static class Builder<T extends CaptureArgs.Builder<T>> {
+ private int mPixelFormat = PixelFormat.RGBA_8888;
+ private final Rect mSourceCrop = new Rect();
+ private float mFrameScaleX = 1;
+ private float mFrameScaleY = 1;
+ private boolean mCaptureSecureLayers;
+ private boolean mAllowProtected;
+ private long mUid = -1;
+ private boolean mGrayscale;
+
+ /**
+ * Construct a new {@link CaptureArgs} with the set parameters. The builder remains
+ * valid.
+ */
+ public CaptureArgs build() {
+ return new CaptureArgs(this);
+ }
+
+ /**
+ * The desired pixel format of the returned buffer.
+ */
+ public T setPixelFormat(int pixelFormat) {
+ mPixelFormat = pixelFormat;
+ return getThis();
+ }
+
+ /**
+ * The portion of the screen to capture into the buffer. Caller may pass in
+ * 'new Rect()' or null if no cropping is desired.
+ */
+ public T setSourceCrop(@Nullable Rect sourceCrop) {
+ if (sourceCrop == null) {
+ mSourceCrop.setEmpty();
+ } else {
+ mSourceCrop.set(sourceCrop);
+ }
+ return getThis();
+ }
+
+ /**
+ * The desired scale of the returned buffer. The raw screen will be scaled up/down.
+ */
+ public T setFrameScale(float frameScale) {
+ mFrameScaleX = frameScale;
+ mFrameScaleY = frameScale;
+ return getThis();
+ }
+
+ /**
+ * The desired scale of the returned buffer, allowing separate values for x and y scale.
+ * The raw screen will be scaled up/down.
+ */
+ public T setFrameScale(float frameScaleX, float frameScaleY) {
+ mFrameScaleX = frameScaleX;
+ mFrameScaleY = frameScaleY;
+ return getThis();
+ }
+
+ /**
+ * Whether to allow the screenshot of secure layers. Warning: This should only be done
+ * if the content will be placed in a secure SurfaceControl.
+ *
+ * @see ScreenshotHardwareBuffer#containsSecureLayers()
+ */
+ public T setCaptureSecureLayers(boolean captureSecureLayers) {
+ mCaptureSecureLayers = captureSecureLayers;
+ return getThis();
+ }
+
+ /**
+ * Whether to allow the screenshot of protected (DRM) content. Warning: The screenshot
+ * cannot be read in unprotected space.
+ *
+ * @see HardwareBuffer#USAGE_PROTECTED_CONTENT
+ */
+ public T setAllowProtected(boolean allowProtected) {
+ mAllowProtected = allowProtected;
+ return getThis();
+ }
+
+ /**
+ * Set the uid of the content that should be screenshot. The code will skip any surfaces
+ * that don't belong to the specified uid.
+ */
+ public T setUid(long uid) {
+ mUid = uid;
+ return getThis();
+ }
+
+ /**
+ * Set whether the screenshot should use grayscale or not.
+ */
+ public T setGrayscale(boolean grayscale) {
+ mGrayscale = grayscale;
+ return getThis();
+ }
+
+ /**
+ * Each sub class should return itself to allow the builder to chain properly
+ */
+ T getThis() {
+ return (T) this;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mPixelFormat);
+ mSourceCrop.writeToParcel(dest, flags);
+ dest.writeFloat(mFrameScaleX);
+ dest.writeFloat(mFrameScaleY);
+ dest.writeBoolean(mCaptureSecureLayers);
+ dest.writeBoolean(mAllowProtected);
+ dest.writeLong(mUid);
+ dest.writeBoolean(mGrayscale);
+ }
+
+ public static final Parcelable.Creator<CaptureArgs> CREATOR =
+ new Parcelable.Creator<CaptureArgs>() {
+ @Override
+ public CaptureArgs createFromParcel(Parcel in) {
+ return new CaptureArgs(in);
+ }
+
+ @Override
+ public CaptureArgs[] newArray(int size) {
+ return new CaptureArgs[size];
+ }
+ };
+ }
+
+ /**
+ * The arguments class used to make display capture requests.
+ *
+ * @hide
+ * @see #nativeCaptureDisplay(DisplayCaptureArgs, long)
+ */
+ public static class DisplayCaptureArgs extends CaptureArgs {
+ private final IBinder mDisplayToken;
+ private final int mWidth;
+ private final int mHeight;
+ private final boolean mUseIdentityTransform;
+
+ private DisplayCaptureArgs(Builder builder) {
+ super(builder);
+ mDisplayToken = builder.mDisplayToken;
+ mWidth = builder.mWidth;
+ mHeight = builder.mHeight;
+ mUseIdentityTransform = builder.mUseIdentityTransform;
+ }
+
+ /**
+ * The Builder class used to construct {@link DisplayCaptureArgs}
+ */
+ public static class Builder extends CaptureArgs.Builder<Builder> {
+ private IBinder mDisplayToken;
+ private int mWidth;
+ private int mHeight;
+ private boolean mUseIdentityTransform;
+
+ /**
+ * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
+ * remains valid.
+ */
+ public DisplayCaptureArgs build() {
+ if (mDisplayToken == null) {
+ throw new IllegalStateException(
+ "Can't take screenshot with null display token");
+ }
+ return new DisplayCaptureArgs(this);
+ }
+
+ public Builder(IBinder displayToken) {
+ setDisplayToken(displayToken);
+ }
+
+ /**
+ * The display to take the screenshot of.
+ */
+ public Builder setDisplayToken(IBinder displayToken) {
+ mDisplayToken = displayToken;
+ return this;
+ }
+
+ /**
+ * Set the desired size of the returned buffer. The raw screen will be scaled down to
+ * this size
+ *
+ * @param width The desired width of the returned buffer. Caller may pass in 0 if no
+ * scaling is desired.
+ * @param height The desired height of the returned buffer. Caller may pass in 0 if no
+ * scaling is desired.
+ */
+ public Builder setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ return this;
+ }
+
+ /**
+ * Replace the rotation transform of the display with the identity transformation while
+ * taking the screenshot. This ensures the screenshot is taken in the ROTATION_0
+ * orientation. Set this value to false if the screenshot should be taken in the
+ * current screen orientation.
+ */
+ public Builder setUseIdentityTransform(boolean useIdentityTransform) {
+ mUseIdentityTransform = useIdentityTransform;
+ return this;
+ }
+
+ @Override
+ Builder getThis() {
+ return this;
+ }
+ }
+ }
+
+ /**
+ * The arguments class used to make layer capture requests.
+ *
+ * @hide
+ * @see #nativeCaptureLayers(LayerCaptureArgs, long)
+ */
+ public static class LayerCaptureArgs extends CaptureArgs {
+ private final long mNativeLayer;
+ private final long[] mNativeExcludeLayers;
+ private final boolean mChildrenOnly;
+
+ private LayerCaptureArgs(Builder builder) {
+ super(builder);
+ mChildrenOnly = builder.mChildrenOnly;
+ mNativeLayer = builder.mLayer.mNativeObject;
+ if (builder.mExcludeLayers != null) {
+ mNativeExcludeLayers = new long[builder.mExcludeLayers.length];
+ for (int i = 0; i < builder.mExcludeLayers.length; i++) {
+ mNativeExcludeLayers[i] = builder.mExcludeLayers[i].mNativeObject;
+ }
+ } else {
+ mNativeExcludeLayers = null;
+ }
+ }
+
+ /**
+ * The Builder class used to construct {@link LayerCaptureArgs}
+ */
+ public static class Builder extends CaptureArgs.Builder<Builder> {
+ private SurfaceControl mLayer;
+ private SurfaceControl[] mExcludeLayers;
+ private boolean mChildrenOnly = true;
+
+ /**
+ * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
+ * remains valid.
+ */
+ public LayerCaptureArgs build() {
+ if (mLayer == null) {
+ throw new IllegalStateException(
+ "Can't take screenshot with null layer");
+ }
+ return new LayerCaptureArgs(this);
+ }
+
+ public Builder(SurfaceControl layer, CaptureArgs args) {
+ setLayer(layer);
+ setPixelFormat(args.mPixelFormat);
+ setSourceCrop(args.mSourceCrop);
+ setFrameScale(args.mFrameScaleX, args.mFrameScaleY);
+ setCaptureSecureLayers(args.mCaptureSecureLayers);
+ setAllowProtected(args.mAllowProtected);
+ setUid(args.mUid);
+ setGrayscale(args.mGrayscale);
+ }
+
+ public Builder(SurfaceControl layer) {
+ setLayer(layer);
+ }
+
+ /**
+ * The root layer to capture.
+ */
+ public Builder setLayer(SurfaceControl layer) {
+ mLayer = layer;
+ return this;
+ }
+
+ /**
+ * An array of layer handles to exclude.
+ */
+ public Builder setExcludeLayers(@Nullable SurfaceControl[] excludeLayers) {
+ mExcludeLayers = excludeLayers;
+ return this;
+ }
+
+ /**
+ * Whether to include the layer itself in the screenshot or just the children and their
+ * descendants.
+ */
+ public Builder setChildrenOnly(boolean childrenOnly) {
+ mChildrenOnly = childrenOnly;
+ return this;
+ }
+
+ @Override
+ Builder getThis() {
+ return this;
+ }
+ }
+ }
+
+ /**
+ * The object used to receive the results when invoking screen capture requests via
+ * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)} or
+ * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)}
+ *
+ * This listener can only be used for a single call to capture content call.
+ */
+ public static class ScreenCaptureListener implements Parcelable {
+ private final long mNativeObject;
+ private static final NativeAllocationRegistry sRegistry =
+ NativeAllocationRegistry.createMalloced(
+ ScreenCaptureListener.class.getClassLoader(), getNativeListenerFinalizer());
+
+ /**
+ * @param consumer The callback invoked when the screen capture is complete.
+ */
+ public ScreenCaptureListener(Consumer<ScreenshotHardwareBuffer> consumer) {
+ mNativeObject = nativeCreateScreenCaptureListener(consumer);
+ sRegistry.registerNativeAllocation(this, mNativeObject);
+ }
+
+ private ScreenCaptureListener(Parcel in) {
+ if (in.readBoolean()) {
+ mNativeObject = nativeReadListenerFromParcel(in);
+ sRegistry.registerNativeAllocation(this, mNativeObject);
+ } else {
+ mNativeObject = 0;
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ if (mNativeObject == 0) {
+ dest.writeBoolean(false);
+ } else {
+ dest.writeBoolean(true);
+ nativeWriteListenerToParcel(mNativeObject, dest);
+ }
+ }
+
+ public static final Parcelable.Creator<ScreenCaptureListener> CREATOR =
+ new Parcelable.Creator<ScreenCaptureListener>() {
+ @Override
+ public ScreenCaptureListener createFromParcel(Parcel in) {
+ return new ScreenCaptureListener(in);
+ }
+
+ @Override
+ public ScreenCaptureListener[] newArray(int size) {
+ return new ScreenCaptureListener[0];
+ }
+ };
+ }
+
+ /**
+ * A helper method to handle the async screencapture callbacks synchronously. This should only
+ * be used if the screencapture caller doesn't care that it blocks waiting for a screenshot.
+ *
+ * @return a Pair that holds the {@link ScreenCaptureListener} that should be used for capture
+ * calls into SurfaceFlinger and a {@link ScreenshotSync} object to retrieve the results.
+ */
+ public static Pair<ScreenCaptureListener, ScreenshotSync> createSyncCaptureListener() {
+ final ScreenshotSync screenshotSync = new ScreenshotSync();
+ final ScreenCaptureListener screenCaptureListener = new ScreenCaptureListener(
+ screenshotSync::setScreenshotHardwareBuffer);
+ return new Pair<>(screenCaptureListener, screenshotSync);
+ }
+
+ /**
+ * Helper class to synchronously get the {@link ScreenshotHardwareBuffer} when calling
+ * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} or
+ * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)}
+ */
+ public static class ScreenshotSync {
+ private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+ private ScreenshotHardwareBuffer mScreenshotHardwareBuffer;
+
+ private void setScreenshotHardwareBuffer(
+ ScreenshotHardwareBuffer screenshotHardwareBuffer) {
+ mScreenshotHardwareBuffer = screenshotHardwareBuffer;
+ mCountDownLatch.countDown();
+ }
+
+ /**
+ * Get the {@link ScreenshotHardwareBuffer} synchronously. This can be null if the
+ * screenshot failed or if there was no callback in {@link #SCREENSHOT_WAIT_TIME_S} seconds.
+ */
+ public ScreenshotHardwareBuffer get() {
+ try {
+ mCountDownLatch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS);
+ return mScreenshotHardwareBuffer;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to wait for screen capture result", e);
+ return null;
+ }
+ }
+ }
+}
diff --git a/core/java/android/window/TaskFragmentInfo.java b/core/java/android/window/TaskFragmentInfo.java
index 56e910769cb5..e2c8a31cc987 100644
--- a/core/java/android/window/TaskFragmentInfo.java
+++ b/core/java/android/window/TaskFragmentInfo.java
@@ -188,6 +188,10 @@ public final class TaskFragmentInfo implements Parcelable {
/**
* Returns {@code true} if the parameters that are important for task fragment organizers are
* equal between this {@link TaskFragmentInfo} and {@param that}.
+ * Note that this method is usually called with
+ * {@link com.android.server.wm.WindowOrganizerController#configurationsAreEqualForOrganizer(
+ * Configuration, Configuration)} to determine if this {@link TaskFragmentInfo} should
+ * be dispatched to the client.
*/
public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentInfo that) {
if (that == null) {
diff --git a/core/java/android/service/cloudsearch/ICloudSearchService.aidl b/core/java/android/window/TaskFragmentParentInfo.aidl
index 104bf99f1537..79d2209ab244 100644
--- a/core/java/android/service/cloudsearch/ICloudSearchService.aidl
+++ b/core/java/android/window/TaskFragmentParentInfo.aidl
@@ -14,15 +14,10 @@
* limitations under the License.
*/
-package android.service.cloudsearch;
-
-import android.app.cloudsearch.SearchRequest;
+package android.window;
/**
- * Interface from the system to CloudSearch service.
- *
+ * The information about the parent Task of a particular TaskFragment
* @hide
*/
-oneway interface ICloudSearchService {
- void onSearch(in SearchRequest request);
-}
+parcelable TaskFragmentParentInfo; \ No newline at end of file
diff --git a/core/java/android/window/TaskFragmentParentInfo.java b/core/java/android/window/TaskFragmentParentInfo.java
new file mode 100644
index 000000000000..64b2638407df
--- /dev/null
+++ b/core/java/android/window/TaskFragmentParentInfo.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.app.WindowConfiguration;
+import android.content.res.Configuration;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The information about the parent Task of a particular TaskFragment
+ * @hide
+ */
+public class TaskFragmentParentInfo implements Parcelable {
+ @NonNull
+ private final Configuration mConfiguration = new Configuration();
+
+ private final int mDisplayId;
+
+ private final boolean mVisibleRequested;
+
+ public TaskFragmentParentInfo(@NonNull Configuration configuration, int displayId,
+ boolean visibleRequested) {
+ mConfiguration.setTo(configuration);
+ mDisplayId = displayId;
+ mVisibleRequested = visibleRequested;
+ }
+
+ public TaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
+ mConfiguration.setTo(info.getConfiguration());
+ mDisplayId = info.mDisplayId;
+ mVisibleRequested = info.mVisibleRequested;
+ }
+
+ /** The {@link Configuration} of the parent Task */
+ @NonNull
+ public Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ /**
+ * The display ID of the parent Task. {@link android.view.Display#INVALID_DISPLAY} means the
+ * Task is detached from previously associated display.
+ */
+ public int getDisplayId() {
+ return mDisplayId;
+ }
+
+ /** Whether the parent Task is requested to be visible or not */
+ public boolean isVisibleRequested() {
+ return mVisibleRequested;
+ }
+
+ /**
+ * Returns {@code true} if the parameters which are important for task fragment
+ * organizers are equal between this {@link TaskFragmentParentInfo} and {@code that}.
+ * Note that this method is usually called with
+ * {@link com.android.server.wm.WindowOrganizerController#configurationsAreEqualForOrganizer(
+ * Configuration, Configuration)} to determine if this {@link TaskFragmentParentInfo} should
+ * be dispatched to the client.
+ */
+ public boolean equalsForTaskFragmentOrganizer(@Nullable TaskFragmentParentInfo that) {
+ if (that == null) {
+ return false;
+ }
+ return getWindowingMode() == that.getWindowingMode() && mDisplayId == that.mDisplayId
+ && mVisibleRequested == that.mVisibleRequested;
+ }
+
+ @WindowConfiguration.WindowingMode
+ private int getWindowingMode() {
+ return mConfiguration.windowConfiguration.getWindowingMode();
+ }
+
+ @Override
+ public String toString() {
+ return TaskFragmentParentInfo.class.getSimpleName() + ":{"
+ + "config=" + mConfiguration
+ + ", displayId=" + mDisplayId
+ + ", visibleRequested=" + mVisibleRequested
+ + "}";
+ }
+
+ /**
+ * Indicates that whether this {@link TaskFragmentParentInfo} equals to {@code obj}.
+ * Note that {@link #equalsForTaskFragmentOrganizer(TaskFragmentParentInfo)} should be used
+ * for most cases because not all {@link Configuration} properties are interested for
+ * {@link TaskFragmentOrganizer}.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ }
+ if (!(obj instanceof TaskFragmentParentInfo)) {
+ return false;
+ }
+ final TaskFragmentParentInfo that = (TaskFragmentParentInfo) obj;
+ return mConfiguration.equals(that.mConfiguration)
+ && mDisplayId == that.mDisplayId
+ && mVisibleRequested == that.mVisibleRequested;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = mConfiguration.hashCode();
+ result = 31 * result + mDisplayId;
+ result = 31 * result + (mVisibleRequested ? 1 : 0);
+ return result;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ mConfiguration.writeToParcel(dest, flags);
+ dest.writeInt(mDisplayId);
+ dest.writeBoolean(mVisibleRequested);
+ }
+
+ private TaskFragmentParentInfo(Parcel in) {
+ mConfiguration.readFromParcel(in);
+ mDisplayId = in.readInt();
+ mVisibleRequested = in.readBoolean();
+ }
+
+ public static final Creator<TaskFragmentParentInfo> CREATOR =
+ new Creator<TaskFragmentParentInfo>() {
+ @Override
+ public TaskFragmentParentInfo createFromParcel(Parcel in) {
+ return new TaskFragmentParentInfo(in);
+ }
+
+ @Override
+ public TaskFragmentParentInfo[] newArray(int size) {
+ return new TaskFragmentParentInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+}
diff --git a/core/java/android/window/TaskFragmentTransaction.java b/core/java/android/window/TaskFragmentTransaction.java
index 04fcd3afcf1d..76677431d6fa 100644
--- a/core/java/android/window/TaskFragmentTransaction.java
+++ b/core/java/android/window/TaskFragmentTransaction.java
@@ -173,10 +173,6 @@ public final class TaskFragmentTransaction implements Parcelable {
/** @see #setTaskId(int) */
private int mTaskId;
- /** @see #setTaskConfiguration(Configuration) */
- @Nullable
- private Configuration mTaskConfiguration;
-
/** @see #setErrorCallbackToken(IBinder) */
@Nullable
private IBinder mErrorCallbackToken;
@@ -193,6 +189,9 @@ public final class TaskFragmentTransaction implements Parcelable {
@Nullable
private IBinder mActivityToken;
+ @Nullable
+ private TaskFragmentParentInfo mTaskFragmentParentInfo;
+
public Change(@ChangeType int type) {
mType = type;
}
@@ -202,11 +201,11 @@ public final class TaskFragmentTransaction implements Parcelable {
mTaskFragmentToken = in.readStrongBinder();
mTaskFragmentInfo = in.readTypedObject(TaskFragmentInfo.CREATOR);
mTaskId = in.readInt();
- mTaskConfiguration = in.readTypedObject(Configuration.CREATOR);
mErrorCallbackToken = in.readStrongBinder();
mErrorBundle = in.readBundle(TaskFragmentTransaction.class.getClassLoader());
mActivityIntent = in.readTypedObject(Intent.CREATOR);
mActivityToken = in.readStrongBinder();
+ mTaskFragmentParentInfo = in.readTypedObject(TaskFragmentParentInfo.CREATOR);
}
@Override
@@ -215,11 +214,11 @@ public final class TaskFragmentTransaction implements Parcelable {
dest.writeStrongBinder(mTaskFragmentToken);
dest.writeTypedObject(mTaskFragmentInfo, flags);
dest.writeInt(mTaskId);
- dest.writeTypedObject(mTaskConfiguration, flags);
dest.writeStrongBinder(mErrorCallbackToken);
dest.writeBundle(mErrorBundle);
dest.writeTypedObject(mActivityIntent, flags);
dest.writeStrongBinder(mActivityToken);
+ dest.writeTypedObject(mTaskFragmentParentInfo, flags);
}
/** The change is related to the TaskFragment created with this unique token. */
@@ -243,10 +242,10 @@ public final class TaskFragmentTransaction implements Parcelable {
return this;
}
+ // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release.
/** Configuration of the parent Task. */
@NonNull
public Change setTaskConfiguration(@NonNull Configuration configuration) {
- mTaskConfiguration = requireNonNull(configuration);
return this;
}
@@ -294,6 +293,19 @@ public final class TaskFragmentTransaction implements Parcelable {
return this;
}
+ // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release.
+ /**
+ * Sets info of the parent Task of the embedded TaskFragment.
+ * @see TaskFragmentParentInfo
+ *
+ * @hide pending unhide
+ */
+ @NonNull
+ public Change setTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
+ mTaskFragmentParentInfo = requireNonNull(info);
+ return this;
+ }
+
@ChangeType
public int getType() {
return mType;
@@ -313,9 +325,10 @@ public final class TaskFragmentTransaction implements Parcelable {
return mTaskId;
}
+ // TODO(b/241043377): Keep this API to prevent @TestApi changes. Remove in the next release.
@Nullable
public Configuration getTaskConfiguration() {
- return mTaskConfiguration;
+ return mTaskFragmentParentInfo.getConfiguration();
}
@Nullable
@@ -339,6 +352,13 @@ public final class TaskFragmentTransaction implements Parcelable {
return mActivityToken;
}
+ // TODO(b/241043377): Hide this API to prevent @TestApi changes. Remove in the next release.
+ /** @hide pending unhide */
+ @Nullable
+ public TaskFragmentParentInfo getTaskFragmentParentInfo() {
+ return mTaskFragmentParentInfo;
+ }
+
@Override
public String toString() {
return "Change{ type=" + mType + " }";
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 8ca763e81757..641d1a189711 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -110,11 +110,11 @@ public final class TransitionInfo implements Parcelable {
/** The container is an input-method window. */
public static final int FLAG_IS_INPUT_METHOD = 1 << 8;
- /** The container is ActivityEmbedding embedded. */
- public static final int FLAG_IS_EMBEDDED = 1 << 9;
+ /** The container is in a Task with embedded activity. */
+ public static final int FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY = 1 << 9;
- /** 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 << 10;
+ /** The container fills its parent Task before and after the transition. */
+ public static final int FLAG_FILLS_TASK = 1 << 10;
/** The container is going to show IME on its task after the transition. */
public static final int FLAG_WILL_IME_SHOWN = 1 << 11;
@@ -125,6 +125,16 @@ public final class TransitionInfo implements Parcelable {
/** The container attaches work profile thumbnail for cross profile animation. */
public static final int FLAG_CROSS_PROFILE_WORK_THUMBNAIL = 1 << 13;
+ /**
+ * Whether the window is covered by an app starting window. This is different from
+ * {@link #FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT} which is only set on the Activity window
+ * that contains the starting window.
+ */
+ public static final int FLAG_IS_BEHIND_STARTING_WINDOW = 1 << 14;
+
+ /** 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 << 15;
+
/** @hide */
@IntDef(prefix = { "FLAG_" }, value = {
FLAG_NONE,
@@ -137,9 +147,13 @@ public final class TransitionInfo implements Parcelable {
FLAG_OCCLUDES_KEYGUARD,
FLAG_DISPLAY_HAS_ALERT_WINDOWS,
FLAG_IS_INPUT_METHOD,
- FLAG_IS_EMBEDDED,
- FLAG_FIRST_CUSTOM,
- FLAG_WILL_IME_SHOWN
+ FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY,
+ FLAG_FILLS_TASK,
+ FLAG_WILL_IME_SHOWN,
+ FLAG_CROSS_PROFILE_OWNER_THUMBNAIL,
+ FLAG_CROSS_PROFILE_WORK_THUMBNAIL,
+ FLAG_IS_BEHIND_STARTING_WINDOW,
+ FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
@@ -322,28 +336,34 @@ public final class TransitionInfo implements Parcelable {
sb.append("IS_INPUT_METHOD");
}
if ((flags & FLAG_TRANSLUCENT) != 0) {
- sb.append((sb.length() == 0 ? "" : "|") + "TRANSLUCENT");
+ sb.append(sb.length() == 0 ? "" : "|").append("TRANSLUCENT");
}
if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
- sb.append((sb.length() == 0 ? "" : "|") + "STARTING_WINDOW_TRANSFER");
+ sb.append(sb.length() == 0 ? "" : "|").append("STARTING_WINDOW_TRANSFER");
}
if ((flags & FLAG_IS_VOICE_INTERACTION) != 0) {
- sb.append((sb.length() == 0 ? "" : "|") + "IS_VOICE_INTERACTION");
+ sb.append(sb.length() == 0 ? "" : "|").append("IS_VOICE_INTERACTION");
}
if ((flags & FLAG_IS_DISPLAY) != 0) {
- sb.append((sb.length() == 0 ? "" : "|") + "IS_DISPLAY");
+ sb.append(sb.length() == 0 ? "" : "|").append("IS_DISPLAY");
}
if ((flags & FLAG_OCCLUDES_KEYGUARD) != 0) {
- sb.append((sb.length() == 0 ? "" : "|") + "OCCLUDES_KEYGUARD");
+ sb.append(sb.length() == 0 ? "" : "|").append("OCCLUDES_KEYGUARD");
}
if ((flags & FLAG_DISPLAY_HAS_ALERT_WINDOWS) != 0) {
- sb.append((sb.length() == 0 ? "" : "|") + "DISPLAY_HAS_ALERT_WINDOWS");
+ sb.append(sb.length() == 0 ? "" : "|").append("DISPLAY_HAS_ALERT_WINDOWS");
+ }
+ if ((flags & FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|").append("IN_TASK_WITH_EMBEDDED_ACTIVITY");
+ }
+ if ((flags & FLAG_FILLS_TASK) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|").append("FILLS_TASK");
}
- if ((flags & FLAG_IS_EMBEDDED) != 0) {
- sb.append((sb.length() == 0 ? "" : "|") + "IS_EMBEDDED");
+ if ((flags & FLAG_IS_BEHIND_STARTING_WINDOW) != 0) {
+ sb.append(sb.length() == 0 ? "" : "|").append("IS_BEHIND_STARTING_WINDOW");
}
if ((flags & FLAG_FIRST_CUSTOM) != 0) {
- sb.append((sb.length() == 0 ? "" : "|") + "FIRST_CUSTOM");
+ sb.append(sb.length() == 0 ? "" : "|").append("FIRST_CUSTOM");
}
return sb.toString();
}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index a99c6bef202f..2d29c5946ede 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -694,6 +694,23 @@ public final class WindowContainerTransaction implements Parcelable {
}
/**
+ * Finishes the Activity.
+ * Comparing to directly calling {@link android.app.Activity#finish()}, calling this can make
+ * sure the finishing happens in the same transaction with other operations.
+ * @param activityToken activity to be finished.
+ */
+ @NonNull
+ public WindowContainerTransaction finishActivity(@NonNull IBinder activityToken) {
+ final HierarchyOp hierarchyOp =
+ new HierarchyOp.Builder(
+ HierarchyOp.HIERARCHY_OP_TYPE_FINISH_ACTIVITY)
+ .setContainer(activityToken)
+ .build();
+ mHierarchyOps.add(hierarchyOp);
+ return this;
+ }
+
+ /**
* Sets/removes the always on top flag for this {@code windowContainer}. See
* {@link com.android.server.wm.ConfigurationContainer#setAlwaysOnTop(boolean)}.
* Please note that this method is only intended to be used for a
@@ -1151,6 +1168,7 @@ public final class WindowContainerTransaction implements Parcelable {
public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18;
public static final int HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP = 19;
public static final int HIERARCHY_OP_TYPE_REMOVE_TASK = 20;
+ public static final int HIERARCHY_OP_TYPE_FINISH_ACTIVITY = 21;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
@@ -1472,6 +1490,8 @@ public final class WindowContainerTransaction implements Parcelable {
+ " alwaysOnTop=" + mAlwaysOnTop + "}";
case HIERARCHY_OP_TYPE_REMOVE_TASK:
return "{RemoveTask: task=" + mContainer + "}";
+ case HIERARCHY_OP_TYPE_FINISH_ACTIVITY:
+ return "{finishActivity: activity=" + mContainer + "}";
default:
return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
+ " mToTop=" + mToTop
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 5cf7e364bbc1..c8eec7dbd486 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -1069,7 +1069,12 @@ public class ChooserActivity extends ResolverActivity implements
}
}
- private ViewGroup createContentPreviewView(ViewGroup parent) {
+ /**
+ * Create a view that will be shown in the content preview area
+ * @param parent reference to the parent container where the view should be attached to
+ * @return content preview view
+ */
+ protected ViewGroup createContentPreviewView(ViewGroup parent) {
Intent targetIntent = getTargetIntent();
int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
return displayContentPreview(previewType, targetIntent, getLayoutInflater(), parent);
@@ -2653,7 +2658,7 @@ public class ChooserActivity extends ResolverActivity implements
boolean isExpandable = getResources().getConfiguration().orientation
== Configuration.ORIENTATION_PORTRAIT && !isInMultiWindowMode();
- if (directShareHeight != 0 && isSendAction(getTargetIntent())
+ if (directShareHeight != 0 && shouldShowContentPreview()
&& isExpandable) {
// make sure to leave room for direct share 4->8 expansion
int requiredExpansionHeight =
@@ -2901,7 +2906,14 @@ public class ChooserActivity extends ResolverActivity implements
return shouldShowTabs()
&& mMultiProfilePagerAdapter.getListAdapterForUserHandle(
UserHandle.of(UserHandle.myUserId())).getCount() > 0
- && isSendAction(getTargetIntent());
+ && shouldShowContentPreview();
+ }
+
+ /**
+ * @return true if we want to show the content preview area
+ */
+ protected boolean shouldShowContentPreview() {
+ return isSendAction(getTargetIntent());
}
private void updateStickyContentPreview() {
@@ -3234,7 +3246,7 @@ public class ChooserActivity extends ResolverActivity implements
return 0;
}
- if (!isSendAction(getTargetIntent())) {
+ if (!shouldShowContentPreview()) {
return 0;
}
@@ -3265,7 +3277,7 @@ public class ChooserActivity extends ResolverActivity implements
// There can be at most one row in the listview, that is internally
// a ViewGroup with 2 rows
public int getServiceTargetRowCount() {
- if (isSendAction(getTargetIntent())
+ if (shouldShowContentPreview()
&& !ActivityManager.isLowRamDeviceStatic()) {
return 1;
}
diff --git a/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl b/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl
new file mode 100644
index 000000000000..b2236c9f4907
--- /dev/null
+++ b/core/java/com/android/internal/app/ILogAccessDialogCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+/**
+ * IPC interface for an application to receive callbacks from the log access dialog callback.
+ */
+oneway interface ILogAccessDialogCallback {
+ void approveAccessForClient(int uid, String packageName);
+ void declineAccessForClient(int uid, String packageName);
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 83bf801391f0..8d51c9cdca9c 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -270,7 +270,6 @@ interface IVoiceInteractionManagerService {
*/
void shutdownHotwordDetectionService();
- @EnforcePermission(allOf={"RECORD_AUDIO", "CAPTURE_AUDIO_HOTWORD"})
void startListeningFromMic(
in AudioFormat audioFormat,
in IMicrophoneHotwordDetectionVoiceInteractionCallback callback);
@@ -286,7 +285,6 @@ interface IVoiceInteractionManagerService {
/**
* Test API to simulate to trigger hardware recognition event for test.
*/
- @EnforcePermission(allOf={"RECORD_AUDIO", "CAPTURE_AUDIO_HOTWORD"})
void triggerHardwareRecognitionEventForTest(
in SoundTrigger.KeyphraseRecognitionEvent event,
in IHotwordRecognitionStatusCallback callback);
diff --git a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java b/core/java/com/android/internal/app/LogAccessDialogActivity.java
index 811e96ce6d82..4adb8673084b 100644
--- a/services/core/java/com/android/server/logcat/LogAccessDialogActivity.java
+++ b/core/java/com/android/internal/app/LogAccessDialogActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.logcat;
+package com.android.internal.app;
import android.annotation.StyleRes;
import android.app.Activity;
@@ -27,7 +27,14 @@ import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.RemoteException;
import android.os.UserHandle;
+import android.text.Html;
+import android.text.Spannable;
+import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
+import android.text.style.TypefaceSpan;
+import android.text.style.URLSpan;
import android.util.Slog;
import android.view.ContextThemeWrapper;
import android.view.InflateException;
@@ -37,7 +44,6 @@ import android.widget.Button;
import android.widget.TextView;
import com.android.internal.R;
-import com.android.server.LocalServices;
/**
* Dialog responsible for obtaining user consent per-use log access
@@ -45,17 +51,19 @@ import com.android.server.LocalServices;
public class LogAccessDialogActivity extends Activity implements
View.OnClickListener {
private static final String TAG = LogAccessDialogActivity.class.getSimpleName();
+ public static final String EXTRA_CALLBACK = "EXTRA_CALLBACK";
+
private static final int DIALOG_TIME_OUT = Build.IS_DEBUGGABLE ? 60000 : 300000;
private static final int MSG_DISMISS_DIALOG = 0;
- private final LogcatManagerService.LogcatManagerServiceInternal mLogcatManagerInternal =
- LocalServices.getService(LogcatManagerService.LogcatManagerServiceInternal.class);
-
private String mPackageName;
private int mUid;
+ private ILogAccessDialogCallback mCallback;
private String mAlertTitle;
+ private String mAlertBody;
+ private String mAlertLearnMore;
private AlertDialog.Builder mAlertDialog;
private AlertDialog mAlert;
private View mAlertView;
@@ -81,6 +89,9 @@ public class LogAccessDialogActivity extends Activity implements
return;
}
+ mAlertBody = getResources().getString(R.string.log_access_confirmation_body);
+ mAlertLearnMore = getResources().getString(R.string.log_access_confirmation_learn_more);
+
// create View
boolean isDarkTheme = (getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
@@ -118,6 +129,13 @@ public class LogAccessDialogActivity extends Activity implements
return false;
}
+ mCallback = ILogAccessDialogCallback.Stub.asInterface(
+ intent.getExtras().getBinder(EXTRA_CALLBACK));
+ if (mCallback == null) {
+ Slog.e(TAG, "Missing callback");
+ return false;
+ }
+
mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
if (mPackageName == null || mPackageName.length() == 0) {
Slog.e(TAG, "Missing package name extra");
@@ -165,13 +183,22 @@ public class LogAccessDialogActivity extends Activity implements
return titleString;
}
+ private Spannable styleFont(String text) {
+ Spannable s = (Spannable) Html.fromHtml(text);
+ for (URLSpan span : s.getSpans(0, s.length(), URLSpan.class)) {
+ TypefaceSpan typefaceSpan = new TypefaceSpan("google-sans");
+ s.setSpan(typefaceSpan, s.getSpanStart(span), s.getSpanEnd(span), 0);
+ }
+ return s;
+ }
+
/**
* Returns the dialog view.
* If we cannot retrieve the package name, it returns null and we decline the full device log
* access
*/
private View createView(@StyleRes int themeId) {
- Context themedContext = new ContextThemeWrapper(getApplicationContext(), themeId);
+ Context themedContext = new ContextThemeWrapper(this, themeId);
final View view = LayoutInflater.from(themedContext).inflate(
R.layout.log_access_user_consent_dialog_permission, null /*root*/);
@@ -182,6 +209,19 @@ public class LogAccessDialogActivity extends Activity implements
((TextView) view.findViewById(R.id.log_access_dialog_title))
.setText(mAlertTitle);
+ if (!TextUtils.isEmpty(mAlertLearnMore)) {
+ Spannable mSpannableLearnMore = styleFont(mAlertLearnMore);
+
+ ((TextView) view.findViewById(R.id.log_access_dialog_body))
+ .setText(TextUtils.concat(mAlertBody, "\n\n", mSpannableLearnMore));
+
+ ((TextView) view.findViewById(R.id.log_access_dialog_body))
+ .setMovementMethod(LinkMovementMethod.getInstance());
+ } else {
+ ((TextView) view.findViewById(R.id.log_access_dialog_body))
+ .setText(mAlertBody);
+ }
+
Button button_allow = (Button) view.findViewById(R.id.log_access_dialog_allow_button);
button_allow.setOnClickListener(this);
@@ -194,19 +234,27 @@ public class LogAccessDialogActivity extends Activity implements
@Override
public void onClick(View view) {
- switch (view.getId()) {
- case R.id.log_access_dialog_allow_button:
- mLogcatManagerInternal.approveAccessForClient(mUid, mPackageName);
- finish();
- break;
- case R.id.log_access_dialog_deny_button:
- declineLogAccess();
- finish();
- break;
+ try {
+ switch (view.getId()) {
+ case R.id.log_access_dialog_allow_button:
+ mCallback.approveAccessForClient(mUid, mPackageName);
+ finish();
+ break;
+ case R.id.log_access_dialog_deny_button:
+ declineLogAccess();
+ finish();
+ break;
+ }
+ } catch (RemoteException e) {
+ finish();
}
}
private void declineLogAccess() {
- mLogcatManagerInternal.declineAccessForClient(mUid, mPackageName);
+ try {
+ mCallback.declineAccessForClient(mUid, mPackageName);
+ } catch (RemoteException e) {
+ finish();
+ }
}
}
diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java
index 354eb62ba045..be0b729aedfe 100644
--- a/core/java/com/android/internal/app/SimpleIconFactory.java
+++ b/core/java/com/android/internal/app/SimpleIconFactory.java
@@ -51,6 +51,7 @@ import android.util.Pools.SynchronizedPool;
import android.util.TypedValue;
import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
import org.xmlpull.v1.XmlPullParser;
@@ -69,6 +70,7 @@ public class SimpleIconFactory {
private static final SynchronizedPool<SimpleIconFactory> sPool =
new SynchronizedPool<>(Runtime.getRuntime().availableProcessors());
+ private static boolean sPoolEnabled = true;
private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
private static final float BLUR_FACTOR = 1.5f / 48;
@@ -92,7 +94,7 @@ public class SimpleIconFactory {
*/
@Deprecated
public static SimpleIconFactory obtain(Context ctx) {
- SimpleIconFactory instance = sPool.acquire();
+ SimpleIconFactory instance = sPoolEnabled ? sPool.acquire() : null;
if (instance == null) {
final ActivityManager am = (ActivityManager) ctx.getSystemService(ACTIVITY_SERVICE);
final int iconDpi = (am == null) ? 0 : am.getLauncherLargeIconDensity();
@@ -106,6 +108,17 @@ public class SimpleIconFactory {
return instance;
}
+ /**
+ * Enables or disables SimpleIconFactory objects pooling. It is enabled in production, you
+ * could use this method in tests and disable the pooling to make the icon rendering more
+ * deterministic because some sizing parameters will not be cached. Please ensure that you
+ * reset this value back after finishing the test.
+ */
+ @VisibleForTesting
+ public static void setPoolEnabled(boolean poolEnabled) {
+ sPoolEnabled = poolEnabled;
+ }
+
private static int getAttrDimFromContext(Context ctx, @AttrRes int attrId, String errorMsg) {
final Resources res = ctx.getResources();
TypedValue outVal = new TypedValue();
diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java
index 87e8ac1be611..72b9cd272d02 100644
--- a/core/java/com/android/internal/app/procstats/ProcessState.java
+++ b/core/java/com/android/internal/app/procstats/ProcessState.java
@@ -473,7 +473,10 @@ public final class ProcessState {
}
}
mCurCombinedState = state;
- mStats.mUidStates.get(mUid).updateCombinedState(state, now);
+ final UidState uidState = mStats.mUidStates.get(mUid);
+ if (uidState != null) {
+ uidState.updateCombinedState(state, now);
+ }
}
}
diff --git a/core/java/com/android/internal/inputmethod/EditableInputConnection.java b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
index 1e5208739314..f260d7dfc6a6 100644
--- a/core/java/com/android/internal/inputmethod/EditableInputConnection.java
+++ b/core/java/com/android/internal/inputmethod/EditableInputConnection.java
@@ -41,6 +41,8 @@ import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InsertGesture;
+import android.view.inputmethod.JoinOrSplitGesture;
+import android.view.inputmethod.RemoveSpaceGesture;
import android.view.inputmethod.SelectGesture;
import android.widget.TextView;
@@ -277,6 +279,10 @@ public final class EditableInputConnection extends BaseInputConnection
result = mTextView.performHandwritingDeleteGesture((DeleteGesture) gesture);
} else if (gesture instanceof InsertGesture) {
result = mTextView.performHandwritingInsertGesture((InsertGesture) gesture);
+ } else if (gesture instanceof RemoveSpaceGesture) {
+ result = mTextView.performHandwritingRemoveSpaceGesture((RemoveSpaceGesture) gesture);
+ } else if (gesture instanceof JoinOrSplitGesture) {
+ result = mTextView.performHandwritingJoinOrSplitGesture((JoinOrSplitGesture) gesture);
} else {
result = HANDWRITING_GESTURE_RESULT_UNSUPPORTED;
}
diff --git a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
index f456e85adff4..ea5c9a33b762 100644
--- a/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
+++ b/core/java/com/android/internal/inputmethod/IRemoteInputConnection.aidl
@@ -22,12 +22,14 @@ import android.view.KeyEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.DeleteGesture;
+import android.view.inputmethod.DeleteRangeGesture;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputContentInfo;
import android.view.inputmethod.InsertGesture;
import android.view.inputmethod.JoinOrSplitGesture;
import android.view.inputmethod.RemoveSpaceGesture;
import android.view.inputmethod.SelectGesture;
+import android.view.inputmethod.SelectRangeGesture;
import android.view.inputmethod.TextAttribute;
import com.android.internal.infra.AndroidFuture;
@@ -95,12 +97,18 @@ import com.android.internal.inputmethod.InputConnectionCommandHeader;
void performHandwritingSelectGesture(in InputConnectionCommandHeader header,
in SelectGesture gesture, in ResultReceiver resultReceiver);
+ void performHandwritingSelectRangeGesture(in InputConnectionCommandHeader header,
+ in SelectRangeGesture gesture, in ResultReceiver resultReceiver);
+
void performHandwritingInsertGesture(in InputConnectionCommandHeader header,
in InsertGesture gesture, in ResultReceiver resultReceiver);
void performHandwritingDeleteGesture(in InputConnectionCommandHeader header,
in DeleteGesture gesture, in ResultReceiver resultReceiver);
+ void performHandwritingDeleteRangeGesture(in InputConnectionCommandHeader header,
+ in DeleteRangeGesture gesture, in ResultReceiver resultReceiver);
+
void performHandwritingRemoveSpaceGesture(in InputConnectionCommandHeader header,
in RemoveSpaceGesture gesture, in ResultReceiver resultReceiver);
@@ -129,4 +137,7 @@ import com.android.internal.inputmethod.InputConnectionCommandHeader;
int afterLength, int flags, in AndroidFuture future /* T=SurroundingText */);
void setImeConsumesInput(in InputConnectionCommandHeader header, boolean imeConsumesInput);
+
+ void replaceText(in InputConnectionCommandHeader header, int start, int end, CharSequence text,
+ int newCursorPosition,in TextAttribute textAttribute);
}
diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
index bbf0a764d9e4..7f3144bb5894 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java
@@ -272,19 +272,24 @@ public final class InputMethodDebug {
if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_SELECT) != 0) {
joiner.add("SELECT");
}
+ if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_SELECT_RANGE) != 0) {
+ joiner.add("SELECT_RANGE");
+ }
if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_INSERT) != 0) {
joiner.add("INSERT");
}
if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_DELETE) != 0) {
joiner.add("DELETE");
}
+ if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_DELETE_RANGE) != 0) {
+ joiner.add("DELETE_RANGE");
+ }
if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_REMOVE_SPACE) != 0) {
joiner.add("REMOVE_SPACE");
}
if ((gestureTypeFlags & HandwritingGesture.GESTURE_TYPE_JOIN_OR_SPLIT) != 0) {
joiner.add("JOIN_OR_SPLIT");
}
-
return joiner.setEmptyValue("(none)").toString();
}
diff --git a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
index 6a7028d31edb..fcaa1e1c330e 100644
--- a/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
+++ b/core/java/com/android/internal/inputmethod/RemoteInputConnectionImpl.java
@@ -41,6 +41,7 @@ import android.view.ViewRootImpl;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.DeleteGesture;
+import android.view.inputmethod.DeleteRangeGesture;
import android.view.inputmethod.DumpableInputConnection;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.HandwritingGesture;
@@ -51,6 +52,7 @@ import android.view.inputmethod.InsertGesture;
import android.view.inputmethod.JoinOrSplitGesture;
import android.view.inputmethod.RemoveSpaceGesture;
import android.view.inputmethod.SelectGesture;
+import android.view.inputmethod.SelectRangeGesture;
import android.view.inputmethod.TextAttribute;
import android.view.inputmethod.TextSnapshot;
@@ -985,6 +987,14 @@ public final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub
@Dispatching(cancellable = true)
@Override
+ public void performHandwritingSelectRangeGesture(
+ InputConnectionCommandHeader header, SelectRangeGesture gesture,
+ ResultReceiver resultReceiver) {
+ performHandwritingGestureInternal(header, gesture, resultReceiver);
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
public void performHandwritingInsertGesture(
InputConnectionCommandHeader header, InsertGesture gesture,
ResultReceiver resultReceiver) {
@@ -1001,6 +1011,14 @@ public final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub
@Dispatching(cancellable = true)
@Override
+ public void performHandwritingDeleteRangeGesture(
+ InputConnectionCommandHeader header, DeleteRangeGesture gesture,
+ ResultReceiver resultReceiver) {
+ performHandwritingGestureInternal(header, gesture, resultReceiver);
+ }
+
+ @Dispatching(cancellable = true)
+ @Override
public void performHandwritingRemoveSpaceGesture(
InputConnectionCommandHeader header, RemoveSpaceGesture gesture,
ResultReceiver resultReceiver) {
@@ -1167,6 +1185,30 @@ public final class RemoteInputConnectionImpl extends IRemoteInputConnection.Stub
});
}
+ @Dispatching(cancellable = true)
+ @Override
+ public void replaceText(
+ InputConnectionCommandHeader header,
+ int start,
+ int end,
+ @NonNull CharSequence text,
+ int newCursorPosition,
+ @Nullable TextAttribute textAttribute) {
+ dispatchWithTracing(
+ "replaceText",
+ () -> {
+ if (header.mSessionId != mCurrentSessionId.get()) {
+ return; // cancelled
+ }
+ InputConnection ic = getInputConnection();
+ if (ic == null || !isActive()) {
+ Log.w(TAG, "replaceText on inactive InputConnection");
+ return;
+ }
+ ic.replaceText(start, end, text, newCursorPosition, textAttribute);
+ });
+ }
+
private final IRemoteAccessibilityInputConnection mAccessibilityInputConnection =
new IRemoteAccessibilityInputConnection.Stub() {
@Dispatching(cancellable = true)
diff --git a/core/java/com/android/internal/jank/EventLogTags.logtags b/core/java/com/android/internal/jank/EventLogTags.logtags
new file mode 100644
index 000000000000..6139bce04fb3
--- /dev/null
+++ b/core/java/com/android/internal/jank/EventLogTags.logtags
@@ -0,0 +1,10 @@
+# See system/core/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.internal.jank;
+
+# Marks a request to start tracing a CUJ. Doesn't mean the request was executed.
+37001 jank_cuj_events_begin_request (CUJ Type|1|5)
+# Marks a request to end tracing a CUJ. Doesn't mean the request was executed.
+37002 jank_cuj_events_end_request (CUJ Type|1|5)
+# Marks a request to cancel tracing a CUJ. Doesn't mean the request was executed.
+37003 jank_cuj_events_cancel_request (CUJ Type|1|5)
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index b5991b3fcbdc..76f33a6c3937 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -29,7 +29,9 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_LAUNCH_FROM_WIDGET;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_OPEN_ALL_APPS;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_QUICK_SWITCH;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_LAUNCH_CAMERA;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_APPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PASSWORD_DISAPPEAR;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_PATTERN_APPEAR;
@@ -220,6 +222,8 @@ public class InteractionJankMonitor {
public static final int CUJ_TASKBAR_EXPAND = 60;
public static final int CUJ_TASKBAR_COLLAPSE = 61;
public static final int CUJ_SHADE_CLEAR_ALL = 62;
+ public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION = 63;
+ public static final int CUJ_LOCKSCREEN_OCCLUSION = 64;
private static final int NO_STATSD_LOGGING = -1;
@@ -291,6 +295,8 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_EXPAND,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TASKBAR_COLLAPSE,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_CLEAR_ALL,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LOCKSCREEN_OCCLUSION,
};
private static class InstanceHolder {
@@ -377,7 +383,9 @@ public class InteractionJankMonitor {
CUJ_USER_DIALOG_OPEN,
CUJ_TASKBAR_EXPAND,
CUJ_TASKBAR_COLLAPSE,
- CUJ_SHADE_CLEAR_ALL
+ CUJ_SHADE_CLEAR_ALL,
+ CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION,
+ CUJ_LOCKSCREEN_OCCLUSION
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -434,6 +442,22 @@ public class InteractionJankMonitor {
@VisibleForTesting
public FrameTracker createFrameTracker(Configuration config, Session session) {
final View view = config.mView;
+
+ if (!config.hasValidView()) {
+ boolean attached = false;
+ boolean hasViewRoot = false;
+ boolean hasRenderer = false;
+ if (view != null) {
+ attached = view.isAttachedToWindow();
+ hasViewRoot = view.getViewRootImpl() != null;
+ hasRenderer = view.getThreadedRenderer() != null;
+ }
+ Log.d(TAG, "create FrameTracker fails: view=" + view
+ + ", attached=" + attached + ", hasViewRoot=" + hasViewRoot
+ + ", hasRenderer=" + hasRenderer, new Throwable());
+ return null;
+ }
+
final ThreadedRendererWrapper threadedRenderer =
view == null ? null : new ThreadedRendererWrapper(view.getThreadedRenderer());
final ViewRootWrapper viewRoot =
@@ -517,6 +541,7 @@ public class InteractionJankMonitor {
public boolean begin(@NonNull Configuration.Builder builder) {
try {
final Configuration config = builder.build();
+ EventLogTags.writeJankCujEventsBeginRequest(config.mCujType);
final TrackerResult result = new TrackerResult();
final boolean success = config.getHandler().runWithScissors(
() -> result.mResult = beginInternal(config), EXECUTOR_TASK_TIMEOUT);
@@ -541,6 +566,7 @@ public class InteractionJankMonitor {
// begin a new trace session.
tracker = createFrameTracker(conf, new Session(cujType, conf.mTag));
+ if (tracker == null) return false;
putTracker(cujType, tracker);
tracker.begin();
@@ -589,6 +615,7 @@ public class InteractionJankMonitor {
* @return boolean true if the tracker is ended successfully, false otherwise.
*/
public boolean end(@CujType int cujType) {
+ EventLogTags.writeJankCujEventsEndRequest(cujType);
FrameTracker tracker = getTracker(cujType);
// Skip this call since we haven't started a trace yet.
if (tracker == null) return false;
@@ -626,6 +653,7 @@ public class InteractionJankMonitor {
* @return boolean true if the tracker is cancelled successfully, false otherwise.
*/
public boolean cancel(@CujType int cujType) {
+ EventLogTags.writeJankCujEventsCancelRequest(cujType);
return cancel(cujType, REASON_CANCEL_NORMAL);
}
@@ -868,6 +896,10 @@ public class InteractionJankMonitor {
return "TASKBAR_COLLAPSE";
case CUJ_SHADE_CLEAR_ALL:
return "SHADE_CLEAR_ALL";
+ case CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION:
+ return "LAUNCHER_UNLOCK_ENTRANCE_ANIMATION";
+ case CUJ_LOCKSCREEN_OCCLUSION:
+ return "LOCKSCREEN_OCCLUSION";
}
return "UNKNOWN";
}
@@ -1058,9 +1090,19 @@ public class InteractionJankMonitor {
msg.append("Must pass in a valid surface control if only instrument surface; ");
}
} else {
- if (mView == null || !mView.isAttachedToWindow()) {
+ if (!hasValidView()) {
shouldThrow = true;
- msg.append("Null view or unattached view while instrumenting view; ");
+ boolean attached = false;
+ boolean hasViewRoot = false;
+ boolean hasRenderer = false;
+ if (mView != null) {
+ attached = mView.isAttachedToWindow();
+ hasViewRoot = mView.getViewRootImpl() != null;
+ hasRenderer = mView.getThreadedRenderer() != null;
+ }
+ String err = "invalid view: view=" + mView + ", attached=" + attached
+ + ", hasViewRoot=" + hasViewRoot + ", hasRenderer=" + hasRenderer;
+ msg.append(err);
}
}
if (shouldThrow) {
@@ -1068,6 +1110,12 @@ public class InteractionJankMonitor {
}
}
+ boolean hasValidView() {
+ return mSurfaceOnly
+ || (mView != null && mView.isAttachedToWindow()
+ && mView.getViewRootImpl() != null && mView.getThreadedRenderer() != null);
+ }
+
/**
* @return true if only instrumenting surface, false otherwise
*/
diff --git a/core/java/com/android/internal/jank/TEST_MAPPING b/core/java/com/android/internal/jank/TEST_MAPPING
new file mode 100644
index 000000000000..4e00ff19e9d9
--- /dev/null
+++ b/core/java/com/android/internal/jank/TEST_MAPPING
@@ -0,0 +1,22 @@
+{
+ "presubmit": [
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "com.android.internal.jank"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ],
+ "file_patterns": [
+ "core/java/com/android/internal/jank/.*",
+ "core/tests/coretests/src/com/android/internal/jank/.*"
+ ]
+ }
+ ]
+}
diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java
index f097bf73eb76..552334486356 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistory.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistory.java
@@ -247,6 +247,13 @@ public class BatteryStatsHistory {
SystemProperties.set("debug.tracing." + name, Integer.toString(value));
}
}
+
+ /**
+ * Records an instant event (one with no duration).
+ */
+ public void traceInstantEvent(@NonNull String track, @NonNull String name) {
+ Trace.instantForTrack(Trace.TRACE_TAG_POWER, track, name);
+ }
}
private TraceDelegate mTracer;
@@ -1165,6 +1172,25 @@ public class BatteryStatsHistory {
}
/**
+ * Writes event details into Atrace.
+ */
+ private void recordTraceEvents(int code, HistoryTag tag) {
+ if (code == HistoryItem.EVENT_NONE) return;
+ if (!mTracer.tracingEnabled()) return;
+
+ final int idx = code & HistoryItem.EVENT_TYPE_MASK;
+ final String prefix = (code & HistoryItem.EVENT_FLAG_START) != 0 ? "+" :
+ (code & HistoryItem.EVENT_FLAG_FINISH) != 0 ? "-" : "";
+
+ final String[] names = BatteryStats.HISTORY_EVENT_NAMES;
+ if (idx < 0 || idx >= names.length) return;
+
+ final String track = "battery_stats." + names[idx];
+ final String name = prefix + names[idx] + "=" + tag.uid + ":\"" + tag.string + "\"";
+ mTracer.traceInstantEvent(track, name);
+ }
+
+ /**
* Writes changes to a HistoryItem state bitmap to Atrace.
*/
private void recordTraceCounters(int oldval, int newval, BitDescription[] descriptions) {
@@ -1229,6 +1255,7 @@ public class BatteryStatsHistory {
+ Integer.toHexString(lastDiffStates2));
}
+ recordTraceEvents(cur.eventCode, cur.eventTag);
recordTraceCounters(mHistoryLastWritten.states,
cur.states & mActiveHistoryStates, BatteryStats.HISTORY_STATE_DESCRIPTIONS);
recordTraceCounters(mHistoryLastWritten.states2,
diff --git a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
index 50dcca64a146..664aeee6e299 100644
--- a/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
+++ b/core/java/com/android/internal/os/LongArrayMultiStateCounter.java
@@ -98,6 +98,18 @@ public final class LongArrayMultiStateCounter implements Parcelable {
native_getValues(mNativeObject, array);
}
+ /**
+ * Combines contained values into a smaller array by aggregating them
+ * according to an index map.
+ */
+ public boolean combineValues(long[] array, int[] indexMap) {
+ if (indexMap.length != mLength) {
+ throw new IllegalArgumentException(
+ "Wrong index map size " + indexMap.length + ", expected " + mLength);
+ }
+ return native_combineValues(mNativeObject, array, indexMap);
+ }
+
@Override
public String toString() {
final long[] array = new long[mLength];
@@ -116,6 +128,10 @@ public final class LongArrayMultiStateCounter implements Parcelable {
@FastNative
private native void native_getValues(long nativeObject, long[] array);
+
+ @FastNative
+ private native boolean native_combineValues(long nativeObject, long[] array,
+ int[] indexMap);
}
private static final NativeAllocationRegistry sRegistry =
diff --git a/core/java/com/android/internal/os/TimeoutRecord.java b/core/java/com/android/internal/os/TimeoutRecord.java
index a8885f9ef25b..be3f1720acdd 100644
--- a/core/java/com/android/internal/os/TimeoutRecord.java
+++ b/core/java/com/android/internal/os/TimeoutRecord.java
@@ -20,6 +20,8 @@ import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.SystemClock;
+import com.android.internal.os.anr.AnrLatencyTracker;
+
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -40,7 +42,7 @@ public class TimeoutRecord {
TimeoutKind.APP_REGISTERED})
@Retention(RetentionPolicy.SOURCE)
- private @interface TimeoutKind {
+ public @interface TimeoutKind {
int INPUT_DISPATCH_NO_FOCUSED_WINDOW = 1;
int INPUT_DISPATCH_WINDOW_UNRESPONSIVE = 2;
int BROADCAST_RECEIVER = 3;
@@ -66,12 +68,16 @@ public class TimeoutRecord {
*/
public final boolean mEndTakenBeforeLocks;
+ /** Latency tracker associated with this instance. */
+ public final AnrLatencyTracker mLatencyTracker;
+
private TimeoutRecord(@TimeoutKind int kind, @NonNull String reason, long endUptimeMillis,
boolean endTakenBeforeLocks) {
this.mKind = kind;
this.mReason = reason;
this.mEndUptimeMillis = endUptimeMillis;
this.mEndTakenBeforeLocks = endTakenBeforeLocks;
+ this.mLatencyTracker = new AnrLatencyTracker(kind, endUptimeMillis);
}
private static TimeoutRecord endingNow(@TimeoutKind int kind, String reason) {
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index ea5f0b2f2144..deafd19e866f 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -1005,7 +1005,7 @@ public final class Zygote {
* because we expect to reboot the system whenever this property changes
*/
private static final boolean ENABLE_JDWP = SystemProperties.get(
- "persist.debuggable.dalvik.vm.jdwp.enabled").equals("1");
+ "persist.debug.dalvik.vm.jdwp.enabled").equals("1");
/**
* Applies debugger system properties to the zygote arguments.
diff --git a/core/java/com/android/internal/os/anr/AnrLatencyTracker.java b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
new file mode 100644
index 000000000000..a182920cbf7a
--- /dev/null
+++ b/core/java/com/android/internal/os/anr/AnrLatencyTracker.java
@@ -0,0 +1,456 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.anr;
+
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+
+import static com.android.internal.os.TimeoutRecord.TimeoutKind;
+import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__BROADCAST_OF_INTENT;
+import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__CONTENT_PROVIDER_NOT_RESPONDING;
+import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__EXECUTING_SERVICE;
+import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__INPUT_DISPATCHING_TIMEOUT;
+import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__INPUT_DISPATCHING_TIMEOUT_NO_FOCUSED_WINDOW;
+import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__START_FOREGROUND_SERVICE;
+import static com.android.internal.util.FrameworkStatsLog.ANRLATENCY_REPORTED__ANR_TYPE__UNKNOWN_ANR_TYPE;
+
+import android.os.SystemClock;
+import android.os.Trace;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Store different latencies from the ANR flow and trace functions, it records latency breakdown
+ * for key methods, lock acquisition and other potentially expensive operations in the ANR
+ * reporting flow and exports the data as comma separated text on calling
+ * dumpAsCommaSeparatedArrayWithHeader and as an atom to statsd on being closed.
+ */
+public class AnrLatencyTracker implements AutoCloseable {
+
+ private static final AtomicInteger sNextAnrRecordPlacedOnQueueCookieGenerator =
+ new AtomicInteger();
+
+ private long mAnrTriggerUptime;
+ private long mEndUptime;
+
+ private long mAppNotRespondingStartUptime;
+ private long mAnrRecordPlacedOnQueueUptime;
+ private long mAnrProcessingStartedUptime;
+ private long mDumpStackTracesStartUptime;
+
+ private long mUpdateCpuStatsNowLastCallUptime;
+ private long mUpdateCpuStatsNowTotalLatency = 0;
+ private long mCurrentPsiStateLastCallUptime;
+ private long mCurrentPsiStateTotalLatency = 0;
+ private long mProcessCpuTrackerMethodsLastCallUptime;
+ private long mProcessCpuTrackerMethodsTotalLatency = 0;
+ private long mCriticalEventLoglastCallUptime;
+ private long mCriticalEventLogTotalLatency = 0;
+
+ private long mGlobalLockLastTryAcquireStart;
+ private long mGlobalLockTotalContention = 0;
+ private long mPidLockLastTryAcquireStart;
+ private long mPidLockTotalContention = 0;
+ private long mAMSLockLastTryAcquireStart;
+ private long mAMSLockTotalContention = 0;
+ private long mProcLockLastTryAcquireStart;
+ private long mProcLockTotalContention = 0;
+ private long mAnrRecordLastTryAcquireStart;
+ private long mAnrRecordLockTotalContention = 0;
+
+ private int mAnrQueueSize;
+ private int mAnrType;
+ private int mDumpedProcessesCount = 0;
+
+ private long mFirstPidsDumpingStart;
+ private long mFirstPidsDumpingDuration = 0;
+ private long mNativePidsDumpingStart;
+ private long mNativePidsDumpingDuration = 0;
+ private long mExtraPidsDumpingStart;
+ private long mExtraPidsDumpingDuration = 0;
+
+ private boolean mIsPushed = false;
+ private boolean mIsSkipped = false;
+
+
+ private final int mAnrRecordPlacedOnQueueCookie =
+ sNextAnrRecordPlacedOnQueueCookieGenerator.incrementAndGet();
+
+ public AnrLatencyTracker(@TimeoutKind int timeoutKind, long anrTriggerUptime) {
+ mAnrTriggerUptime = anrTriggerUptime;
+ mAnrType = timeoutKindToAnrType(timeoutKind);
+
+ }
+
+ /** Records the start of AnrHelper#appNotResponding. */
+ public void appNotRespondingStarted() {
+ mAppNotRespondingStartUptime = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ "AnrHelper#appNotResponding()");
+ }
+
+ /** Records the end of AnrHelper#appNotResponding. */
+ public void appNotRespondingEnded() {
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the placing of the AnrHelper.AnrRecord instance on the processing queue. */
+ public void anrRecordPlacingOnQueueWithSize(int queueSize) {
+ mAnrRecordPlacedOnQueueUptime = getUptimeMillis();
+ Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ "anrRecordPlacedOnQueue", mAnrRecordPlacedOnQueueCookie);
+ mAnrQueueSize = queueSize;
+ // Since we are recording the anr record queue size after pushing the current
+ // record, we need to increment the current queue size by 1
+ Trace.traceCounter(TRACE_TAG_ACTIVITY_MANAGER, "anrRecordsQueueSize", queueSize + 1);
+ }
+
+ /** Records the start of ProcessErrorStateRecord#appNotResponding. */
+ public void anrProcessingStarted() {
+ mAnrProcessingStartedUptime = getUptimeMillis();
+ Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
+ "anrRecordPlacedOnQueue", mAnrRecordPlacedOnQueueCookie);
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ "anrProcessing");
+ }
+
+ /** Records the end of ProcessErrorStateRecord#appNotResponding, the tracker is closed here. */
+ public void anrProcessingEnded() {
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ close();
+ }
+
+ /** Records the start of ActivityManagerService#dumpStackTraces. */
+ public void dumpStackTracesStarted() {
+ mDumpStackTracesStartUptime = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+ "dumpStackTraces()");
+ }
+
+ /** Records the end of ActivityManagerService#dumpStackTraces. */
+ public void dumpStackTracesEnded() {
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of ActivityManagerService#updateCpuStatsNow. */
+ public void updateCpuStatsNowCalled() {
+ mUpdateCpuStatsNowLastCallUptime = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "updateCpuStatsNow()");
+ }
+
+ /** Records the return of ActivityManagerService#updateCpuStatsNow. */
+ public void updateCpuStatsNowReturned() {
+ mUpdateCpuStatsNowTotalLatency +=
+ getUptimeMillis() - mUpdateCpuStatsNowLastCallUptime;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of ResourcePressureUtil#currentPsiState. */
+ public void currentPsiStateCalled() {
+ mCurrentPsiStateLastCallUptime = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "currentPsiState()");
+ }
+
+ /** Records the return of ResourcePressureUtil#currentPsiState. */
+ public void currentPsiStateReturned() {
+ mCurrentPsiStateTotalLatency += getUptimeMillis() - mCurrentPsiStateLastCallUptime;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of ProcessCpuTracker methods. */
+ public void processCpuTrackerMethodsCalled() {
+ mProcessCpuTrackerMethodsLastCallUptime = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "processCpuTracker");
+ }
+
+ /** Records the return of ProcessCpuTracker methods. */
+ public void processCpuTrackerMethodsReturned() {
+ mProcessCpuTrackerMethodsTotalLatency +=
+ getUptimeMillis() - mProcessCpuTrackerMethodsLastCallUptime;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of ANR headers dumping to file (subject and criticalEventSection). */
+ public void criticalEventLogStarted() {
+ mCriticalEventLoglastCallUptime = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "criticalEventLog");
+ }
+
+ /** Records the end of ANR headers dumping to file (subject and criticalEventSection). */
+ public void criticalEventLogEnded() {
+ mCriticalEventLogTotalLatency +=
+ getUptimeMillis() - mCriticalEventLoglastCallUptime;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of native pid collection. */
+ public void nativePidCollectionStarted() {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "nativePidCollection");
+ }
+
+ /** Records the end of native pid collection. */
+ public void nativePidCollectionEnded() {
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of pid dumping to file (subject and criticalEventSection). */
+ public void dumpingPidStarted(int pid) {
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingPid#" + pid);
+ }
+
+ /** Records the end of pid dumping to file (subject and criticalEventSection). */
+ public void dumpingPidEnded() {
+ mDumpedProcessesCount++;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of pid dumping to file (subject and criticalEventSection). */
+ public void dumpingFirstPidsStarted() {
+ mFirstPidsDumpingStart = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingFirstPids");
+ }
+
+ /** Records the end of pid dumping to file (subject and criticalEventSection). */
+ public void dumpingFirstPidsEnded() {
+ mFirstPidsDumpingDuration = getUptimeMillis() - mFirstPidsDumpingStart;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of pid dumping to file (subject and criticalEventSection). */
+ public void dumpingNativePidsStarted() {
+ mNativePidsDumpingStart = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingNativePids");
+ }
+
+ /** Records the end of pid dumping to file (subject and criticalEventSection). */
+ public void dumpingNativePidsEnded() {
+ mNativePidsDumpingDuration = getUptimeMillis() - mNativePidsDumpingStart;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of pid dumping to file (subject and criticalEventSection). */
+ public void dumpingExtraPidsStarted() {
+ mExtraPidsDumpingStart = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dumpingExtraPids");
+ }
+
+ /** Records the end of pid dumping to file (subject and criticalEventSection). */
+ public void dumpingExtraPidsEnded() {
+ mExtraPidsDumpingDuration = getUptimeMillis() - mExtraPidsDumpingStart;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of contention on ActivityManagerService.mGlobalLock. */
+ public void waitingOnGlobalLockStarted() {
+ mGlobalLockLastTryAcquireStart = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "globalLock");
+ }
+
+ /** Records the end of contention on ActivityManagerService.mGlobalLock. */
+ public void waitingOnGlobalLockEnded() {
+ mGlobalLockTotalContention += getUptimeMillis() - mGlobalLockLastTryAcquireStart;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of contention on ActivityManagerService.mPidsSelfLocked. */
+ public void waitingOnPidLockStarted() {
+ mPidLockLastTryAcquireStart = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "pidLockContention");
+ }
+
+ /** Records the end of contention on ActivityManagerService.mPidsSelfLocked. */
+ public void waitingOnPidLockEnded() {
+ mPidLockTotalContention += getUptimeMillis() - mPidLockLastTryAcquireStart;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of contention on ActivityManagerService. */
+ public void waitingOnAMSLockStarted() {
+ mAMSLockLastTryAcquireStart = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "AMSLockContention");
+ }
+
+ /** Records the end of contention on ActivityManagerService. */
+ public void waitingOnAMSLockEnded() {
+ mAMSLockTotalContention += getUptimeMillis() - mAMSLockLastTryAcquireStart;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of contention on ActivityManagerService.mProcLock. */
+ public void waitingOnProcLockStarted() {
+ mProcLockLastTryAcquireStart = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "procLockContention");
+ }
+
+ /** Records the start of contention on ActivityManagerService.mProcLock. */
+ public void waitingOnProcLockEnded() {
+ mProcLockTotalContention += getUptimeMillis() - mProcLockLastTryAcquireStart;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Records the start of contention on AnrHelper.mAnrRecords. */
+ public void waitingOnAnrRecordLockStarted() {
+ mAnrRecordLastTryAcquireStart = getUptimeMillis();
+ Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "anrRecordLockContention");
+ }
+
+ /** Records the end of contention on AnrHelper.mAnrRecords. */
+ public void waitingOnAnrRecordLockEnded() {
+ mAnrRecordLockTotalContention +=
+ getUptimeMillis() - mAnrRecordLastTryAcquireStart;
+ Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+ }
+
+ /** Counts the number of records in the records queue when the ANR record is popped. */
+ public void anrRecordsQueueSizeWhenPopped(int queueSize) {
+ Trace.traceCounter(TRACE_TAG_ACTIVITY_MANAGER, "anrRecordsQueueSize", queueSize);
+ }
+
+ /** Records a skipped ANR in ProcessErrorStateRecord#appNotResponding. */
+ public void anrSkippedProcessErrorStateRecordAppNotResponding() {
+ anrSkipped("appNotResponding");
+ }
+
+ /** Records a skipped ANR in ActivityManagerService#dumpStackTraces. */
+ public void anrSkippedDumpStackTraces() {
+ anrSkipped("dumpStackTraces");
+ }
+
+ /**
+ * Returns latency data as a comma separated value string for inclusion in ANR report.
+ */
+ public String dumpAsCommaSeparatedArrayWithHeader() {
+ return "DurationsV1: " + mAnrTriggerUptime
+ /* triggering_to_app_not_responding_duration = */
+ + "," + (mAppNotRespondingStartUptime - mAnrTriggerUptime)
+ /* app_not_responding_duration = */
+ + "," + (mAnrRecordPlacedOnQueueUptime - mAppNotRespondingStartUptime)
+ /* anr_record_placed_on_queue_duration = */
+ + "," + (mAnrProcessingStartedUptime - mAnrRecordPlacedOnQueueUptime)
+ /* anr_processing_duration = */
+ + "," + (mDumpStackTracesStartUptime - mAnrProcessingStartedUptime)
+
+ /* update_cpu_stats_now_total_duration = */
+ + "," + mUpdateCpuStatsNowTotalLatency
+ /* current_psi_state_total_duration = */
+ + "," + mCurrentPsiStateTotalLatency
+ /* process_cpu_tracker_methods_total_duration = */
+ + "," + mProcessCpuTrackerMethodsTotalLatency
+ /* critical_event_log_duration = */
+ + "," + mCriticalEventLogTotalLatency
+
+ /* global_lock_total_contention = */
+ + "," + mGlobalLockTotalContention
+ /* pid_lock_total_contention = */
+ + "," + mPidLockTotalContention
+ /* ams_lock_total_contention = */
+ + "," + mAMSLockTotalContention
+ /* proc_lock_total_contention = */
+ + "," + mProcLockTotalContention
+ /* anr_record_lock_total_contention = */
+ + "," + mAnrRecordLockTotalContention
+
+ /* anr_queue_size_when_pushed = */
+ + "," + mAnrQueueSize
+ /* dumped_processes_count = */
+ + "," + mDumpedProcessesCount
+ + "\n\n";
+
+ }
+
+ /**
+ * Closes the ANR latency instance by writing the atom to statsd, this method is idempotent.
+ */
+ @Override
+ public void close() {
+ if (!mIsSkipped && !mIsPushed) {
+ mEndUptime = getUptimeMillis();
+ pushAtom();
+ mIsPushed = true;
+ }
+ }
+
+ private static int timeoutKindToAnrType(@TimeoutKind int timeoutKind) {
+ switch (timeoutKind) {
+ case TimeoutKind.INPUT_DISPATCH_NO_FOCUSED_WINDOW:
+ return ANRLATENCY_REPORTED__ANR_TYPE__INPUT_DISPATCHING_TIMEOUT_NO_FOCUSED_WINDOW;
+ case TimeoutKind.INPUT_DISPATCH_WINDOW_UNRESPONSIVE:
+ return ANRLATENCY_REPORTED__ANR_TYPE__INPUT_DISPATCHING_TIMEOUT;
+ case TimeoutKind.BROADCAST_RECEIVER:
+ return ANRLATENCY_REPORTED__ANR_TYPE__BROADCAST_OF_INTENT;
+ case TimeoutKind.SERVICE_START:
+ return ANRLATENCY_REPORTED__ANR_TYPE__START_FOREGROUND_SERVICE;
+ case TimeoutKind.SERVICE_EXEC:
+ return ANRLATENCY_REPORTED__ANR_TYPE__EXECUTING_SERVICE;
+ case TimeoutKind.CONTENT_PROVIDER:
+ return ANRLATENCY_REPORTED__ANR_TYPE__CONTENT_PROVIDER_NOT_RESPONDING;
+ default:
+ return ANRLATENCY_REPORTED__ANR_TYPE__UNKNOWN_ANR_TYPE;
+ }
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public long getUptimeMillis() {
+ return SystemClock.uptimeMillis();
+ }
+
+ /** @hide */
+ @VisibleForTesting
+ public void pushAtom() {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.ANR_LATENCY_REPORTED,
+
+ /* total_duration = */ mEndUptime - mAnrTriggerUptime,
+ /* triggering_to_stack_dump_duration = */
+ mFirstPidsDumpingStart - mAnrTriggerUptime,
+ /* triggering_to_app_not_responding_duration = */
+ mAppNotRespondingStartUptime - mAnrTriggerUptime,
+ /* app_not_responding_duration = */
+ mAnrRecordPlacedOnQueueUptime - mAppNotRespondingStartUptime,
+ /* anr_record_placed_on_queue_duration = */
+ mAnrProcessingStartedUptime - mAnrRecordPlacedOnQueueUptime,
+ /* anr_processing_duration = */
+ mDumpStackTracesStartUptime - mAnrProcessingStartedUptime,
+ /* dump_stack_traces_duration = */ mFirstPidsDumpingDuration
+ + mNativePidsDumpingDuration
+ + mExtraPidsDumpingDuration,
+
+ /* update_cpu_stats_now_total_duration = */ mUpdateCpuStatsNowTotalLatency,
+ /* current_psi_state_total_duration = */ mCurrentPsiStateTotalLatency,
+ /* process_cpu_tracker_methods_total_duration = */
+ mProcessCpuTrackerMethodsTotalLatency,
+ /* critical_event_log_duration = */ mCriticalEventLogTotalLatency,
+
+ /* global_lock_total_contention = */ mGlobalLockTotalContention,
+ /* pid_lock_total_contention = */ mPidLockTotalContention,
+ /* ams_lock_total_contention = */ mAMSLockTotalContention,
+ /* proc_lock_total_contention = */ mProcLockTotalContention,
+ /* anr_record_lock_total_contention = */ mAnrRecordLockTotalContention,
+
+ /* anr_queue_size_when_pushed = */ mAnrQueueSize,
+ /* anr_type = */ mAnrType,
+ /* dumped_processes_count = */ mDumpedProcessesCount);
+ }
+
+ private void anrSkipped(String method) {
+ Trace.instant(TRACE_TAG_ACTIVITY_MANAGER, "AnrSkipped@" + method);
+ mIsSkipped = true;
+ }
+}
diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java
index 0e8dc071cbdc..8f943ef654f9 100644
--- a/core/java/com/android/internal/protolog/ProtoLogGroup.java
+++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java
@@ -88,6 +88,7 @@ public enum ProtoLogGroup implements IProtoLogGroup {
WM_DEBUG_WALLPAPER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM),
WM_DEBUG_BACK_PREVIEW(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
"CoreBackPreview"),
+ WM_DEBUG_DREAM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM),
TEST_GROUP(true, true, false, "WindowManagerProtoLogTest");
private final boolean mEnabled;
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 14a6d5e4d08c..8fcb6d5514d4 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -16,6 +16,28 @@ package com.android.internal.util;
import static android.os.Trace.TRACE_TAG_APP;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH;
+import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__UNKNOWN_ACTION;
+
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -30,6 +52,7 @@ import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.EventLogTags;
import com.android.internal.os.BackgroundThread;
@@ -103,14 +126,14 @@ public class LatencyTracker {
public static final int ACTION_START_RECENTS_ANIMATION = 8;
/**
- * Time it takes the sensor to detect rotation.
+ * Time it takes to for the camera based algorithm to rotate the screen.
*/
- public static final int ACTION_ROTATE_SCREEN_SENSOR = 9;
+ public static final int ACTION_ROTATE_SCREEN_CAMERA_CHECK = 9;
/**
- * Time it takes to for the camera based algorithm to rotate the screen.
+ * Time it takes the sensor to detect rotation.
*/
- public static final int ACTION_ROTATE_SCREEN_CAMERA_CHECK = 10;
+ public static final int ACTION_ROTATE_SCREEN_SENSOR = 10;
/**
* Time it takes to start unlock animation .
@@ -143,9 +166,20 @@ public class LatencyTracker {
public static final int ACTION_LOAD_SHARE_SHEET = 16;
/**
+ * Time it takes for showing the selection toolbar.
+ */
+ public static final int ACTION_SHOW_SELECTION_TOOLBAR = 17;
+
+ /**
* Time it takes to show AOD display after folding the device.
*/
- public static final int ACTION_FOLD_TO_AOD = 17;
+ public static final int ACTION_FOLD_TO_AOD = 18;
+
+ /**
+ * Time it takes to show the {@link android.service.voice.VoiceInteractionSession} system UI
+ * after a {@link android.hardware.soundtrigger3.ISoundTriggerHw} voice trigger.
+ */
+ public static final int ACTION_SHOW_VOICE_INTERACTION = 19;
private static final int[] ACTIONS_ALL = {
ACTION_EXPAND_PANEL,
@@ -157,15 +191,17 @@ public class LatencyTracker {
ACTION_ROTATE_SCREEN,
ACTION_FACE_WAKE_AND_UNLOCK,
ACTION_START_RECENTS_ANIMATION,
- ACTION_ROTATE_SCREEN_SENSOR,
ACTION_ROTATE_SCREEN_CAMERA_CHECK,
+ ACTION_ROTATE_SCREEN_SENSOR,
ACTION_LOCKSCREEN_UNLOCK,
ACTION_USER_SWITCH,
ACTION_SWITCH_DISPLAY_UNFOLD,
ACTION_UDFPS_ILLUMINATE,
ACTION_SHOW_BACK_ARROW,
ACTION_LOAD_SHARE_SHEET,
+ ACTION_SHOW_SELECTION_TOOLBAR,
ACTION_FOLD_TO_AOD,
+ ACTION_SHOW_VOICE_INTERACTION,
};
/** @hide */
@@ -179,39 +215,44 @@ public class LatencyTracker {
ACTION_ROTATE_SCREEN,
ACTION_FACE_WAKE_AND_UNLOCK,
ACTION_START_RECENTS_ANIMATION,
- ACTION_ROTATE_SCREEN_SENSOR,
ACTION_ROTATE_SCREEN_CAMERA_CHECK,
+ ACTION_ROTATE_SCREEN_SENSOR,
ACTION_LOCKSCREEN_UNLOCK,
ACTION_USER_SWITCH,
ACTION_SWITCH_DISPLAY_UNFOLD,
ACTION_UDFPS_ILLUMINATE,
ACTION_SHOW_BACK_ARROW,
ACTION_LOAD_SHARE_SHEET,
- ACTION_FOLD_TO_AOD
+ ACTION_SHOW_SELECTION_TOOLBAR,
+ ACTION_FOLD_TO_AOD,
+ ACTION_SHOW_VOICE_INTERACTION,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Action {
}
- private static final int[] STATSD_ACTION = new int[]{
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET,
- FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD
+ @VisibleForTesting
+ public static final int[] STATSD_ACTION = new int[] {
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD,
+ UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION,
};
private static LatencyTracker sLatencyTracker;
@@ -269,44 +310,48 @@ public class LatencyTracker {
public static String getNameOfAction(int atomsProtoAction) {
// Defined in AtomsProto.java
switch (atomsProtoAction) {
- case 0:
+ case UIACTION_LATENCY_REPORTED__ACTION__UNKNOWN_ACTION:
return "UNKNOWN";
- case 1:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL:
return "ACTION_EXPAND_PANEL";
- case 2:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS:
return "ACTION_TOGGLE_RECENTS";
- case 3:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK:
return "ACTION_FINGERPRINT_WAKE_AND_UNLOCK";
- case 4:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL:
return "ACTION_CHECK_CREDENTIAL";
- case 5:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED:
return "ACTION_CHECK_CREDENTIAL_UNLOCKED";
- case 6:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_TURN_ON_SCREEN:
return "ACTION_TURN_ON_SCREEN";
- case 7:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN:
return "ACTION_ROTATE_SCREEN";
- case 8:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK:
return "ACTION_FACE_WAKE_AND_UNLOCK";
- case 9:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_START_RECENTS_ANIMATION:
return "ACTION_START_RECENTS_ANIMATION";
- case 10:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_CAMERA_CHECK:
return "ACTION_ROTATE_SCREEN_CAMERA_CHECK";
- case 11:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_ROTATE_SCREEN_SENSOR:
return "ACTION_ROTATE_SCREEN_SENSOR";
- case 12:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOCKSCREEN_UNLOCK:
return "ACTION_LOCKSCREEN_UNLOCK";
- case 13:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_USER_SWITCH:
return "ACTION_USER_SWITCH";
- case 14:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SWITCH_DISPLAY_UNFOLD:
return "ACTION_SWITCH_DISPLAY_UNFOLD";
- case 15:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_UDFPS_ILLUMINATE:
return "ACTION_UDFPS_ILLUMINATE";
- case 16:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_BACK_ARROW:
return "ACTION_SHOW_BACK_ARROW";
- case 17:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_LOAD_SHARE_SHEET:
return "ACTION_LOAD_SHARE_SHEET";
- case 19:
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_SELECTION_TOOLBAR:
+ return "ACTION_SHOW_SELECTION_TOOLBAR";
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_FOLD_TO_AOD:
return "ACTION_FOLD_TO_AOD";
+ case UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHOW_VOICE_INTERACTION:
+ return "ACTION_SHOW_VOICE_INTERACTION";
default:
throw new IllegalArgumentException("Invalid action");
}
diff --git a/core/java/com/android/internal/util/ScreenshotHelper.java b/core/java/com/android/internal/util/ScreenshotHelper.java
index 9474f6fc3252..79c519645a24 100644
--- a/core/java/com/android/internal/util/ScreenshotHelper.java
+++ b/core/java/com/android/internal/util/ScreenshotHelper.java
@@ -377,6 +377,9 @@ public class ScreenshotHelper {
msg.replyTo = new Messenger(h);
if (mScreenshotConnection == null || mScreenshotService == null) {
+ if (mScreenshotConnection != null) {
+ resetConnection();
+ }
final ComponentName serviceComponent = ComponentName.unflattenFromString(
mContext.getResources().getString(
com.android.internal.R.string.config_screenshotServiceComponent));
diff --git a/core/java/com/android/internal/util/TEST_MAPPING b/core/java/com/android/internal/util/TEST_MAPPING
index 41d59bbeb801..00a8118c0e4b 100644
--- a/core/java/com/android/internal/util/TEST_MAPPING
+++ b/core/java/com/android/internal/util/TEST_MAPPING
@@ -15,6 +15,21 @@
}
],
"file_patterns": ["Xml"]
+ },
+ {
+ "name": "FrameworksCoreTests",
+ "options": [
+ {
+ "include-filter": "com.android.internal.util.LatencyTrackerTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "org.junit.Ignore"
+ }
+ ],
+ "file_patterns": ["LatencyTracker.java"]
}
]
}
diff --git a/core/java/com/android/internal/util/TypedProperties.java b/core/java/com/android/internal/util/TypedProperties.java
index 5613999ea773..c10dbe7fdf3b 100644
--- a/core/java/com/android/internal/util/TypedProperties.java
+++ b/core/java/com/android/internal/util/TypedProperties.java
@@ -264,29 +264,29 @@ public class TypedProperties extends HashMap<String, Object> {
// Ensure that the type can hold this value, and return.
int width = (type >> 8) & 0xff;
switch (width) {
- case 1:
- if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
- throw new ParseException(st, "8-bit integer constant");
- }
- return new Byte((byte)value);
- case 2:
- if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
- throw new ParseException(st, "16-bit integer constant");
- }
- return new Short((short)value);
- case 4:
- if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
- throw new ParseException(st, "32-bit integer constant");
- }
- return new Integer((int)value);
- case 8:
- if (value < Long.MIN_VALUE || value > Long.MAX_VALUE) {
- throw new ParseException(st, "64-bit integer constant");
- }
- return new Long(value);
- default:
- throw new IllegalStateException(
- "Internal error; unexpected integer type width " + width);
+ case 1:
+ if (value < Byte.MIN_VALUE || value > Byte.MAX_VALUE) {
+ throw new ParseException(st, "8-bit integer constant");
+ }
+ return Byte.valueOf((byte) value);
+ case 2:
+ if (value < Short.MIN_VALUE || value > Short.MAX_VALUE) {
+ throw new ParseException(st, "16-bit integer constant");
+ }
+ return Short.valueOf((short) value);
+ case 4:
+ if (value < Integer.MIN_VALUE || value > Integer.MAX_VALUE) {
+ throw new ParseException(st, "32-bit integer constant");
+ }
+ return Integer.valueOf((int) value);
+ case 8:
+ if (value < Long.MIN_VALUE || value > Long.MAX_VALUE) {
+ throw new ParseException(st, "64-bit integer constant");
+ }
+ return Long.valueOf(value);
+ default:
+ throw new IllegalStateException(
+ "Internal error; unexpected integer type width " + width);
}
} else if ((type & 0xff) == 'F') {
if (token != StreamTokenizer.TT_WORD) {
@@ -317,10 +317,10 @@ public class TypedProperties extends HashMap<String, Object> {
throw new ParseException(st, "32-bit float constant");
}
}
- return new Float((float)value);
+ return Float.valueOf((float) value);
} else {
// This property is a double; no need to truncate.
- return new Double(value);
+ return Double.valueOf(value);
}
} else if (type == TYPE_STRING) {
// Expect a quoted string or the word "null".
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index f073c1c046c5..2bfde242a987 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -22,36 +22,24 @@ import static com.android.internal.util.function.pooled.PooledLambdaImpl.acquire
import android.os.Message;
import com.android.internal.util.function.DecConsumer;
-import com.android.internal.util.function.DecFunction;
import com.android.internal.util.function.DodecConsumer;
-import com.android.internal.util.function.DodecFunction;
import com.android.internal.util.function.HeptConsumer;
-import com.android.internal.util.function.HeptFunction;
import com.android.internal.util.function.HexConsumer;
-import com.android.internal.util.function.HexFunction;
import com.android.internal.util.function.NonaConsumer;
-import com.android.internal.util.function.NonaFunction;
import com.android.internal.util.function.OctConsumer;
-import com.android.internal.util.function.OctFunction;
import com.android.internal.util.function.QuadConsumer;
-import com.android.internal.util.function.QuadFunction;
import com.android.internal.util.function.QuadPredicate;
import com.android.internal.util.function.QuintConsumer;
-import com.android.internal.util.function.QuintFunction;
import com.android.internal.util.function.QuintPredicate;
import com.android.internal.util.function.TriConsumer;
-import com.android.internal.util.function.TriFunction;
import com.android.internal.util.function.TriPredicate;
import com.android.internal.util.function.UndecConsumer;
-import com.android.internal.util.function.UndecFunction;
import com.android.internal.util.function.pooled.PooledLambdaImpl.LambdaType.ReturnType;
import java.util.function.BiConsumer;
-import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Function;
-import java.util.function.Predicate;
import java.util.function.Supplier;
/**
@@ -194,40 +182,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1) }
- */
- static <A> PooledSupplier<Boolean> obtainSupplier(
- Predicate<? super A> function,
- A arg1) {
- return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null, null, null,
- null, null, null, null);
- }
-
- /**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1) }
- */
- static <A, R> PooledSupplier<R> obtainSupplier(
- Function<? super A, ? extends R> function,
- A arg1) {
- return acquire(PooledLambdaImpl.sPool,
- function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -279,42 +233,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2) }
- */
- static <A, B> PooledSupplier<Boolean> obtainSupplier(
- BiPredicate<? super A, ? super B> function,
- A arg1, B arg2) {
- return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null, null, null,
- null, null, null, null);
- }
-
- /**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2) }
- */
- static <A, B, R> PooledSupplier<R> obtainSupplier(
- BiFunction<? super A, ? super B, ? extends R> function,
- A arg1, B arg2) {
- return acquire(PooledLambdaImpl.sPool,
- function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* {@link PooledConsumer} factory
*
* @param function non-capturing lambda(typically an unbounded method reference)
@@ -411,24 +329,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledFunction} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 placeholder for a missing argument. Use {@link #__} to get one
- * @param arg2 parameter supplied to {@code function} on call
- * @return a {@link PooledFunction}, equivalent to lambda:
- * {@code (arg1) -> function(arg1, arg2) }
- */
- static <A, B, R> PooledFunction<A, R> obtainFunction(
- BiFunction<? super A, ? super B, ? extends R> function,
- ArgumentPlaceholder<A> arg1, B arg2) {
- return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* {@link PooledConsumer} factory
*
* @param function non-capturing lambda(typically an unbounded method reference)
@@ -465,24 +365,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledFunction} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 placeholder for a missing argument. Use {@link #__} to get one
- * @return a {@link PooledFunction}, equivalent to lambda:
- * {@code (arg2) -> function(arg1, arg2) }
- */
- static <A, B, R> PooledFunction<B, R> obtainFunction(
- BiFunction<? super A, ? super B, ? extends R> function,
- A arg1, ArgumentPlaceholder<B> arg2) {
- return acquire(PooledLambdaImpl.sPool,
- function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -536,25 +418,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2, arg3) }
- */
- static <A, B, C, R> PooledSupplier<R> obtainSupplier(
- TriFunction<? super A, ? super B, ? super C, ? extends R> function,
- A arg1, B arg2, C arg3) {
- return acquire(PooledLambdaImpl.sPool,
- function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* {@link PooledConsumer} factory
*
* @param function non-capturing lambda(typically an unbounded method reference)
@@ -574,25 +437,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledFunction} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 placeholder for a missing argument. Use {@link #__} to get one
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @return a {@link PooledFunction}, equivalent to lambda:
- * {@code (arg1) -> function(arg1, arg2, arg3) }
- */
- static <A, B, C, R> PooledFunction<A, R> obtainFunction(
- TriFunction<? super A, ? super B, ? super C, ? extends R> function,
- ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
- return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* {@link PooledConsumer} factory
*
* @param function non-capturing lambda(typically an unbounded method reference)
@@ -612,25 +456,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledFunction} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 placeholder for a missing argument. Use {@link #__} to get one
- * @param arg3 parameter supplied to {@code function} on call
- * @return a {@link PooledFunction}, equivalent to lambda:
- * {@code (arg2) -> function(arg1, arg2, arg3) }
- */
- static <A, B, C, R> PooledFunction<B, R> obtainFunction(
- TriFunction<? super A, ? super B, ? super C, ? extends R> function,
- A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
- return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* {@link PooledConsumer} factory
*
* @param function non-capturing lambda(typically an unbounded method reference)
@@ -650,25 +475,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledFunction} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 placeholder for a missing argument. Use {@link #__} to get one
- * @return a {@link PooledFunction}, equivalent to lambda:
- * {@code (arg3) -> function(arg1, arg2, arg3) }
- */
- static <A, B, C, R> PooledFunction<C, R> obtainFunction(
- TriFunction<? super A, ? super B, ? super C, ? extends R> function,
- A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
- return acquire(PooledLambdaImpl.sPool,
- function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -724,26 +530,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2, arg3, arg4) }
- */
- static <A, B, C, D, R> PooledSupplier<R> obtainSupplier(
- QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
- A arg1, B arg2, C arg3, D arg4) {
- return acquire(PooledLambdaImpl.sPool,
- function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* {@link PooledConsumer} factory
*
* @param function non-capturing lambda(typically an unbounded method reference)
@@ -764,26 +550,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledFunction} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 placeholder for a missing argument. Use {@link #__} to get one
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @return a {@link PooledFunction}, equivalent to lambda:
- * {@code (arg1) -> function(arg1, arg2, arg3, arg4) }
- */
- static <A, B, C, D, R> PooledFunction<A, R> obtainFunction(
- QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
- ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
- return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* {@link PooledConsumer} factory
*
* @param function non-capturing lambda(typically an unbounded method reference)
@@ -804,26 +570,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledFunction} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 placeholder for a missing argument. Use {@link #__} to get one
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @return a {@link PooledFunction}, equivalent to lambda:
- * {@code (arg2) -> function(arg1, arg2, arg3, arg4) }
- */
- static <A, B, C, D, R> PooledFunction<B, R> obtainFunction(
- QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
- A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
- return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* {@link PooledConsumer} factory
*
* @param function non-capturing lambda(typically an unbounded method reference)
@@ -844,26 +590,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledFunction} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 placeholder for a missing argument. Use {@link #__} to get one
- * @param arg4 parameter supplied to {@code function} on call
- * @return a {@link PooledFunction}, equivalent to lambda:
- * {@code (arg3) -> function(arg1, arg2, arg3, arg4) }
- */
- static <A, B, C, D, R> PooledFunction<C, R> obtainFunction(
- QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
- A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
- return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* {@link PooledConsumer} factory
*
* @param function non-capturing lambda(typically an unbounded method reference)
@@ -884,26 +610,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledFunction} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 placeholder for a missing argument. Use {@link #__} to get one
- * @return a {@link PooledFunction}, equivalent to lambda:
- * {@code (arg4) -> function(arg1, arg2, arg3, arg4) }
- */
- static <A, B, C, D, R> PooledFunction<D, R> obtainFunction(
- QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
- A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
- return acquire(PooledLambdaImpl.sPool,
- function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null, null, null,
- null, null, null, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -961,27 +667,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @param arg5 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2, arg3, arg4, arg5) }
- */
- static <A, B, C, D, E, R> PooledSupplier<R> obtainSupplier(
- QuintFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? extends R>
- function, A arg1, B arg2, C arg3, D arg4, E arg5) {
- return acquire(PooledLambdaImpl.sPool,
- function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null, null, null,
- null, null, null, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -1042,28 +727,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @param arg5 parameter supplied to {@code function} on call
- * @param arg6 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6) }
- */
- static <A, B, C, D, E, F, R> PooledSupplier<R> obtainSupplier(
- HexFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
- ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
- return acquire(PooledLambdaImpl.sPool,
- function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, null, null,
- null, null, null, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -1126,30 +789,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @param arg5 parameter supplied to {@code function} on call
- * @param arg6 parameter supplied to {@code function} on call
- * @param arg7 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7) }
- */
- static <A, B, C, D, E, F, G, R> PooledSupplier<R> obtainSupplier(
- HeptFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
- ? super G, ? extends R> function,
- A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7) {
- return acquire(PooledLambdaImpl.sPool,
- function, 7, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, null,
- null, null, null, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -1215,31 +854,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @param arg5 parameter supplied to {@code function} on call
- * @param arg6 parameter supplied to {@code function} on call
- * @param arg7 parameter supplied to {@code function} on call
- * @param arg8 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8) }
- */
- static <A, B, C, D, E, F, G, H, R> PooledSupplier<R> obtainSupplier(
- OctFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
- ? super G, ? super H, ? extends R> function,
- A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8) {
- return acquire(PooledLambdaImpl.sPool,
- function, 8, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- null, null, null, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -1308,32 +922,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @param arg5 parameter supplied to {@code function} on call
- * @param arg6 parameter supplied to {@code function} on call
- * @param arg7 parameter supplied to {@code function} on call
- * @param arg8 parameter supplied to {@code function} on call
- * @param arg9 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9) }
- */
- static <A, B, C, D, E, F, G, H, I, R> PooledSupplier<R> obtainSupplier(
- NonaFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
- ? super G, ? super H, ? super I, ? extends R> function,
- A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9) {
- return acquire(PooledLambdaImpl.sPool,
- function, 9, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, null, null, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -1404,33 +992,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @param arg5 parameter supplied to {@code function} on call
- * @param arg6 parameter supplied to {@code function} on call
- * @param arg7 parameter supplied to {@code function} on call
- * @param arg8 parameter supplied to {@code function} on call
- * @param arg9 parameter supplied to {@code function} on call
- * @param arg10 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10) }
- */
- static <A, B, C, D, E, F, G, H, I, J, R> PooledSupplier<R> obtainSupplier(
- DecFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
- ? super G, ? super H, ? super I, ? super J, ? extends R> function,
- A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10) {
- return acquire(PooledLambdaImpl.sPool,
- function, 10, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, null, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -1504,36 +1065,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @param arg5 parameter supplied to {@code function} on call
- * @param arg6 parameter supplied to {@code function} on call
- * @param arg7 parameter supplied to {@code function} on call
- * @param arg8 parameter supplied to {@code function} on call
- * @param arg9 parameter supplied to {@code function} on call
- * @param arg10 parameter supplied to {@code function} on call
- * @param arg11 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
- * arg11) }
- */
- static <A, B, C, D, E, F, G, H, I, J, K, R> PooledSupplier<R> obtainSupplier(
- UndecFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
- ? super G, ? super H, ? super I, ? super J, ? super K, ? extends R> function,
- A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
- K arg11) {
- return acquire(PooledLambdaImpl.sPool,
- function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, arg11, null);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
@@ -1611,38 +1142,6 @@ public interface PooledLambda {
}
/**
- * {@link PooledSupplier} factory
- *
- * @param function non-capturing lambda(typically an unbounded method reference)
- * to be invoked on call
- * @param arg1 parameter supplied to {@code function} on call
- * @param arg2 parameter supplied to {@code function} on call
- * @param arg3 parameter supplied to {@code function} on call
- * @param arg4 parameter supplied to {@code function} on call
- * @param arg5 parameter supplied to {@code function} on call
- * @param arg6 parameter supplied to {@code function} on call
- * @param arg7 parameter supplied to {@code function} on call
- * @param arg8 parameter supplied to {@code function} on call
- * @param arg9 parameter supplied to {@code function} on call
- * @param arg10 parameter supplied to {@code function} on call
- * @param arg11 parameter supplied to {@code function} on call
- * @param arg12 parameter supplied to {@code function} on call
- * @return a {@link PooledSupplier}, equivalent to lambda:
- * {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
- * arg11) }
- */
- static <A, B, C, D, E, F, G, H, I, J, K, L, R> PooledSupplier<R> obtainSupplier(
- DodecFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
- ? super G, ? super H, ? super I, ? super J, ? super K, ? extends L,
- ? extends R> function,
- A arg1, B arg2, C arg3, D arg4, E arg5, F arg6, G arg7, H arg8, I arg9, J arg10,
- K arg11, L arg12) {
- return acquire(PooledLambdaImpl.sPool,
- function, 11, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8,
- arg9, arg10, arg11, arg12);
- }
-
- /**
* Factory of {@link Message}s that contain an
* ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
* {@link Message#getCallback internal callback}.
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 3e0a6cb1cae0..65c2d00fda06 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -164,6 +164,7 @@ public class LockPatternUtils {
private static final String LOCK_SCREEN_DEVICE_OWNER_INFO = "lockscreen.device_owner_info";
private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents";
+ private static final String KNOWN_TRUST_AGENTS = "lockscreen.knowntrustagents";
private static final String IS_TRUST_USUALLY_MANAGED = "lockscreen.istrustusuallymanaged";
public static final String CURRENT_LSKF_BASED_PROTECTOR_ID_KEY = "sp-handle";
@@ -1089,31 +1090,50 @@ public class LockPatternUtils {
return getString(LOCKSCREEN_POWER_BUTTON_INSTANTLY_LOCKS, userId) != null;
}
+ /** Updates the list of enabled trust agent in LockSettings storage for the given user. */
public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents, int userId) {
+ setString(ENABLED_TRUST_AGENTS, serializeTrustAgents(activeTrustAgents), userId);
+ getTrustManager().reportEnabledTrustAgentsChanged(userId);
+ }
+
+ /** Returns the list of enabled trust agent in LockSettings storage for the given user. */
+ public List<ComponentName> getEnabledTrustAgents(int userId) {
+ return deserializeTrustAgents(getString(ENABLED_TRUST_AGENTS, userId));
+ }
+
+ /** Updates the list of known trust agent in LockSettings storage for the given user. */
+ public void setKnownTrustAgents(Collection<ComponentName> knownTrustAgents, int userId) {
+ setString(KNOWN_TRUST_AGENTS, serializeTrustAgents(knownTrustAgents), userId);
+ }
+
+ /** Returns the list of known trust agent in LockSettings storage for the given user. */
+ public List<ComponentName> getKnownTrustAgents(int userId) {
+ return deserializeTrustAgents(getString(KNOWN_TRUST_AGENTS, userId));
+ }
+
+ private String serializeTrustAgents(Collection<ComponentName> trustAgents) {
StringBuilder sb = new StringBuilder();
- for (ComponentName cn : activeTrustAgents) {
+ for (ComponentName cn : trustAgents) {
if (sb.length() > 0) {
sb.append(',');
}
sb.append(cn.flattenToShortString());
}
- setString(ENABLED_TRUST_AGENTS, sb.toString(), userId);
- getTrustManager().reportEnabledTrustAgentsChanged(userId);
+ return sb.toString();
}
- public List<ComponentName> getEnabledTrustAgents(int userId) {
- String serialized = getString(ENABLED_TRUST_AGENTS, userId);
- if (TextUtils.isEmpty(serialized)) {
- return new ArrayList<ComponentName>();
+ private List<ComponentName> deserializeTrustAgents(String serializedTrustAgents) {
+ if (TextUtils.isEmpty(serializedTrustAgents)) {
+ return new ArrayList<>();
}
- String[] split = serialized.split(",");
- ArrayList<ComponentName> activeTrustAgents = new ArrayList<ComponentName>(split.length);
+ String[] split = serializedTrustAgents.split(",");
+ ArrayList<ComponentName> trustAgents = new ArrayList<>(split.length);
for (String s : split) {
if (!TextUtils.isEmpty(s)) {
- activeTrustAgents.add(ComponentName.unflattenFromString(s));
+ trustAgents.add(ComponentName.unflattenFromString(s));
}
}
- return activeTrustAgents;
+ return trustAgents;
}
/**
@@ -1487,7 +1507,8 @@ public class LockPatternUtils {
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
STRONG_AUTH_REQUIRED_AFTER_TIMEOUT,
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
- STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT})
+ STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT,
+ SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED})
@Retention(RetentionPolicy.SOURCE)
public @interface StrongAuthFlags {}
@@ -1540,6 +1561,12 @@ public class LockPatternUtils {
public static final int STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT = 0x80;
/**
+ * Some authentication is required because the trustagent either timed out or was disabled
+ * manually.
+ */
+ public static final int SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED = 0x100;
+
+ /**
* Strong auth flags that do not prevent biometric methods from being accepted as auth.
* If any other flags are set, biometric authentication is disabled.
*/
diff --git a/core/java/com/android/internal/widget/PointerLocationView.java b/core/java/com/android/internal/widget/PointerLocationView.java
index 80d50ff5d310..c08f26492289 100644
--- a/core/java/com/android/internal/widget/PointerLocationView.java
+++ b/core/java/com/android/internal/widget/PointerLocationView.java
@@ -18,6 +18,7 @@ package com.android.internal.widget;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Paint;
@@ -32,6 +33,7 @@ import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.view.ISystemGestureExclusionListener;
import android.view.InputDevice;
import android.view.KeyEvent;
@@ -45,8 +47,6 @@ import android.view.WindowInsets;
import android.view.WindowManagerGlobal;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
-import java.util.ArrayList;
-
public class PointerLocationView extends View implements InputDeviceListener,
PointerEventListener {
private static final String TAG = "Pointer";
@@ -61,6 +61,9 @@ public class PointerLocationView extends View implements InputDeviceListener,
*/
private static final String GESTURE_EXCLUSION_PROP = "debug.pointerlocation.showexclusion";
+ // In case when it's in first time or no active pointer found, draw the empty state.
+ private static final PointerState EMPTY_POINTER_STATE = new PointerState();
+
public static class PointerState {
// Trace of previous points.
private float[] mTraceX = new float[32];
@@ -149,7 +152,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
private int mMaxNumPointers;
private int mActivePointerId;
@UnsupportedAppUsage
- private final ArrayList<PointerState> mPointers = new ArrayList<PointerState>();
+ private final SparseArray<PointerState> mPointers = new SparseArray<PointerState>();
private final PointerCoords mTempCoords = new PointerCoords();
private final Region mSystemGestureExclusion = new Region();
@@ -175,8 +178,6 @@ public class PointerLocationView extends View implements InputDeviceListener,
mVC = ViewConfiguration.get(c);
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
- mTextPaint.setTextSize(10
- * getResources().getDisplayMetrics().density);
mTextPaint.setARGB(255, 0, 0, 0);
mTextBackgroundPaint = new Paint();
mTextBackgroundPaint.setAntiAlias(false);
@@ -188,20 +189,19 @@ public class PointerLocationView extends View implements InputDeviceListener,
mPaint.setAntiAlias(true);
mPaint.setARGB(255, 255, 255, 255);
mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(2);
mCurrentPointPaint = new Paint();
mCurrentPointPaint.setAntiAlias(true);
mCurrentPointPaint.setARGB(255, 255, 0, 0);
mCurrentPointPaint.setStyle(Paint.Style.STROKE);
- mCurrentPointPaint.setStrokeWidth(2);
mTargetPaint = new Paint();
mTargetPaint.setAntiAlias(false);
mTargetPaint.setARGB(255, 0, 0, 192);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(false);
mPathPaint.setARGB(255, 0, 96, 255);
- mPaint.setStyle(Paint.Style.STROKE);
- mPaint.setStrokeWidth(1);
+ mPathPaint.setStyle(Paint.Style.STROKE);
+
+ configureDensityDependentFactors();
mSystemGestureExclusionPaint = new Paint();
mSystemGestureExclusionPaint.setARGB(25, 255, 0, 0);
@@ -211,8 +211,6 @@ public class PointerLocationView extends View implements InputDeviceListener,
mSystemGestureExclusionRejectedPaint.setARGB(25, 0, 0, 255);
mSystemGestureExclusionRejectedPaint.setStyle(Paint.Style.FILL_AND_STROKE);
- PointerState ps = new PointerState();
- mPointers.add(ps);
mActivePointerId = 0;
mVelocity = VelocityTracker.obtain();
@@ -308,7 +306,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
// Pointer trace.
for (int p = 0; p < NP; p++) {
- final PointerState ps = mPointers.get(p);
+ final PointerState ps = mPointers.valueAt(p);
// Draw path.
final int N = ps.mTraceCount;
@@ -319,7 +317,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
for (int i=0; i < N; i++) {
float x = ps.mTraceX[i];
float y = ps.mTraceY[i];
- if (Float.isNaN(x)) {
+ if (Float.isNaN(x) || Float.isNaN(y)) {
haveLast = false;
continue;
}
@@ -418,10 +416,6 @@ 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;
@@ -429,7 +423,7 @@ public class PointerLocationView extends View implements InputDeviceListener,
canvas.save();
canvas.translate(mWaterfallInsets.left, 0);
- final PointerState ps = mPointers.get(mActivePointerId);
+ final PointerState ps = mPointers.get(mActivePointerId, EMPTY_POINTER_STATE);
canvas.drawRect(0, mHeaderPaddingTop, itemW - 1, bottom, mTextBackgroundPaint);
canvas.drawText(mText.clear()
@@ -600,18 +594,13 @@ public class PointerLocationView extends View implements InputDeviceListener,
@Override
public void onPointerEvent(MotionEvent event) {
final int action = event.getAction();
- int NP = mPointers.size();
if (action == MotionEvent.ACTION_DOWN
|| (action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_POINTER_DOWN) {
final int index = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for down
if (action == MotionEvent.ACTION_DOWN) {
- for (int p=0; p<NP; p++) {
- final PointerState ps = mPointers.get(p);
- ps.clearTrace();
- ps.mCurDown = false;
- }
+ mPointers.clear();
mCurDown = true;
mCurNumPointers = 0;
mMaxNumPointers = 0;
@@ -627,18 +616,17 @@ public class PointerLocationView extends View implements InputDeviceListener,
}
final int id = event.getPointerId(index);
- while (NP <= id) {
- PointerState ps = new PointerState();
- mPointers.add(ps);
- NP++;
+ PointerState ps = mPointers.get(id);
+ if (ps == null) {
+ ps = new PointerState();
+ mPointers.put(id, ps);
}
- if (mActivePointerId < 0 || mActivePointerId >= NP
+ if (!mPointers.contains(mActivePointerId)
|| !mPointers.get(mActivePointerId).mCurDown) {
mActivePointerId = id;
}
- final PointerState ps = mPointers.get(id);
ps.mCurDown = true;
InputDevice device = InputDevice.getDevice(event.getDeviceId());
ps.mHasBoundingBox = device != null &&
@@ -707,13 +695,13 @@ public class PointerLocationView extends View implements InputDeviceListener,
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT; // will be 0 for UP
final int id = event.getPointerId(index);
- if (id >= NP) {
- Slog.wtf(TAG, "Got pointer ID out of bounds: id=" + id + " arraysize="
- + NP + " pointerindex=" + index
+ final PointerState ps = mPointers.get(id);
+ if (ps == null) {
+ Slog.wtf(TAG, "Could not find pointer id=" + id + " in mPointers map,"
+ + " size=" + mPointers.size() + " pointerindex=" + index
+ " action=0x" + Integer.toHexString(action));
return;
}
- final PointerState ps = mPointers.get(id);
ps.mCurDown = false;
if (action == MotionEvent.ACTION_UP
@@ -1016,4 +1004,19 @@ public class PointerLocationView extends View implements InputDeviceListener,
}
}
};
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ configureDensityDependentFactors();
+ }
+
+ // Compute size by display density.
+ private void configureDensityDependentFactors() {
+ final float density = getResources().getDisplayMetrics().density;
+ mTextPaint.setTextSize(10 * density);
+ mPaint.setStrokeWidth(1 * density);
+ mCurrentPointPaint.setStrokeWidth(1 * density);
+ mPathPaint.setStrokeWidth(1 * density);
+ }
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 5498769fcf8f..d59a51ab9174 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -209,6 +209,7 @@ cc_library_shared {
"android_content_res_ApkAssets.cpp",
"android_content_res_ObbScanner.cpp",
"android_content_res_Configuration.cpp",
+ "android_content_res_ResourceTimer.cpp",
"android_security_Scrypt.cpp",
"com_android_internal_content_om_OverlayConfig.cpp",
"com_android_internal_net_NetworkUtilsInternal.cpp",
@@ -231,6 +232,8 @@ cc_library_shared {
"android_hardware_input_InputWindowHandle.cpp",
"android_hardware_input_InputApplicationHandle.cpp",
"android_window_WindowInfosListener.cpp",
+ "android_window_ScreenCapture.cpp",
+ "jni_common.cpp",
],
static_libs: [
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 817b3154889b..68db603c3671 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -117,6 +117,7 @@ extern int register_android_util_MemoryIntArray(JNIEnv* env);
extern int register_android_content_StringBlock(JNIEnv* env);
extern int register_android_content_XmlBlock(JNIEnv* env);
extern int register_android_content_res_ApkAssets(JNIEnv* env);
+extern int register_android_content_res_ResourceTimer(JNIEnv* env);
extern int register_android_graphics_BLASTBufferQueue(JNIEnv* env);
extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
@@ -211,6 +212,8 @@ 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);
+extern int register_android_window_ScreenCapture(JNIEnv* env);
+extern int register_jni_common(JNIEnv* env);
// Namespace for Android Runtime flags applied during boot time.
static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
@@ -1504,6 +1507,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_content_StringBlock),
REG_JNI(register_android_content_XmlBlock),
REG_JNI(register_android_content_res_ApkAssets),
+ REG_JNI(register_android_content_res_ResourceTimer),
REG_JNI(register_android_text_AndroidCharacter),
REG_JNI(register_android_text_Hyphenator),
REG_JNI(register_android_view_InputDevice),
@@ -1647,6 +1651,8 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),
REG_JNI(register_android_window_WindowInfosListener),
+ REG_JNI(register_android_window_ScreenCapture),
+ REG_JNI(register_jni_common),
};
/*
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 14699e7097f4..a068008f5e22 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -22,7 +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
+per-file android_window_* = 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_content_res_ResourceTimer.cpp b/core/jni/android_content_res_ResourceTimer.cpp
new file mode 100644
index 000000000000..91e3c921afe6
--- /dev/null
+++ b/core/jni/android_content_res_ResourceTimer.cpp
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2022, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <jni.h>
+#include <core_jni_helpers.h>
+#include <utils/misc.h>
+#include <androidfw/ResourceTimer.h>
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+static struct {
+ jfieldID maxTimer;
+ jfieldID maxBuckets;
+ jfieldID maxLargest;
+ jfieldID timers;
+} gConfigOffsets;
+
+static struct {
+ jfieldID count;
+ jfieldID total;
+ jfieldID mintime;
+ jfieldID maxtime;
+ jfieldID largest;
+ jfieldID percentile;
+} gTimerOffsets;
+
+// ----------------------------------------------------------------------------
+
+
+static int NativeGetTimers(JNIEnv* env, jobject /*clazz*/, jobjectArray timer, jboolean reset) {
+ size_t size = ResourceTimer::counterSize;
+ if (jsize st = env->GetArrayLength(timer); st < size) {
+ // Shrink the size to the minimum of the available counters and the available space.
+ size = st;
+ }
+ for (size_t i = 0; i < size; i++) {
+ ResourceTimer::Timer src;
+ ResourceTimer::copy(i, src, reset);
+ jobject dst = env->GetObjectArrayElement(timer, i);
+ env->SetIntField(dst, gTimerOffsets.count, src.count);
+ if (src.count == 0) {
+ continue;
+ }
+
+ src.compute();
+ env->SetIntField(dst, gTimerOffsets.count, src.count);
+ env->SetLongField(dst, gTimerOffsets.total, src.total);
+ env->SetIntField(dst, gTimerOffsets.mintime, src.mintime);
+ env->SetIntField(dst, gTimerOffsets.maxtime, src.maxtime);
+ jintArray percentile =
+ reinterpret_cast<jintArray>(env->GetObjectField(dst, gTimerOffsets.percentile));
+ env->SetIntArrayRegion(percentile, 0, 1, &src.pvalues.p50.nominal);
+ env->SetIntArrayRegion(percentile, 1, 1, &src.pvalues.p90.nominal);
+ env->SetIntArrayRegion(percentile, 2, 1, &src.pvalues.p95.nominal);
+ env->SetIntArrayRegion(percentile, 3, 1, &src.pvalues.p99.nominal);
+ jintArray largest =
+ reinterpret_cast<jintArray>(env->GetObjectField(dst, gTimerOffsets.largest));
+ env->SetIntArrayRegion(largest, 0, ResourceTimer::Timer::MaxLargest, src.largest);
+ }
+ return size;
+}
+
+static jstring counterName(JNIEnv *env, int counter) {
+ char const *s = ResourceTimer::toString(static_cast<ResourceTimer::Counter>(counter));
+ return env->NewStringUTF(s);
+}
+
+static int NativeEnableTimers(JNIEnv* env, jobject /*clazz*/, jobject config) {
+ ResourceTimer::enable();
+
+ env->SetIntField(config, gConfigOffsets.maxTimer, ResourceTimer::counterSize);
+ env->SetIntField(config, gConfigOffsets.maxBuckets, 4); // Number of ints in PValues
+ env->SetIntField(config, gConfigOffsets.maxLargest, ResourceTimer::Timer::MaxLargest);
+
+ jclass str = env->FindClass("java/lang/String");
+ jstring empty = counterName(env, 0);
+ jobjectArray timers = env->NewObjectArray(ResourceTimer::counterSize, str, empty);
+ for (int i = 0; i < ResourceTimer::counterSize; i++) {
+ env->SetObjectArrayElement(timers, i, counterName(env, i));
+ }
+ env->SetObjectField(config, gConfigOffsets.timers, timers);
+ return 0;
+}
+
+// ----------------------------------------------------------------------------
+
+// JNI registration.
+static const JNINativeMethod gResourceTimerMethods[] = {
+ {"nativeEnableTimers", "(Landroid/content/res/ResourceTimer$Config;)I",
+ (void *) NativeEnableTimers},
+ {"nativeGetTimers", "([Landroid/content/res/ResourceTimer$Timer;Z)I",
+ (void *) NativeGetTimers},
+};
+
+int register_android_content_res_ResourceTimer(JNIEnv* env) {
+ jclass config = FindClassOrDie(env, "android/content/res/ResourceTimer$Config");
+ gConfigOffsets.maxTimer = GetFieldIDOrDie(env, config, "maxTimer", "I");
+ gConfigOffsets.maxBuckets = GetFieldIDOrDie(env, config, "maxBuckets", "I");
+ gConfigOffsets.maxLargest = GetFieldIDOrDie(env, config, "maxLargest", "I");
+ gConfigOffsets.timers = GetFieldIDOrDie(env, config, "timers", "[Ljava/lang/String;");
+
+ jclass timers = FindClassOrDie(env, "android/content/res/ResourceTimer$Timer");
+ gTimerOffsets.count = GetFieldIDOrDie(env, timers, "count", "I");
+ gTimerOffsets.total = GetFieldIDOrDie(env, timers, "total", "J");
+ gTimerOffsets.mintime = GetFieldIDOrDie(env, timers, "mintime", "I");
+ gTimerOffsets.maxtime = GetFieldIDOrDie(env, timers, "maxtime", "I");
+ gTimerOffsets.largest = GetFieldIDOrDie(env, timers, "largest", "[I");
+ gTimerOffsets.percentile = GetFieldIDOrDie(env, timers, "percentile", "[I");
+
+ return RegisterMethodsOrDie(env, "android/content/res/ResourceTimer", gResourceTimerMethods,
+ NELEM(gResourceTimerMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index f24c66695052..77317d1c3c1a 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2179,7 +2179,7 @@ static jint convertAudioMixToNative(JNIEnv *env,
break;
}
- nAudioMix->mCriteria.add(nCriterion);
+ nAudioMix->mCriteria.push_back(nCriterion);
env->DeleteLocalRef(jCriterion);
}
@@ -2937,13 +2937,14 @@ static jint android_media_AudioSystem_getDirectProfilesForAttributes(JNIEnv *env
for (const auto &audioProfile : audioProfiles) {
jobject jAudioProfile;
- jStatus = convertAudioProfileFromNative(env, &jAudioProfile, &audioProfile, false);
- if (jStatus == AUDIO_JAVA_BAD_VALUE) {
+ jint jConvertProfileStatus = convertAudioProfileFromNative(
+ env, &jAudioProfile, &audioProfile, false);
+ if (jConvertProfileStatus == AUDIO_JAVA_BAD_VALUE) {
// skipping Java layer unsupported audio formats
continue;
}
- if (jStatus != AUDIO_JAVA_SUCCESS) {
- return jStatus;
+ if (jConvertProfileStatus != AUDIO_JAVA_SUCCESS) {
+ return jConvertProfileStatus;
}
env->CallBooleanMethod(jAudioProfilesList, gArrayListMethods.add, jAudioProfile);
env->DeleteLocalRef(jAudioProfile);
diff --git a/core/jni/android_os_MessageQueue.cpp b/core/jni/android_os_MessageQueue.cpp
index f7a98d16f2e3..30d9ea19be39 100644
--- a/core/jni/android_os_MessageQueue.cpp
+++ b/core/jni/android_os_MessageQueue.cpp
@@ -51,6 +51,21 @@ public:
virtual int handleEvent(int fd, int events, void* data);
+ /**
+ * A simple proxy that holds a weak reference to a looper callback.
+ */
+ class WeakLooperCallback : public LooperCallback {
+ protected:
+ virtual ~WeakLooperCallback();
+
+ public:
+ WeakLooperCallback(const wp<LooperCallback>& callback);
+ virtual int handleEvent(int fd, int events, void* data);
+
+ private:
+ wp<LooperCallback> mCallback;
+ };
+
private:
JNIEnv* mPollEnv;
jobject mPollObj;
@@ -131,7 +146,8 @@ void NativeMessageQueue::setFileDescriptorEvents(int fd, int events) {
if (events & CALLBACK_EVENT_OUTPUT) {
looperEvents |= Looper::EVENT_OUTPUT;
}
- mLooper->addFd(fd, Looper::POLL_CALLBACK, looperEvents, this,
+ mLooper->addFd(fd, Looper::POLL_CALLBACK, looperEvents,
+ sp<WeakLooperCallback>::make(this),
reinterpret_cast<void*>(events));
} else {
mLooper->removeFd(fd);
@@ -162,6 +178,24 @@ int NativeMessageQueue::handleEvent(int fd, int looperEvents, void* data) {
}
+// --- NativeMessageQueue::WeakLooperCallback ---
+
+NativeMessageQueue::WeakLooperCallback::WeakLooperCallback(const wp<LooperCallback>& callback) :
+ mCallback(callback) {
+}
+
+NativeMessageQueue::WeakLooperCallback::~WeakLooperCallback() {
+}
+
+int NativeMessageQueue::WeakLooperCallback::handleEvent(int fd, int events, void* data) {
+ sp<LooperCallback> callback = mCallback.promote();
+ if (callback != nullptr) {
+ return callback->handleEvent(fd, events, data);
+ }
+ return 0;
+}
+
+
// ----------------------------------------------------------------------------
sp<MessageQueue> android_os_MessageQueue_getMessageQueue(JNIEnv* env, jobject messageQueueObj) {
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 8c23b214b8fe..b60ec9f493c2 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -39,6 +39,7 @@
#include "androidfw/AssetManager2.h"
#include "androidfw/AttributeResolution.h"
#include "androidfw/MutexGuard.h"
+#include <androidfw/ResourceTimer.h>
#include "androidfw/ResourceTypes.h"
#include "androidfw/ResourceUtils.h"
@@ -630,6 +631,7 @@ static jint NativeGetResourceValue(JNIEnv* env, jclass /*clazz*/, jlong ptr, jin
jshort density, jobject typed_value,
jboolean resolve_references) {
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ ResourceTimer _timer(ResourceTimer::Counter::GetResourceValue);
auto value = assetmanager->GetResource(static_cast<uint32_t>(resid), false /*may_be_bag*/,
static_cast<uint16_t>(density));
if (!value.has_value()) {
@@ -1232,6 +1234,7 @@ static jboolean NativeRetrieveAttributes(JNIEnv* env, jclass /*clazz*/, jlong pt
}
ScopedLock<AssetManager2> assetmanager(AssetManagerFromLong(ptr));
+ ResourceTimer _timer(ResourceTimer::Counter::RetrieveAttributes);
ResXMLParser* xml_parser = reinterpret_cast<ResXMLParser*>(xml_parser_ptr);
auto result =
RetrieveAttributes(assetmanager.get(), xml_parser, reinterpret_cast<uint32_t*>(attrs),
@@ -1293,6 +1296,10 @@ static void NativeThemeRebase(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong th
} else {
CHECK(style_count == 0) << "style_ids is null while style_count is non-zero";
}
+ auto style_id_args_copy = std::vector<uint32_t>{style_id_args, style_id_args + style_count};
+ if (style_ids != nullptr) {
+ env->ReleasePrimitiveArrayCritical(style_ids, style_id_args, JNI_ABORT);
+ }
jboolean* force_args = nullptr;
if (force != nullptr) {
@@ -1305,15 +1312,14 @@ static void NativeThemeRebase(JNIEnv* env, jclass /*clazz*/, jlong ptr, jlong th
} else {
CHECK(style_count == 0) << "force is null while style_count is non-zero";
}
-
- auto theme = reinterpret_cast<Theme*>(theme_ptr);
- theme->Rebase(&(*assetmanager), style_id_args, force_args, static_cast<size_t>(style_count));
- if (style_ids != nullptr) {
- env->ReleasePrimitiveArrayCritical(style_ids, style_id_args, JNI_ABORT);
- }
+ auto force_args_copy = std::vector<jboolean>{force_args, force_args + style_count};
if (force != nullptr) {
env->ReleasePrimitiveArrayCritical(force, force_args, JNI_ABORT);
}
+
+ auto theme = reinterpret_cast<Theme*>(theme_ptr);
+ theme->Rebase(&(*assetmanager), style_id_args_copy.data(), force_args_copy.data(),
+ static_cast<size_t>(style_count));
}
static void NativeThemeCopy(JNIEnv* env, jclass /*clazz*/, jlong dst_asset_manager_ptr,
diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp
index 891330082f3e..5a444bb1d0ff 100644
--- a/core/jni/android_util_XmlBlock.cpp
+++ b/core/jni/android_util_XmlBlock.cpp
@@ -28,6 +28,9 @@
#include <stdio.h>
namespace android {
+constexpr int kNullDocument = UNEXPECTED_NULL;
+// The reason not to ResXMLParser::BAD_DOCUMENT which is -1 is that other places use the same value.
+constexpr int kBadDocument = BAD_VALUE;
// ----------------------------------------------------------------------------
@@ -92,9 +95,7 @@ static jlong android_content_XmlBlock_nativeCreateParseState(JNIEnv* env, jobjec
return reinterpret_cast<jlong>(st);
}
-static jint android_content_XmlBlock_nativeNext(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_XmlBlock_nativeNext(CRITICAL_JNI_PARAMS_COMMA jlong token) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
return ResXMLParser::END_DOCUMENT;
@@ -121,14 +122,10 @@ static jint android_content_XmlBlock_nativeNext(JNIEnv* env, jobject clazz,
} while (true);
bad:
- jniThrowException(env, "org/xmlpull/v1/XmlPullParserException",
- "Corrupt XML binary file");
- return ResXMLParser::BAD_DOCUMENT;
+ return kBadDocument;
}
-static jint android_content_XmlBlock_nativeGetNamespace(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_XmlBlock_nativeGetNamespace(CRITICAL_JNI_PARAMS_COMMA jlong token) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
return -1;
@@ -137,9 +134,7 @@ static jint android_content_XmlBlock_nativeGetNamespace(JNIEnv* env, jobject cla
return static_cast<jint>(st->getElementNamespaceID());
}
-static jint android_content_XmlBlock_nativeGetName(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_XmlBlock_nativeGetName(CRITICAL_JNI_PARAMS_COMMA jlong token) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
return -1;
@@ -148,9 +143,7 @@ static jint android_content_XmlBlock_nativeGetName(JNIEnv* env, jobject clazz,
return static_cast<jint>(st->getElementNameID());
}
-static jint android_content_XmlBlock_nativeGetText(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_XmlBlock_nativeGetText(CRITICAL_JNI_PARAMS_COMMA jlong token) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
return -1;
@@ -159,97 +152,80 @@ static jint android_content_XmlBlock_nativeGetText(JNIEnv* env, jobject clazz,
return static_cast<jint>(st->getTextID());
}
-static jint android_content_XmlBlock_nativeGetLineNumber(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_XmlBlock_nativeGetLineNumber(CRITICAL_JNI_PARAMS_COMMA jlong token) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
return static_cast<jint>(st->getLineNumber());
}
-static jint android_content_XmlBlock_nativeGetAttributeCount(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_XmlBlock_nativeGetAttributeCount(
+ CRITICAL_JNI_PARAMS_COMMA jlong token) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
return static_cast<jint>(st->getAttributeCount());
}
-static jint android_content_XmlBlock_nativeGetAttributeNamespace(JNIEnv* env, jobject clazz,
- jlong token, jint idx)
-{
+static jint android_content_XmlBlock_nativeGetAttributeNamespace(
+ CRITICAL_JNI_PARAMS_COMMA jlong token, jint idx) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
return static_cast<jint>(st->getAttributeNamespaceID(idx));
}
-static jint android_content_XmlBlock_nativeGetAttributeName(JNIEnv* env, jobject clazz,
- jlong token, jint idx)
-{
+static jint android_content_XmlBlock_nativeGetAttributeName(CRITICAL_JNI_PARAMS_COMMA jlong token,
+ jint idx) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
return static_cast<jint>(st->getAttributeNameID(idx));
}
-static jint android_content_XmlBlock_nativeGetAttributeResource(JNIEnv* env, jobject clazz,
- jlong token, jint idx)
-{
+static jint android_content_XmlBlock_nativeGetAttributeResource(
+ CRITICAL_JNI_PARAMS_COMMA jlong token, jint idx) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
return static_cast<jint>(st->getAttributeNameResID(idx));
}
-static jint android_content_XmlBlock_nativeGetAttributeDataType(JNIEnv* env, jobject clazz,
- jlong token, jint idx)
-{
+static jint android_content_XmlBlock_nativeGetAttributeDataType(
+ CRITICAL_JNI_PARAMS_COMMA jlong token, jint idx) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
return static_cast<jint>(st->getAttributeDataType(idx));
}
-static jint android_content_XmlBlock_nativeGetAttributeData(JNIEnv* env, jobject clazz,
- jlong token, jint idx)
-{
+static jint android_content_XmlBlock_nativeGetAttributeData(CRITICAL_JNI_PARAMS_COMMA jlong token,
+ jint idx) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
return static_cast<jint>(st->getAttributeData(idx));
}
-static jint android_content_XmlBlock_nativeGetAttributeStringValue(JNIEnv* env, jobject clazz,
- jlong token, jint idx)
-{
+static jint android_content_XmlBlock_nativeGetAttributeStringValue(
+ CRITICAL_JNI_PARAMS_COMMA jlong token, jint idx) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
return static_cast<jint>(st->getAttributeValueStringID(idx));
@@ -286,39 +262,32 @@ static jint android_content_XmlBlock_nativeGetAttributeIndex(JNIEnv* env, jobjec
return idx;
}
-static jint android_content_XmlBlock_nativeGetIdAttribute(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_XmlBlock_nativeGetIdAttribute(CRITICAL_JNI_PARAMS_COMMA jlong token) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
ssize_t idx = st->indexOfID();
return idx >= 0 ? static_cast<jint>(st->getAttributeValueStringID(idx)) : -1;
}
-static jint android_content_XmlBlock_nativeGetClassAttribute(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_XmlBlock_nativeGetClassAttribute(
+ CRITICAL_JNI_PARAMS_COMMA jlong token) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
ssize_t idx = st->indexOfClass();
return idx >= 0 ? static_cast<jint>(st->getAttributeValueStringID(idx)) : -1;
}
-static jint android_content_XmlBlock_nativeGetStyleAttribute(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_XmlBlock_nativeGetStyleAttribute(
+ CRITICAL_JNI_PARAMS_COMMA jlong token) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
- jniThrowNullPointerException(env, NULL);
- return 0;
+ return kNullDocument;
}
ssize_t idx = st->indexOfStyle();
@@ -336,9 +305,7 @@ static jint android_content_XmlBlock_nativeGetStyleAttribute(JNIEnv* env, jobjec
? value.data : 0;
}
-static jint android_content_XmlBlock_nativeGetSourceResId(JNIEnv* env, jobject clazz,
- jlong token)
-{
+static jint android_content_XmlBlock_nativeGetSourceResId(CRITICAL_JNI_PARAMS_COMMA jlong token) {
ResXMLParser* st = reinterpret_cast<ResXMLParser*>(token);
if (st == NULL) {
return 0;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index e64b2615e1e2..b11f22a030d9 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -21,7 +21,6 @@
#include <android-base/chrono_utils.h>
#include <android/graphics/properties.h>
#include <android/graphics/region.h>
-#include <android/gui/BnScreenCaptureListener.h>
#include <android/gui/BnWindowInfosReportedListener.h>
#include <android/hardware/display/IDeviceProductInfoConstants.h>
#include <android/os/IInputConstants.h>
@@ -29,6 +28,7 @@
#include <android_runtime/android_graphics_GraphicBuffer.h>
#include <android_runtime/android_hardware_HardwareBuffer.h>
#include <android_runtime/android_view_Surface.h>
+#include <android_runtime/android_view_SurfaceControl.h>
#include <android_runtime/android_view_SurfaceSession.h>
#include <gui/ISurfaceComposer.h>
#include <gui/Surface.h>
@@ -60,12 +60,12 @@
#include "android_os_Parcel.h"
#include "android_util_Binder.h"
#include "core_jni_helpers.h"
+#include "jni_common.h"
// ----------------------------------------------------------------------------
namespace android {
-using gui::CaptureArgs;
using gui::FocusRequest;
static void doThrowNPE(JNIEnv* env) {
@@ -130,37 +130,6 @@ static struct {
jfieldID group;
} gDisplayModeClassInfo;
-static struct {
- jfieldID bottom;
- jfieldID left;
- jfieldID right;
- jfieldID top;
-} gRectClassInfo;
-
-static struct {
- jfieldID pixelFormat;
- jfieldID sourceCrop;
- jfieldID frameScaleX;
- jfieldID frameScaleY;
- jfieldID captureSecureLayers;
- jfieldID allowProtected;
- jfieldID uid;
- jfieldID grayscale;
-} gCaptureArgsClassInfo;
-
-static struct {
- jfieldID displayToken;
- jfieldID width;
- jfieldID height;
- jfieldID useIdentityTransform;
-} gDisplayCaptureArgsClassInfo;
-
-static struct {
- jfieldID layer;
- jfieldID excludeLayers;
- jfieldID childrenOnly;
-} gLayerCaptureArgsClassInfo;
-
// Implements SkMallocPixelRef::ReleaseProc, to delete the screenshot on unref.
void DeleteScreenshot(void* addr, void* context) {
delete ((ScreenshotClient*) context);
@@ -220,16 +189,6 @@ static struct {
static struct {
jclass clazz;
- jmethodID builder;
-} gScreenshotHardwareBufferClassInfo;
-
-static struct {
- jclass clazz;
- jmethodID onScreenCaptureComplete;
-} gScreenCaptureListenerClassInfo;
-
-static struct {
- jclass clazz;
jmethodID ctor;
jfieldID defaultMode;
jfieldID allowGroupSwitching;
@@ -263,26 +222,14 @@ static struct {
static struct {
jclass clazz;
- jmethodID invokeReleaseCallback;
-} gInvokeReleaseCallback;
-
-class JNamedColorSpace {
-public:
- // ColorSpace.Named.SRGB.ordinal() = 0;
- static constexpr jint SRGB = 0;
-
- // ColorSpace.Named.DISPLAY_P3.ordinal() = 7;
- static constexpr jint DISPLAY_P3 = 7;
-};
+ jfieldID mNativeObject;
+} gTransactionClassInfo;
-constexpr jint fromDataspaceToNamedColorSpaceValue(const ui::Dataspace dataspace) {
- switch (dataspace) {
- case ui::Dataspace::DISPLAY_P3:
- return JNamedColorSpace::DISPLAY_P3;
- default:
- return JNamedColorSpace::SRGB;
- }
-}
+static struct {
+ jclass clazz;
+ jfieldID mNativeObject;
+ jmethodID invokeReleaseCallback;
+} gSurfaceControlClassInfo;
constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode) {
switch (colorMode) {
@@ -296,59 +243,6 @@ constexpr ui::Dataspace pickDataspaceFromColorMode(const ui::ColorMode colorMode
}
}
-class ScreenCaptureListenerWrapper : public gui::BnScreenCaptureListener {
-public:
- explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
- env->GetJavaVM(&mVm);
- screenCaptureListenerObject = env->NewGlobalRef(jobject);
- LOG_ALWAYS_FATAL_IF(!screenCaptureListenerObject, "Failed to make global ref");
- }
-
- ~ScreenCaptureListenerWrapper() {
- if (screenCaptureListenerObject) {
- getenv()->DeleteGlobalRef(screenCaptureListenerObject);
- screenCaptureListenerObject = nullptr;
- }
- }
-
- binder::Status onScreenCaptureCompleted(
- const gui::ScreenCaptureResults& captureResults) override {
- JNIEnv* env = getenv();
- if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) {
- env->CallVoidMethod(screenCaptureListenerObject,
- gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr);
- return binder::Status::ok();
- }
- captureResults.fenceResult.value()->waitForever("");
- jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
- env, captureResults.buffer->toAHardwareBuffer());
- const jint namedColorSpace =
- fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
- jobject screenshotHardwareBuffer =
- env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
- gScreenshotHardwareBufferClassInfo.builder,
- jhardwareBuffer, namedColorSpace,
- captureResults.capturedSecureLayers,
- captureResults.capturedHdrLayers);
- env->CallVoidMethod(screenCaptureListenerObject,
- gScreenCaptureListenerClassInfo.onScreenCaptureComplete,
- screenshotHardwareBuffer);
- env->DeleteLocalRef(jhardwareBuffer);
- env->DeleteLocalRef(screenshotHardwareBuffer);
- return binder::Status::ok();
- }
-
-private:
- jobject screenCaptureListenerObject;
- JavaVM* mVm;
-
- JNIEnv* getenv() {
- JNIEnv* env;
- mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
- return env;
- }
-};
-
class TransactionCommittedListenerWrapper {
public:
explicit TransactionCommittedListenerWrapper(JNIEnv* env, jobject object) {
@@ -494,102 +388,6 @@ static void nativeSetDefaultBufferSize(JNIEnv* env, jclass clazz, jlong nativeOb
}
}
-static Rect rectFromObj(JNIEnv* env, jobject rectObj) {
- int left = env->GetIntField(rectObj, gRectClassInfo.left);
- int top = env->GetIntField(rectObj, gRectClassInfo.top);
- int right = env->GetIntField(rectObj, gRectClassInfo.right);
- int bottom = env->GetIntField(rectObj, gRectClassInfo.bottom);
- return Rect(left, top, right, bottom);
-}
-
-static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs& captureArgs) {
- captureArgs.pixelFormat = static_cast<ui::PixelFormat>(
- env->GetIntField(captureArgsObject, gCaptureArgsClassInfo.pixelFormat));
- captureArgs.sourceCrop =
- rectFromObj(env,
- env->GetObjectField(captureArgsObject, gCaptureArgsClassInfo.sourceCrop));
- captureArgs.frameScaleX =
- env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleX);
- captureArgs.frameScaleY =
- env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleY);
- captureArgs.captureSecureLayers =
- env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.captureSecureLayers);
- captureArgs.allowProtected =
- env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.allowProtected);
- captureArgs.uid = env->GetLongField(captureArgsObject, gCaptureArgsClassInfo.uid);
- captureArgs.grayscale =
- env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.grayscale);
-}
-
-static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
- jobject displayCaptureArgsObject) {
- DisplayCaptureArgs captureArgs;
- getCaptureArgs(env, displayCaptureArgsObject, captureArgs);
-
- captureArgs.displayToken =
- ibinderForJavaObject(env,
- env->GetObjectField(displayCaptureArgsObject,
- gDisplayCaptureArgsClassInfo.displayToken));
- captureArgs.width =
- env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.width);
- captureArgs.height =
- env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.height);
- captureArgs.useIdentityTransform =
- env->GetBooleanField(displayCaptureArgsObject,
- gDisplayCaptureArgsClassInfo.useIdentityTransform);
- return captureArgs;
-}
-
-static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
- jobject screenCaptureListenerObject) {
- const DisplayCaptureArgs captureArgs =
- displayCaptureArgsFromObject(env, displayCaptureArgsObject);
-
- if (captureArgs.displayToken == NULL) {
- return BAD_VALUE;
- }
-
- sp<IScreenCaptureListener> captureListener =
- new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject);
- return ScreenshotClient::captureDisplay(captureArgs, captureListener);
-}
-
-static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
- jobject screenCaptureListenerObject) {
- LayerCaptureArgs captureArgs;
- getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
- SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
- env->GetLongField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.layer));
- if (layer == nullptr) {
- return BAD_VALUE;
- }
-
- captureArgs.layerHandle = layer->getHandle();
- captureArgs.childrenOnly =
- env->GetBooleanField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.childrenOnly);
- jlongArray excludeObjectArray = reinterpret_cast<jlongArray>(
- env->GetObjectField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.excludeLayers));
- if (excludeObjectArray != NULL) {
- const jsize len = env->GetArrayLength(excludeObjectArray);
- captureArgs.excludeHandles.reserve(len);
-
- const jlong* objects = env->GetLongArrayElements(excludeObjectArray, nullptr);
- for (jsize i = 0; i < len; i++) {
- auto excludeObject = reinterpret_cast<SurfaceControl *>(objects[i]);
- if (excludeObject == nullptr) {
- jniThrowNullPointerException(env, "Exclude layer is null");
- return NULL;
- }
- captureArgs.excludeHandles.emplace(excludeObject->getHandle());
- }
- env->ReleaseLongArrayElements(excludeObjectArray, const_cast<jlong*>(objects), JNI_ABORT);
- }
-
- sp<IScreenCaptureListener> captureListener =
- new ScreenCaptureListenerWrapper(env, screenCaptureListenerObject);
- return ScreenshotClient::captureLayers(captureArgs, captureListener);
-}
-
static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
transaction->apply(sync);
@@ -664,12 +462,12 @@ static void nativeSetGeometry(JNIEnv* env, jclass clazz, jlong transactionObj, j
Rect source, dst;
if (sourceObj != NULL) {
- source = rectFromObj(env, sourceObj);
+ source = JNICommon::rectFromObj(env, sourceObj);
} else {
source.makeInvalid();
}
if (dstObj != NULL) {
- dst = rectFromObj(env, dstObj);
+ dst = JNICommon::rectFromObj(env, dstObj);
} else {
dst.makeInvalid();
}
@@ -720,10 +518,11 @@ static ReleaseBufferCallback genReleaseCallback(JNIEnv* env, jobject releaseCall
if (fenceCopy) {
fenceCopy->incStrong(0);
}
- globalCallbackRef->env()->CallStaticVoidMethod(gInvokeReleaseCallback.clazz,
- gInvokeReleaseCallback.invokeReleaseCallback,
- globalCallbackRef->object(),
- reinterpret_cast<jlong>(fenceCopy));
+ globalCallbackRef->env()
+ ->CallStaticVoidMethod(gSurfaceControlClassInfo.clazz,
+ gSurfaceControlClassInfo.invokeReleaseCallback,
+ globalCallbackRef->object(),
+ reinterpret_cast<jlong>(fenceCopy));
};
}
@@ -1183,20 +982,6 @@ static jobject nativeGetDisplayedContentSample(JNIEnv* env, jclass clazz, jobjec
histogramComponent3);
}
-static jobject nativeCreateDisplay(JNIEnv* env, jclass clazz, jstring nameObj,
- jboolean secure) {
- ScopedUtfChars name(env, nameObj);
- sp<IBinder> token(SurfaceComposerClient::createDisplay(
- String8(name.c_str()), bool(secure)));
- return javaObjectForIBinder(env, token);
-}
-
-static void nativeDestroyDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObj));
- if (token == NULL) return;
- SurfaceComposerClient::destroyDisplay(token);
-}
-
static void nativeSetDisplaySurface(JNIEnv* env, jclass clazz,
jlong transactionObj,
jobject tokenObj, jlong nativeSurfaceObject) {
@@ -1743,27 +1528,6 @@ static void nativeReparent(JNIEnv* env, jclass clazz, jlong transactionObj,
transaction->reparent(ctrl, newParent);
}
-static void nativeOverrideHdrTypes(JNIEnv* env, jclass clazz, jobject tokenObject,
- jintArray jHdrTypes) {
- sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
- if (token == nullptr || jHdrTypes == nullptr) return;
-
- int* hdrTypes = env->GetIntArrayElements(jHdrTypes, 0);
- int numHdrTypes = env->GetArrayLength(jHdrTypes);
-
- std::vector<ui::Hdr> hdrTypesVector;
- for (int i = 0; i < numHdrTypes; i++) {
- hdrTypesVector.push_back(static_cast<ui::Hdr>(hdrTypes[i]));
- }
- env->ReleaseIntArrayElements(jHdrTypes, hdrTypes, 0);
-
- status_t error = SurfaceComposerClient::overrideHdrTypes(token, hdrTypesVector);
- if (error != NO_ERROR) {
- jniThrowExceptionFmt(env, "java/lang/SecurityException",
- "ACCESS_SURFACE_FLINGER is missing");
- }
-}
-
static jboolean nativeGetBootDisplayModeSupport(JNIEnv* env, jclass clazz) {
bool isBootDisplayModeSupported = false;
SurfaceComposerClient::getBootDisplayModeSupport(&isBootDisplayModeSupported);
@@ -2129,6 +1893,28 @@ static jobject nativeGetDefaultApplyToken(JNIEnv* env, jclass clazz) {
// ----------------------------------------------------------------------------
+SurfaceControl* android_view_SurfaceControl_getNativeSurfaceControl(JNIEnv* env,
+ jobject surfaceControlObj) {
+ if (!!surfaceControlObj &&
+ env->IsInstanceOf(surfaceControlObj, gSurfaceControlClassInfo.clazz)) {
+ return reinterpret_cast<SurfaceControl*>(
+ env->GetLongField(surfaceControlObj, gSurfaceControlClassInfo.mNativeObject));
+ } else {
+ return nullptr;
+ }
+}
+
+SurfaceComposerClient::Transaction* android_view_SurfaceTransaction_getNativeSurfaceTransaction(
+ JNIEnv* env, jobject surfaceTransactionObj) {
+ if (!!surfaceTransactionObj &&
+ env->IsInstanceOf(surfaceTransactionObj, gTransactionClassInfo.clazz)) {
+ return reinterpret_cast<SurfaceComposerClient::Transaction*>(
+ env->GetLongField(surfaceTransactionObj, gTransactionClassInfo.mNativeObject));
+ } else {
+ return nullptr;
+ }
+}
+
static const JNINativeMethod sSurfaceControlMethods[] = {
// clang-format off
{"nativeCreate", "(Landroid/view/SurfaceSession;Ljava/lang/String;IIIIJLandroid/os/Parcel;)J",
@@ -2210,10 +1996,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeGetPhysicalDisplayIds },
{"nativeGetPhysicalDisplayToken", "(J)Landroid/os/IBinder;",
(void*)nativeGetPhysicalDisplayToken },
- {"nativeCreateDisplay", "(Ljava/lang/String;Z)Landroid/os/IBinder;",
- (void*)nativeCreateDisplay },
- {"nativeDestroyDisplay", "(Landroid/os/IBinder;)V",
- (void*)nativeDestroyDisplay },
{"nativeSetDisplaySurface", "(JLandroid/os/IBinder;J)V",
(void*)nativeSetDisplaySurface },
{"nativeSetDisplayLayerStack", "(JLandroid/os/IBinder;I)V",
@@ -2253,8 +2035,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeSetGameContentType },
{"nativeGetCompositionDataspaces", "()[I",
(void*)nativeGetCompositionDataspaces},
- {"nativeOverrideHdrTypes", "(Landroid/os/IBinder;[I)V",
- (void*)nativeOverrideHdrTypes },
{"nativeClearContentFrameStats", "(J)Z",
(void*)nativeClearContentFrameStats },
{"nativeGetContentFrameStats", "(JLandroid/view/WindowContentFrameStats;)Z",
@@ -2269,12 +2049,6 @@ static const JNINativeMethod sSurfaceControlMethods[] = {
(void*)nativeGetProtectedContentSupport },
{"nativeReparent", "(JJJ)V",
(void*)nativeReparent },
- {"nativeCaptureDisplay",
- "(Landroid/view/SurfaceControl$DisplayCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
- (void*)nativeCaptureDisplay },
- {"nativeCaptureLayers",
- "(Landroid/view/SurfaceControl$LayerCaptureArgs;Landroid/view/SurfaceControl$ScreenCaptureListener;)I",
- (void*)nativeCaptureLayers },
{"nativeSetInputWindowInfo", "(JJLandroid/view/InputWindowHandle;)V",
(void*)nativeSetInputWindowInfo },
{"nativeSetMetadata", "(JJILandroid/os/Parcel;)V",
@@ -2416,12 +2190,6 @@ int register_android_view_SurfaceControl(JNIEnv* env)
GetFieldIDOrDie(env, modeClazz, "presentationDeadlineNanos", "J");
gDisplayModeClassInfo.group = GetFieldIDOrDie(env, modeClazz, "group", "I");
- jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
- gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
- gRectClassInfo.left = GetFieldIDOrDie(env, rectClazz, "left", "I");
- gRectClassInfo.right = GetFieldIDOrDie(env, rectClazz, "right", "I");
- gRectClassInfo.top = GetFieldIDOrDie(env, rectClazz, "top", "I");
-
jclass frameStatsClazz = FindClassOrDie(env, "android/view/FrameStats");
jfieldID undefined_time_nano_field = GetStaticFieldIDOrDie(env,
frameStatsClazz, "UNDEFINED_TIME_NANO", "J");
@@ -2462,15 +2230,6 @@ int register_android_view_SurfaceControl(JNIEnv* env)
GetMethodIDOrDie(env, deviceProductInfoManufactureDateClazz, "<init>",
"(Ljava/lang/Integer;Ljava/lang/Integer;)V");
- jclass screenshotGraphicsBufferClazz =
- FindClassOrDie(env, "android/view/SurfaceControl$ScreenshotHardwareBuffer");
- gScreenshotHardwareBufferClassInfo.clazz =
- MakeGlobalRefOrDie(env, screenshotGraphicsBufferClazz);
- gScreenshotHardwareBufferClassInfo.builder =
- GetStaticMethodIDOrDie(env, screenshotGraphicsBufferClazz, "createFromNative",
- "(Landroid/hardware/HardwareBuffer;IZZ)Landroid/view/"
- "SurfaceControl$ScreenshotHardwareBuffer;");
-
jclass displayedContentSampleClazz = FindClassOrDie(env,
"android/hardware/display/DisplayedContentSample");
gDisplayedContentSampleClassInfo.clazz = MakeGlobalRefOrDie(env, displayedContentSampleClazz);
@@ -2523,46 +2282,6 @@ int register_android_view_SurfaceControl(JNIEnv* env)
gDesiredDisplayModeSpecsClassInfo.appRequestRefreshRateMax =
GetFieldIDOrDie(env, DesiredDisplayModeSpecsClazz, "appRequestRefreshRateMax", "F");
- jclass captureArgsClazz = FindClassOrDie(env, "android/view/SurfaceControl$CaptureArgs");
- gCaptureArgsClassInfo.pixelFormat = GetFieldIDOrDie(env, captureArgsClazz, "mPixelFormat", "I");
- gCaptureArgsClassInfo.sourceCrop =
- GetFieldIDOrDie(env, captureArgsClazz, "mSourceCrop", "Landroid/graphics/Rect;");
- gCaptureArgsClassInfo.frameScaleX = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScaleX", "F");
- gCaptureArgsClassInfo.frameScaleY = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScaleY", "F");
- gCaptureArgsClassInfo.captureSecureLayers =
- GetFieldIDOrDie(env, captureArgsClazz, "mCaptureSecureLayers", "Z");
- gCaptureArgsClassInfo.allowProtected =
- GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z");
- gCaptureArgsClassInfo.uid = GetFieldIDOrDie(env, captureArgsClazz, "mUid", "J");
- gCaptureArgsClassInfo.grayscale = GetFieldIDOrDie(env, captureArgsClazz, "mGrayscale", "Z");
-
- jclass displayCaptureArgsClazz =
- FindClassOrDie(env, "android/view/SurfaceControl$DisplayCaptureArgs");
- gDisplayCaptureArgsClassInfo.displayToken =
- GetFieldIDOrDie(env, displayCaptureArgsClazz, "mDisplayToken", "Landroid/os/IBinder;");
- gDisplayCaptureArgsClassInfo.width =
- GetFieldIDOrDie(env, displayCaptureArgsClazz, "mWidth", "I");
- gDisplayCaptureArgsClassInfo.height =
- GetFieldIDOrDie(env, displayCaptureArgsClazz, "mHeight", "I");
- gDisplayCaptureArgsClassInfo.useIdentityTransform =
- GetFieldIDOrDie(env, displayCaptureArgsClazz, "mUseIdentityTransform", "Z");
-
- jclass layerCaptureArgsClazz =
- FindClassOrDie(env, "android/view/SurfaceControl$LayerCaptureArgs");
- gLayerCaptureArgsClassInfo.layer =
- GetFieldIDOrDie(env, layerCaptureArgsClazz, "mNativeLayer", "J");
- gLayerCaptureArgsClassInfo.excludeLayers =
- GetFieldIDOrDie(env, layerCaptureArgsClazz, "mNativeExcludeLayers", "[J");
- gLayerCaptureArgsClassInfo.childrenOnly =
- GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
-
- jclass screenCaptureListenerClazz =
- FindClassOrDie(env, "android/view/SurfaceControl$ScreenCaptureListener");
- gScreenCaptureListenerClassInfo.clazz = MakeGlobalRefOrDie(env, screenCaptureListenerClazz);
- gScreenCaptureListenerClassInfo.onScreenCaptureComplete =
- GetMethodIDOrDie(env, screenCaptureListenerClazz, "onScreenCaptureComplete",
- "(Landroid/view/SurfaceControl$ScreenshotHardwareBuffer;)V");
-
jclass jankDataClazz =
FindClassOrDie(env, "android/view/SurfaceControl$JankData");
gJankDataClassInfo.clazz = MakeGlobalRefOrDie(env, jankDataClazz);
@@ -2594,11 +2313,18 @@ int register_android_view_SurfaceControl(JNIEnv* env)
GetFieldIDOrDie(env, displayDecorationSupportClazz, "alphaInterpretation", "I");
jclass surfaceControlClazz = FindClassOrDie(env, "android/view/SurfaceControl");
- gInvokeReleaseCallback.clazz = MakeGlobalRefOrDie(env, surfaceControlClazz);
- gInvokeReleaseCallback.invokeReleaseCallback =
+ gSurfaceControlClassInfo.clazz = MakeGlobalRefOrDie(env, surfaceControlClazz);
+ gSurfaceControlClassInfo.mNativeObject =
+ GetFieldIDOrDie(env, gSurfaceControlClassInfo.clazz, "mNativeObject", "J");
+ gSurfaceControlClassInfo.invokeReleaseCallback =
GetStaticMethodIDOrDie(env, surfaceControlClazz, "invokeReleaseCallback",
"(Ljava/util/function/Consumer;J)V");
+ jclass surfaceTransactionClazz = FindClassOrDie(env, "android/view/SurfaceControl$Transaction");
+ gTransactionClassInfo.clazz = MakeGlobalRefOrDie(env, surfaceTransactionClazz);
+ gTransactionClassInfo.mNativeObject =
+ GetFieldIDOrDie(env, gTransactionClassInfo.clazz, "mNativeObject", "J");
+
return err;
}
diff --git a/core/jni/android_window_ScreenCapture.cpp b/core/jni/android_window_ScreenCapture.cpp
new file mode 100644
index 000000000000..c1929c6535fb
--- /dev/null
+++ b/core/jni/android_window_ScreenCapture.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "ScreenCapture"
+// #define LOG_NDEBUG 0
+
+#include <android/gui/BnScreenCaptureListener.h>
+#include <android_runtime/android_hardware_HardwareBuffer.h>
+#include <gui/SurfaceComposerClient.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <ui/GraphicTypes.h>
+
+#include "android_os_Parcel.h"
+#include "android_util_Binder.h"
+#include "core_jni_helpers.h"
+#include "jni_common.h"
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+using gui::CaptureArgs;
+
+static struct {
+ jfieldID pixelFormat;
+ jfieldID sourceCrop;
+ jfieldID frameScaleX;
+ jfieldID frameScaleY;
+ jfieldID captureSecureLayers;
+ jfieldID allowProtected;
+ jfieldID uid;
+ jfieldID grayscale;
+} gCaptureArgsClassInfo;
+
+static struct {
+ jfieldID displayToken;
+ jfieldID width;
+ jfieldID height;
+ jfieldID useIdentityTransform;
+} gDisplayCaptureArgsClassInfo;
+
+static struct {
+ jfieldID layer;
+ jfieldID excludeLayers;
+ jfieldID childrenOnly;
+} gLayerCaptureArgsClassInfo;
+
+static struct {
+ jmethodID accept;
+} gConsumerClassInfo;
+
+static struct {
+ jclass clazz;
+ jmethodID builder;
+} gScreenshotHardwareBufferClassInfo;
+
+enum JNamedColorSpace : jint {
+ // ColorSpace.Named.SRGB.ordinal() = 0;
+ SRGB = 0,
+
+ // ColorSpace.Named.DISPLAY_P3.ordinal() = 7;
+ DISPLAY_P3 = 7,
+};
+
+constexpr jint fromDataspaceToNamedColorSpaceValue(const ui::Dataspace dataspace) {
+ switch (dataspace) {
+ case ui::Dataspace::DISPLAY_P3:
+ return JNamedColorSpace::DISPLAY_P3;
+ default:
+ return JNamedColorSpace::SRGB;
+ }
+}
+
+static void checkAndClearException(JNIEnv* env, const char* methodName) {
+ if (env->ExceptionCheck()) {
+ ALOGE("An exception was thrown by callback '%s'.", methodName);
+ env->ExceptionClear();
+ }
+}
+
+class ScreenCaptureListenerWrapper : public gui::BnScreenCaptureListener {
+public:
+ explicit ScreenCaptureListenerWrapper(JNIEnv* env, jobject jobject) {
+ env->GetJavaVM(&mVm);
+ mConsumerObject = env->NewGlobalRef(jobject);
+ LOG_ALWAYS_FATAL_IF(!mConsumerObject, "Failed to make global ref");
+ }
+
+ ~ScreenCaptureListenerWrapper() {
+ if (mConsumerObject) {
+ getenv()->DeleteGlobalRef(mConsumerObject);
+ mConsumerObject = nullptr;
+ }
+ }
+
+ binder::Status onScreenCaptureCompleted(
+ const gui::ScreenCaptureResults& captureResults) override {
+ JNIEnv* env = getenv();
+ if (!captureResults.fenceResult.ok() || captureResults.buffer == nullptr) {
+ env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, nullptr);
+ checkAndClearException(env, "accept");
+ return binder::Status::ok();
+ }
+ captureResults.fenceResult.value()->waitForever(LOG_TAG);
+ jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
+ env, captureResults.buffer->toAHardwareBuffer());
+ const jint namedColorSpace =
+ fromDataspaceToNamedColorSpaceValue(captureResults.capturedDataspace);
+ jobject screenshotHardwareBuffer =
+ env->CallStaticObjectMethod(gScreenshotHardwareBufferClassInfo.clazz,
+ gScreenshotHardwareBufferClassInfo.builder,
+ jhardwareBuffer, namedColorSpace,
+ captureResults.capturedSecureLayers,
+ captureResults.capturedHdrLayers);
+ checkAndClearException(env, "builder");
+ env->CallVoidMethod(mConsumerObject, gConsumerClassInfo.accept, screenshotHardwareBuffer);
+ checkAndClearException(env, "accept");
+ env->DeleteLocalRef(jhardwareBuffer);
+ env->DeleteLocalRef(screenshotHardwareBuffer);
+ return binder::Status::ok();
+ }
+
+private:
+ jobject mConsumerObject;
+ JavaVM* mVm;
+
+ JNIEnv* getenv() {
+ JNIEnv* env;
+ if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ if (mVm->AttachCurrentThreadAsDaemon(&env, nullptr) != JNI_OK) {
+ LOG_ALWAYS_FATAL("Failed to AttachCurrentThread!");
+ }
+ }
+ return env;
+ }
+};
+
+static void getCaptureArgs(JNIEnv* env, jobject captureArgsObject, CaptureArgs& captureArgs) {
+ captureArgs.pixelFormat = static_cast<ui::PixelFormat>(
+ env->GetIntField(captureArgsObject, gCaptureArgsClassInfo.pixelFormat));
+ captureArgs.sourceCrop =
+ JNICommon::rectFromObj(env,
+ env->GetObjectField(captureArgsObject,
+ gCaptureArgsClassInfo.sourceCrop));
+ captureArgs.frameScaleX =
+ env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleX);
+ captureArgs.frameScaleY =
+ env->GetFloatField(captureArgsObject, gCaptureArgsClassInfo.frameScaleY);
+ captureArgs.captureSecureLayers =
+ env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.captureSecureLayers);
+ captureArgs.allowProtected =
+ env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.allowProtected);
+ captureArgs.uid = env->GetLongField(captureArgsObject, gCaptureArgsClassInfo.uid);
+ captureArgs.grayscale =
+ env->GetBooleanField(captureArgsObject, gCaptureArgsClassInfo.grayscale);
+}
+
+static DisplayCaptureArgs displayCaptureArgsFromObject(JNIEnv* env,
+ jobject displayCaptureArgsObject) {
+ DisplayCaptureArgs captureArgs;
+ getCaptureArgs(env, displayCaptureArgsObject, captureArgs);
+
+ captureArgs.displayToken =
+ ibinderForJavaObject(env,
+ env->GetObjectField(displayCaptureArgsObject,
+ gDisplayCaptureArgsClassInfo.displayToken));
+ captureArgs.width =
+ env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.width);
+ captureArgs.height =
+ env->GetIntField(displayCaptureArgsObject, gDisplayCaptureArgsClassInfo.height);
+ captureArgs.useIdentityTransform =
+ env->GetBooleanField(displayCaptureArgsObject,
+ gDisplayCaptureArgsClassInfo.useIdentityTransform);
+ return captureArgs;
+}
+
+static jint nativeCaptureDisplay(JNIEnv* env, jclass clazz, jobject displayCaptureArgsObject,
+ jlong screenCaptureListenerObject) {
+ const DisplayCaptureArgs captureArgs =
+ displayCaptureArgsFromObject(env, displayCaptureArgsObject);
+
+ if (captureArgs.displayToken == nullptr) {
+ return BAD_VALUE;
+ }
+
+ sp<gui::IScreenCaptureListener> captureListener =
+ reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
+ return ScreenshotClient::captureDisplay(captureArgs, captureListener);
+}
+
+static jint nativeCaptureLayers(JNIEnv* env, jclass clazz, jobject layerCaptureArgsObject,
+ jlong screenCaptureListenerObject) {
+ LayerCaptureArgs captureArgs;
+ getCaptureArgs(env, layerCaptureArgsObject, captureArgs);
+ SurfaceControl* layer = reinterpret_cast<SurfaceControl*>(
+ env->GetLongField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.layer));
+ if (layer == nullptr) {
+ return BAD_VALUE;
+ }
+
+ captureArgs.layerHandle = layer->getHandle();
+ captureArgs.childrenOnly =
+ env->GetBooleanField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.childrenOnly);
+
+ jlongArray excludeObjectArray = reinterpret_cast<jlongArray>(
+ env->GetObjectField(layerCaptureArgsObject, gLayerCaptureArgsClassInfo.excludeLayers));
+ if (excludeObjectArray != nullptr) {
+ ScopedLongArrayRO excludeArray(env, excludeObjectArray);
+ const jsize len = excludeArray.size();
+ captureArgs.excludeHandles.reserve(len);
+
+ for (jsize i = 0; i < len; i++) {
+ auto excludeObject = reinterpret_cast<SurfaceControl*>(excludeArray[i]);
+ if (excludeObject == nullptr) {
+ jniThrowNullPointerException(env, "Exclude layer is null");
+ return BAD_VALUE;
+ }
+ captureArgs.excludeHandles.emplace(excludeObject->getHandle());
+ }
+ }
+
+ sp<gui::IScreenCaptureListener> captureListener =
+ reinterpret_cast<gui::IScreenCaptureListener*>(screenCaptureListenerObject);
+ return ScreenshotClient::captureLayers(captureArgs, captureListener);
+}
+
+static jlong nativeCreateScreenCaptureListener(JNIEnv* env, jclass clazz, jobject consumerObj) {
+ sp<gui::IScreenCaptureListener> listener =
+ sp<ScreenCaptureListenerWrapper>::make(env, consumerObj);
+ listener->incStrong((void*)nativeCreateScreenCaptureListener);
+ return reinterpret_cast<jlong>(listener.get());
+}
+
+static void nativeWriteListenerToParcel(JNIEnv* env, jclass clazz, jlong nativeObject,
+ jobject parcelObj) {
+ Parcel* parcel = parcelForJavaObject(env, parcelObj);
+ if (parcel == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return;
+ }
+ ScreenCaptureListenerWrapper* const self =
+ reinterpret_cast<ScreenCaptureListenerWrapper*>(nativeObject);
+ if (self != nullptr) {
+ parcel->writeStrongBinder(IInterface::asBinder(self));
+ }
+}
+
+static jlong nativeReadListenerFromParcel(JNIEnv* env, jclass clazz, jobject parcelObj) {
+ Parcel* parcel = parcelForJavaObject(env, parcelObj);
+ if (parcel == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return 0;
+ }
+ sp<gui::IScreenCaptureListener> listener =
+ interface_cast<gui::IScreenCaptureListener>(parcel->readStrongBinder());
+ if (listener == nullptr) {
+ return 0;
+ }
+ listener->incStrong((void*)nativeCreateScreenCaptureListener);
+ return reinterpret_cast<jlong>(listener.get());
+}
+
+void destroyNativeListener(void* ptr) {
+ ScreenCaptureListenerWrapper* listener = reinterpret_cast<ScreenCaptureListenerWrapper*>(ptr);
+ listener->decStrong((void*)nativeCreateScreenCaptureListener);
+}
+
+static jlong getNativeListenerFinalizer(JNIEnv* env, jclass clazz) {
+ return static_cast<jlong>(reinterpret_cast<uintptr_t>(&destroyNativeListener));
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod sScreenCaptureMethods[] = {
+ // clang-format off
+ {"nativeCaptureDisplay", "(Landroid/window/ScreenCapture$DisplayCaptureArgs;J)I",
+ (void*)nativeCaptureDisplay },
+ {"nativeCaptureLayers", "(Landroid/window/ScreenCapture$LayerCaptureArgs;J)I",
+ (void*)nativeCaptureLayers },
+ {"nativeCreateScreenCaptureListener", "(Ljava/util/function/Consumer;)J",
+ (void*)nativeCreateScreenCaptureListener },
+ {"nativeWriteListenerToParcel", "(JLandroid/os/Parcel;)V", (void*)nativeWriteListenerToParcel },
+ {"nativeReadListenerFromParcel", "(Landroid/os/Parcel;)J",
+ (void*)nativeReadListenerFromParcel },
+ {"getNativeListenerFinalizer", "()J", (void*)getNativeListenerFinalizer },
+ // clang-format on
+};
+
+int register_android_window_ScreenCapture(JNIEnv* env) {
+ int err = RegisterMethodsOrDie(env, "android/window/ScreenCapture", sScreenCaptureMethods,
+ NELEM(sScreenCaptureMethods));
+
+ jclass captureArgsClazz = FindClassOrDie(env, "android/window/ScreenCapture$CaptureArgs");
+ gCaptureArgsClassInfo.pixelFormat = GetFieldIDOrDie(env, captureArgsClazz, "mPixelFormat", "I");
+ gCaptureArgsClassInfo.sourceCrop =
+ GetFieldIDOrDie(env, captureArgsClazz, "mSourceCrop", "Landroid/graphics/Rect;");
+ gCaptureArgsClassInfo.frameScaleX = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScaleX", "F");
+ gCaptureArgsClassInfo.frameScaleY = GetFieldIDOrDie(env, captureArgsClazz, "mFrameScaleY", "F");
+ gCaptureArgsClassInfo.captureSecureLayers =
+ GetFieldIDOrDie(env, captureArgsClazz, "mCaptureSecureLayers", "Z");
+ gCaptureArgsClassInfo.allowProtected =
+ GetFieldIDOrDie(env, captureArgsClazz, "mAllowProtected", "Z");
+ gCaptureArgsClassInfo.uid = GetFieldIDOrDie(env, captureArgsClazz, "mUid", "J");
+ gCaptureArgsClassInfo.grayscale = GetFieldIDOrDie(env, captureArgsClazz, "mGrayscale", "Z");
+
+ jclass displayCaptureArgsClazz =
+ FindClassOrDie(env, "android/window/ScreenCapture$DisplayCaptureArgs");
+ gDisplayCaptureArgsClassInfo.displayToken =
+ GetFieldIDOrDie(env, displayCaptureArgsClazz, "mDisplayToken", "Landroid/os/IBinder;");
+ gDisplayCaptureArgsClassInfo.width =
+ GetFieldIDOrDie(env, displayCaptureArgsClazz, "mWidth", "I");
+ gDisplayCaptureArgsClassInfo.height =
+ GetFieldIDOrDie(env, displayCaptureArgsClazz, "mHeight", "I");
+ gDisplayCaptureArgsClassInfo.useIdentityTransform =
+ GetFieldIDOrDie(env, displayCaptureArgsClazz, "mUseIdentityTransform", "Z");
+
+ jclass layerCaptureArgsClazz =
+ FindClassOrDie(env, "android/window/ScreenCapture$LayerCaptureArgs");
+ gLayerCaptureArgsClassInfo.layer =
+ GetFieldIDOrDie(env, layerCaptureArgsClazz, "mNativeLayer", "J");
+ gLayerCaptureArgsClassInfo.excludeLayers =
+ GetFieldIDOrDie(env, layerCaptureArgsClazz, "mNativeExcludeLayers", "[J");
+ gLayerCaptureArgsClassInfo.childrenOnly =
+ GetFieldIDOrDie(env, layerCaptureArgsClazz, "mChildrenOnly", "Z");
+
+ jclass consumer = FindClassOrDie(env, "java/util/function/Consumer");
+ gConsumerClassInfo.accept = GetMethodIDOrDie(env, consumer, "accept", "(Ljava/lang/Object;)V");
+
+ jclass screenshotGraphicsBufferClazz =
+ FindClassOrDie(env, "android/window/ScreenCapture$ScreenshotHardwareBuffer");
+ gScreenshotHardwareBufferClassInfo.clazz =
+ MakeGlobalRefOrDie(env, screenshotGraphicsBufferClazz);
+ gScreenshotHardwareBufferClassInfo.builder =
+ GetStaticMethodIDOrDie(env, screenshotGraphicsBufferClazz, "createFromNative",
+ "(Landroid/hardware/HardwareBuffer;IZZ)Landroid/window/"
+ "ScreenCapture$ScreenshotHardwareBuffer;");
+
+ return err;
+}
+
+} // namespace android
diff --git a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
index 5b946d5c8d3a..a95b6e37f5de 100644
--- a/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
+++ b/core/jni/com_android_internal_os_LongArrayMultiStateCounter.cpp
@@ -244,6 +244,38 @@ static void native_getValues_LongArrayContainer(JNIEnv *env, jobject self, jlong
std::copy(vector->data(), vector->data() + vector->size(), scopedArray.get());
}
+static jboolean native_combineValues_LongArrayContainer(JNIEnv *env, jobject self, jlong nativePtr,
+ jlongArray jarray, jintArray jindexMap) {
+ std::vector<uint64_t> *vector = reinterpret_cast<std::vector<uint64_t> *>(nativePtr);
+ ScopedLongArrayRW scopedArray(env, jarray);
+ ScopedIntArrayRO scopedIndexMap(env, jindexMap);
+
+ const uint64_t *data = vector->data();
+ uint64_t *array = reinterpret_cast<uint64_t *>(scopedArray.get());
+ const uint8_t size = scopedArray.size();
+
+ for (int i = 0; i < size; i++) {
+ array[i] = 0;
+ }
+
+ bool nonZero = false;
+ for (int i = 0; i < vector->size(); i++) {
+ jint index = scopedIndexMap[i];
+ if (index < 0 || index >= size) {
+ jniThrowExceptionFmt(env, "java/lang/IndexOutOfBoundsException",
+ "Index %d is out of bounds: [0, %d]", index, size - 1);
+ return false;
+ }
+
+ if (data[i] != 0L) {
+ array[index] += data[i];
+ nonZero = true;
+ }
+ }
+
+ return nonZero;
+}
+
static const JNINativeMethod g_LongArrayContainer_methods[] = {
// @CriticalNative
{"native_init", "(I)J", (void *)native_init_LongArrayContainer},
@@ -253,6 +285,8 @@ static const JNINativeMethod g_LongArrayContainer_methods[] = {
{"native_setValues", "(J[J)V", (void *)native_setValues_LongArrayContainer},
// @FastNative
{"native_getValues", "(J[J)V", (void *)native_getValues_LongArrayContainer},
+ // @FastNative
+ {"native_combineValues", "(J[J[I)Z", (void *)native_combineValues_LongArrayContainer},
};
int register_com_android_internal_os_LongArrayMultiStateCounter(JNIEnv *env) {
diff --git a/core/jni/include/android_runtime/android_view_SurfaceControl.h b/core/jni/include/android_runtime/android_view_SurfaceControl.h
new file mode 100644
index 000000000000..10a754903208
--- /dev/null
+++ b/core/jni/include/android_runtime/android_view_SurfaceControl.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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_VIEW_SURFACECONTROL_H
+#define _ANDROID_VIEW_SURFACECONTROL_H
+
+#include <gui/SurfaceComposerClient.h>
+#include <gui/SurfaceControl.h>
+
+#include "jni.h"
+
+namespace android {
+
+/* Gets the underlying native SurfaceControl for a java SurfaceControl. */
+extern SurfaceControl* android_view_SurfaceControl_getNativeSurfaceControl(
+ JNIEnv* env, jobject surfaceControlObj);
+
+/* Gets the underlying native SurfaceControl for a java SurfaceControl. */
+extern SurfaceComposerClient::Transaction*
+android_view_SurfaceTransaction_getNativeSurfaceTransaction(JNIEnv* env,
+ jobject surfaceTransactionObj);
+
+} // namespace android
+
+#endif // _ANDROID_VIEW_SURFACECONTROL_H
diff --git a/core/jni/jni_common.cpp b/core/jni/jni_common.cpp
new file mode 100644
index 000000000000..8d376cf2c7f9
--- /dev/null
+++ b/core/jni/jni_common.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "jni_common.h"
+
+#include <jni.h>
+#include <ui/GraphicTypes.h>
+#include <ui/Rect.h>
+
+#include "core_jni_helpers.h"
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+static struct {
+ jfieldID bottom;
+ jfieldID left;
+ jfieldID right;
+ jfieldID top;
+} gRectClassInfo;
+
+Rect JNICommon::rectFromObj(JNIEnv* env, jobject rectObj) {
+ int left = env->GetIntField(rectObj, gRectClassInfo.left);
+ int top = env->GetIntField(rectObj, gRectClassInfo.top);
+ int right = env->GetIntField(rectObj, gRectClassInfo.right);
+ int bottom = env->GetIntField(rectObj, gRectClassInfo.bottom);
+ return Rect(left, top, right, bottom);
+}
+
+int register_jni_common(JNIEnv* env) {
+ jclass rectClazz = FindClassOrDie(env, "android/graphics/Rect");
+ gRectClassInfo.bottom = GetFieldIDOrDie(env, rectClazz, "bottom", "I");
+ gRectClassInfo.left = GetFieldIDOrDie(env, rectClazz, "left", "I");
+ gRectClassInfo.right = GetFieldIDOrDie(env, rectClazz, "right", "I");
+ gRectClassInfo.top = GetFieldIDOrDie(env, rectClazz, "top", "I");
+ return 0;
+}
+
+} // namespace android
diff --git a/core/java/android/app/cloudsearch/SearchResponse.aidl b/core/jni/jni_common.h
index 2064d112c6b9..a2bf6fb3f5e0 100644
--- a/core/java/android/app/cloudsearch/SearchResponse.aidl
+++ b/core/jni/jni_common.h
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2022, The Android Open Source Project
+/*
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,7 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+#include <jni.h>
-package android.app.cloudsearch;
+namespace android {
-parcelable SearchResponse; \ No newline at end of file
+class Rect;
+
+class JNICommon {
+public:
+ static Rect rectFromObj(JNIEnv* env, jobject rectObj);
+};
+} // namespace android \ No newline at end of file
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 4bbfee2f93cd..59e01bfa0c4b 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -61,7 +61,6 @@ import "frameworks/base/core/proto/android/util/textdump.proto";
import "frameworks/base/core/proto/android/privacy.proto";
import "frameworks/base/core/proto/android/section.proto";
import "frameworks/base/proto/src/ipconnectivity.proto";
-import "packages/modules/Permission/service/proto/role_service.proto";
package android.os;
@@ -358,10 +357,7 @@ message IncidentProto {
(section).userdebug_and_eng_only = true
];
- optional com.android.role.RoleServiceDumpProto role = 3024 [
- (section).type = SECTION_DUMPSYS,
- (section).args = "role --proto"
- ];
+ reserved 3024;
optional android.service.restricted_image.RestrictedImagesDumpProto restricted_images = 3025 [
(section).type = SECTION_DUMPSYS,
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 93ce7832824b..7e17840445ab 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -130,6 +130,10 @@ android_app {
// Allow overlay to add resource
"--auto-add-overlay",
+
+ // Framework resources benefit tremendously from enabling sparse encoding, saving tens
+ // of MBs in size and RAM use.
+ "--enable-sparse-encoding",
],
resource_zips: [
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4d41c3091786..78c07107bb2a 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6810,8 +6810,9 @@
android:exported="false">
</activity>
- <activity android:name="com.android.server.logcat.LogAccessDialogActivity"
+ <activity android:name="com.android.internal.app.LogAccessDialogActivity"
android:theme="@style/Theme.Translucent.NoTitleBar"
+ android:process=":ui"
android:excludeFromRecents="true"
android:exported="false">
</activity>
diff --git a/core/res/OWNERS b/core/res/OWNERS
index d8fc2181cfe8..22f40a1461c0 100644
--- a/core/res/OWNERS
+++ b/core/res/OWNERS
@@ -36,6 +36,9 @@ per-file res/xml/config_user_types.xml = file:/MULTIUSER_OWNERS
# Car
per-file res/values/dimens_car.xml = file:/platform/packages/services/Car:/OWNERS
+# Device Idle
+per-file res/values/config_device_idle.xml = file:/apex/jobscheduler/OWNERS
+
# Wear
per-file res/*-watch/* = file:/WEAR_OWNERS
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 9b997bf4c4fd..0b9386c37b51 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Gebruik skermslot"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Voer jou skermslot in om voort te gaan"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Druk ferm op die sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Kon nie vingerafdruk verwerk nie. Probeer asseblief weer."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Kan nie vingerafdruk herken nie. Probeer weer."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Maak vingerafdruksensor skoon en probeer weer"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Maak sensor skoon en probeer weer"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Druk ferm op sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Vinger is te stadig beweer. Probeer asseblief weer."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Probeer \'n ander vingerafdruk"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Te helder"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Druk van aan/af-skakelaar bespeur"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Probeer om dit te verstel"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Verander elke keer die posisie van jou vinger so effens"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesig is gestaaf; druk asseblief bevestig"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Vingerafdrukhardeware is nie beskikbaar nie."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan nie vingerafdruk opstel nie"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Vingerafdrukuittelling is bereik. Probeer weer."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vingerafdrukopstelling het uitgetel. Probeer weer."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Vingerafdrukhandeling is gekanselleer."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vingerafdrukhandeling is deur gebruiker gekanselleer."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Te veel pogings. Probeer later weer."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Te veel pogings. Gebruik eerder skermslot."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Te veel pogings. Gebruik eerder skermslot."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Probeer weer."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan nie vingerafdruk verwerk nie. Probeer weer."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukke is geregistreer nie."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Hierdie toetstel het nie \'n vingerafdruksensor nie."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor is tydelik gedeaktiveer."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Besoek \'n verskaffer wat herstelwerk doen."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Kan nie jou gesigmodel skep nie. Probeer weer."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Te helder. Probeer sagter beligting."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nie genoeg lig nie"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Beweeg foon verder weg"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Beweeg foon nader"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Beweeg foon hoër op"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Begin programme."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Voltooi herlaai."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Jy het die aan/af-skakelaar gedruk – dit skakel gewoonlik die skerm af.\n\nProbeer liggies tik terwyl jy jou vingerafdruk opstel."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tik om skerm af te skakel"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Skakel skerm af"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Skakel skerm af om opstelling te stop"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Skakel af"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Gaan voort met vingerafdrukverifiëring?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Jy het die aan/af-skakelaar gedruk – dit skakel gewoonlik die skerm af.\n\nProbeer liggies tik om jou vingerafdruk te verifieer."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Skakel skerm af"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Foon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dokluidsprekers"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Eksterne toestel"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Oorfone"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Stelsel"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 698bce43aad8..1e8d6813d89d 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"የማያ ገጽ መቆለፊን ይጠቀሙ"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ለመቀጠል የማያ ገጽ ቁልፍዎን ያስገቡ"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"ዳሳሹን በደንብ ይጫኑት"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ጣት አሻራን መስራት አልተቻለም። እባክዎ እንደገና ይሞክሩ።"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"የጣት አሻራን መለየት አልተቻለም። እንደገና ይሞክሩ።"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"የጣት አሻራ ዳሳሽን ያጽዱ እና እንደገና ይሞክሩ"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"ዳሳሹን ያጽዱ እና እንደገና ይሞክሩ"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"ዳሳሹን ጠበቅ አድርገው ይጫኑት"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"ጣት ከልክ በላይ ተንቀራፎ ተንቀሳቅሷል። እባክዎ እንደገና ይሞክሩ።"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ሌላ የጣት አሻራ ይሞክሩ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"በጣም ብርሃናማ"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"በኃይል መጫን እንዳለ ታውቋል"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ለማስተካከል ይሞክሩ"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"በእያንዳንዱ ጊዜ የጣትዎን ቦታ በትንሹ ይለዋውጡ"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ፊት ተረጋግጧል፣ እባክዎ አረጋግጥን ይጫኑ"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"የጣት አሻራ ሃርድዌር አይገኝም።"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"የጣት አሻራን ማዋቀር አልተቻለም"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"የጣት አሻራ ማብቂያ ጊዜ ደርሷል። እንደገና ይሞክሩ።"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"የጣት አሻራ ውቅረት ጊዜው አብቅቷል። እንደገና ይሞክሩ።"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"የጣት አሻራ ስርዓተ ክወና ተትቷል።"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"የጣት አሻራ ክወና በተጠቃሚ ተሰርዟል።"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"ከልክ በላይ ብዙ ሙከራዎች። በኋላ ላይ እንደገና ይሞክሩ።"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"በጣም ብዙ ሙከራዎች። በምትኩ የማያ ገጽ መቆለፊያን ይጠቀሙ።"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"በጣም ብዙ ሙከራዎች። በምትኩ የማያ ገጽ መቆለፊያን ይጠቀሙ።"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"እንደገና ይሞክሩ።"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"የጣት አሻራን ማሰናዳት አልተቻለም። እንደገና ይሞክሩ።"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ምንም የጣት አሻራዎች አልተመዘገቡም።"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ይህ መሣሪያ የጣት አሻራ ዳሳሽ የለውም።"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ዳሳሽ ለጊዜው ተሰናክሏል።"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"የጥገና አገልግሎት ሰጪን ይጎብኙ።"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"የመልክዎን ሞዴል መፍጠር አልተቻለም። እንደገና ይሞክሩ።"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ከልክ በላይ ፈካ ያለ። ይበልጥ ረጋ ያለ ብርሃን አጠቃቀምን ይሞክሩ።"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"በቂ ብርሃን የለም"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ስልኩን ያርቁት"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ስልኩን ያቅርቡት"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ስልኩን ከፍ ወዳለ ቦታ ይውሰዱት"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"መተግበሪያዎችን በማስጀመር ላይ፡፡"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"አጨራረስ ማስነሻ፡፡"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"የማብሪያ/ማጥፊያ ቁልፉን ተጭነዋል — ይህ ብዙውን ጊዜ ማያ ገጹን ያጠፋል።\n\nየጣት አሻራዎን በሚያዋቅሩበት ጊዜ በትንሹ መታ ለማድረግ ይሞክሩ።"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"ማያ ገጽን ለማጥፋት መታ ያድርጉ"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"ማያ ገጽን አጥፋ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"ውቅረትን ለመጨረስ ማያ ገጽን ያጥፉ"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"አጥፋ"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"የጣት አሻራዎን ማረጋገጥ ይቀጥሉ?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"የማብሪያ/ማጥፊያ ቁልፉን ተጭነዋል - ይህ ብዙውን ጊዜ ማያ ገጹን ያጠፋል። \n\n የጣት አሻራዎን ለማረጋገጥ በትንሹ መታ ለማድረግ ይሞክሩ።"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ማያ ገጽን አጥፋ"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"ቴሌቪዥን"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ስልክ"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"የትከል ድምፅ ማጉያዎች"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"ኤችዲኤምአይ"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"የውጪ መሣሪያ"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"የጆሮ ማዳመጫዎች"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"ዩ ኤስ ቢ"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"ስርዓት"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 82fc943f7784..1529b0db05cb 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -587,13 +587,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"استخدام قفل الشاشة"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"أدخِل قفل الشاشة للمتابعة"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"اضغط بقوة على المستشعر"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"تعذرت معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"يتعذّر التعرّف على بصمة الإصبع. يُرجى إعادة المحاولة."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"يُرجى تنظيف مستشعر بصمات الإصبع ثم إعادة المحاولة."</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"تنظيف المستشعر ثم إعادة المحاولة"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"اضغط بقوة على المستشعر"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"تم تحريك الإصبع ببطء شديد. يُرجى إعادة المحاولة."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"يمكنك تجربة بصمة إصبع أخرى."</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"الصورة ساطعة للغاية."</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"تم رصد الضغط على زر التشغيل."</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"حاوِل تعديل بصمة الإصبع."</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"غيِّر موضع إصبعك قليلاً في كل مرة."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -605,12 +606,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"تمّت مصادقة الوجه، يُرجى الضغط على \"تأكيد\"."</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"جهاز بصمة الإصبع غير متاح."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"يتعذّر إعداد بصمة الإصبع."</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"تم بلوغ مهلة إدخال بصمة الإصبع. أعد المحاولة."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"انتهت مهلة إعداد بصمة الإصبع. يُرجى إعادة المحاولة."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"تم إلغاء تشغيل بصمة الإصبع."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"تم إلغاء تشغيل بصمة الإصبع بواسطة المستخدم."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"تم إجراء عدد كبير من المحاولات. أعد المحاولة لاحقًا."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تم إجراء عدد كبير جدًا من المحاولات. عليك استخدام قفل الشاشة بدلاً من ذلك."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"أعد المحاولة."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"تتعذّر معالجة بصمة الإصبع. يُرجى إعادة المحاولة."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ليست هناك بصمات إصبع مسجَّلة."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"لا يحتوي هذا الجهاز على مستشعِر بصمات إصبع."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"تم إيقاف جهاز الاستشعار مؤقتًا."</string>
@@ -638,8 +639,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"يُرجى التواصل مع مقدِّم خدمات إصلاح."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"يتعذّر إنشاء نموذج الوجه. يُرجى إعادة المحاولة."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ساطع للغاية. تجربة مستوى سطوع أقلّ."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"الإضاءة غير كافية."</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"يُرجى إبعاد الهاتف عنك."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"يُرجى تقريب الهاتف منك."</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"يُرجى رفع الهاتف للأعلى."</string>
@@ -1253,8 +1253,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"بدء التطبيقات."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"جارٍ إعادة التشغيل."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ضغطت على زر التشغيل، يؤدي هذا عادةً إلى إيقاف الشاشة.\n\nجرِّب النقر بخفة أثناء إعداد بصمتك."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"النقر لإيقاف الشاشة"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"إيقاف الشاشة"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"لإنهاء عملية الإعداد، أوقِف الشاشة."</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"إيقاف"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"هل تريد مواصلة تأكيد بصمة إصبعك؟"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"ضغطت على زر التشغيل، يؤدي هذا عادةً إلى إيقاف الشاشة.\n\nجرِّب النقر بخفة لتأكيد بصمة إصبعك."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"إيقاف الشاشة"</string>
@@ -1615,7 +1615,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"التلفزيون"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"الهاتف"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"مكبرات صوت للإرساء"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"جهاز خارجي"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"سماعات رأس"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"النظام"</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 93957e59f81d..a854ea631041 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"স্ক্ৰীন ল\'ক ব্যৱহাৰ কৰক"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"অব্যাহত ৰাখিবলৈ আপোনাৰ স্ক্ৰীন লক দিয়ক"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"ছেন্সৰটোত ভালকৈ টিপক"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ফিগাৰপ্ৰিণ্টৰ প্ৰক্ৰিয়া সম্পাদন কৰিবপৰা নগ\'ল। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"ফিংগাৰপ্ৰিণ্ট চিনাক্ত কৰিব পৰা নাই। পুনৰ চেষ্টা কৰক।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো মচি পুনৰ চেষ্টা কৰক"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"ছেন্সৰটো মচি পুনৰ চেষ্টা কৰক"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"ছেন্সৰটোত ভালকৈ টিপক"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"আঙুলিৰ গতি অতি মন্থৰ আছিল। অনুগ্ৰহ কৰি আকৌ চেষ্টা কৰক৷"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"অন্য এটা ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰি চাওক"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"অতি উজ্জ্বল"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"পাৱাৰ বুটাম টিপাটো চিনাক্ত কৰা হৈছে"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"মিলাই চাওক"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"প্ৰতিবাৰতে আপোনাৰ আঙুলিটোৰ স্থান সামান্য সলনি কৰক"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"মুখমণ্ডলৰ বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰা হ’ল, অনুগ্ৰহ কৰি ‘নিশ্চিত কৰক’ বুটামটো টিপক"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ফিংগাৰপ্ৰিণ্ট হাৰ্ডৱেৰ নাই।"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ফিংগাৰপ্ৰিণ্ট ছেট আপ কৰিব নোৱাৰি"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"ফিংগাৰপ্ৰিণ্ট গ্ৰহণৰ সময়সীমা উকলি গৈছে। আকৌ চেষ্টা কৰক।"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ফিংগাৰপ্ৰিণ্ট ছেটআপ কৰাৰ সময় উকলি গৈছে। পুনৰ চেষ্টা কৰক।"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ফিংগাৰপ্ৰিণ্ট কাৰ্য বাতিল কৰা হ’ল।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ব্যৱহাৰকাৰীয়ে ফিংগাৰপ্ৰিণ্ট ক্ৰিয়া বাতিল কৰিছে।"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"অত্যধিক ভুল প্ৰয়াস। কিছুসময়ৰ পাছত আকৌ চেষ্টা কৰক।"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"অতি বেছিসংখ্যক প্ৰয়াস। ইয়াৰ সলনি স্ক্ৰীন লক ব্যৱহাৰ কৰক।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"অতি বেছিসংখ্যক প্ৰয়াস। ইয়াৰ সলনি স্ক্ৰীন লক ব্যৱহাৰ কৰক।"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"আকৌ চেষ্টা কৰক।"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ফিংগাৰপ্ৰিণ্ট চিনাক্তকৰণ প্ৰক্ৰিয়া কৰিব পৰা নাই। পুনৰ চেষ্টা কৰক।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনো ফিংগাৰপ্ৰিণ্ট যোগ কৰা নহ\'ল।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইচটোত ফিংগাৰপ্ৰিণ্ট ছেন্সৰ নাই।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ছেন্সৰটো সাময়িকভাৱে অক্ষম হৈ আছে।"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"মেৰামতি সেৱা প্ৰদানকাৰী কোনো প্ৰতিষ্ঠানলৈ যাওক।"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"মুখাৱয়বৰ মডেল সৃষ্টি কৰিব নোৱাৰি। পুনৰ চেষ্টা কৰক।"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"অতি উজ্জ্বল। ইয়াতকৈ কম পোহৰৰ উৎস ব্যৱহাৰ কৰক।"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"পৰ্যাপ্ত পোহৰ নাই"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ফ’নটো আৰু আঁতৰলৈ নিয়ক"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ফ’নটো ওচৰলৈ আনক"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ফ’নটো ওপৰলৈ নিয়ক"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"আৰম্ভ হৈ থকা এপসমূহ।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"বুট কাৰ্য সমাপ্ত কৰিছে।"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"আপুনি পাৱাৰ বুটামটো টিপিছে — এইটোৱে সাধাৰণতে স্ক্ৰীনখন অফ কৰে।\n\nআপোনাৰ ফিংগাৰপ্ৰিণ্টটো ছেট আপ কৰাৰ সময়ত লাহেকৈ টিপি চাওক।"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"স্ক্ৰীন অফ কৰিবলৈ টিপক"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"স্ক্ৰীন অফ কৰক"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"ছেটআপ সমাপ্ত কৰিবলৈ স্ক্ৰীন অফ কৰক"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"অফ কৰক"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"ফিংগাৰপ্ৰিণ্ট সত্যাপন কৰা জাৰি ৰাখিবনে?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"আপুনি পাৱাৰ বুটামটো টিপিছে — এইটোৱে সাধাৰণতে স্ক্ৰীনখন অফ কৰে।\n\nআপোনাৰ ফিংগাৰপ্ৰিণ্টটো সত্যাপন কৰিবলৈ লাহেকৈ টিপি চাওক।"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"স্ক্ৰীন অফ কৰক"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"টিভি"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ফ\'ন"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ড\'ক স্পীকাৰসমূহ"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"বাহ্যিক ডিভাইচ"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"হেডফ\'নবোৰ"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"ইউএছবি"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"ছিষ্টেম"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index c4f3d75c0e19..14495006cf4e 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ekran kilidindən istifadə edin"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Davam etmək üçün ekran kilidinizi daxil edin"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Sensora basıb saxlayın"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Barmaq izi tanınmadı. Lütfən, yenidən cəhd edin."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Barmaq izini tanımaq olmur. Yenidən cəhd edin."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Barmaq izi sensorunu silib yenidən cəhd edin"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Sensoru silib yenidən cəhd edin"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Sensora basıb saxlayın"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Barmağınızı çox yavaş hərəkət etdirdiniz. Lütfən, yenidən cəhd edin."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Başqa bir barmaq izini sınayın"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Çox işıqlıdır"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Qidalanma düyməsi basılıb"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Tənzimləməyə çalışın"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Hər dəfə barmağınızın yerini bir az dəyişdirin"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Üz təsdiq edildi, təsdiq düyməsinə basın"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Barmaq izi üçün avadanlıq yoxdur."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Barmaq izini ayarlamaq mümkün deyil"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Barmaq izinin vaxtı başa çatdı. Yenidən cəhd edin."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Barmaq izi ayarlama vaxtı bitib. Yenidən cəhd edin."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Barmaq izi əməliyyatı ləğv edildi."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Barmaq izi əməliyyatı istifadəçi tərəfindən ləğv edildi."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Cəhdlər çox oldu. Sonraya saxlayın."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Həddindən çox cəhd edilib. Əvəzində ekran kilidindən istifadə edin."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Həddindən çox cəhd edilib. Əvəzində ekran kilidindən istifadə edin."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Yenidən cəhd edin."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Barmaq izini emal etmək mümkün deyil. Yenidən cəhd edin."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Barmaq izi qeydə alınmayıb."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda barmaq izi sensoru yoxdur."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor müvəqqəti deaktivdir."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Təmir provayderini ziyarət edin."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Üz modelinizi yaratmaq olmur. Yenə cəhd edin."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Çox işıqlıdır. Daha az işıqlı şəkli sınayın."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Kifayət qədər İşıq yoxdur"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Telefonu uzaq tutun"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Telefonu yaxına tutun"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Telefonu yuxarı tutun"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Tətbiqlər başladılır."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Yükləmə başa çatır."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Qidalanma düyməsini basdınız — adətən bu, ekranı söndürür.\n\nBarmaq izini ayarlayarkən yüngülcə toxunmağa çalışın."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Ekranı söndürmək üçün toxunun"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Ekranı söndürün"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Ayarlamanı bitirmək üçün ekranı söndürün"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Söndürün"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Barmaq izini doğrulamağa davam edilsin?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Qidalanma düyməsini basdınız — adətən bu, ekranı söndürür.\n\nBarmaq izini doğrulamaq üçün yüngülcə toxunmağa çalışın."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ekranı söndürün"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dok spikerlər"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Xarici Cihaz"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Qulaqlıq"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistem"</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index bf9c57b5aa40..bcaea5d049f2 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Koristite zaključavanje ekrana"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Upotrebite zaključavanje ekrana da biste nastavili"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Jako pritisnite senzor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nije uspela obrada otiska prsta. Probajte ponovo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Prepoznavanje otiska prsta nije uspelo. Probajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Obrišite senzor za otisak prsta i probajte ponovo"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Obrišite senzor i probajte ponovo"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Jako pritisnite senzor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Previše sporo ste pomerili prst. Probajte ponovo."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Probajte sa drugim otiskom prsta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Previše je svetlo"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Otkriven je pritisak dugmeta za uključivanje"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Probajte da prilagodite"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Svaki put pomalo promenite položaj prsta"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je potvrđeno. Pritisnite Potvrdi"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardver za otiske prstiju nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Podešavanje otiska prsta nije uspelo"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Vremensko ograničenje za otisak prsta je isteklo. Probajte ponovo."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vreme za podešavanje otiska prsta je isteklo. Probajte ponovo."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja sa otiskom prsta je otkazana."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Korisnik je otkazao radnju sa otiskom prsta."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Previše pokušaja. Probajte ponovo kasnije."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Koristite zaključavanje ekrana umesto toga."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Koristite zaključavanje ekrana umesto toga."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Probajte ponovo."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Obrađivanje otiska prsta nije uspelo. Probajte ponovo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registrovan nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Posetite dobavljača za popravke."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Pravljenje modela lica nije uspelo. Probajte ponovo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Previše je svetlo. Probajte sa slabijim osvetljenjem."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nema dovoljno svetla"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Udaljite telefon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Približite telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Pomerite telefon nagore"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Pokretanje aplikacija."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Završavanje pokretanja."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnuli ste dugme za uključivanje – time obično isključujete ekran.\n\nProbajte lagano da dodirnete dok podešavate otisak prsta."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Dodirnite da biste isključili ekran"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Isključi ekran"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Završite podešavanje isključivanjem ekrana"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Isključi"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Nastavljate verifikaciju otiska prsta?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnuli ste dugme za uključivanje – time obično isključujete ekran.\n\nProbajte lagano da dodirnete da biste verifikovali otisak prsta."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Isključi ekran"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Zvučnici bazne stanice"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Spoljni uređaj"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Slušalice"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistem"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 1af3c7dd6c85..48dd891a213d 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -585,13 +585,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ужываць блакіроўку экрана"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Каб працягнуць, скарыстайце свой сродак блакіроўкі экрана"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Моцна націсніце на сканер"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Не атрымалася апрацаваць адбітак пальца. Паспрабуйце яшчэ раз."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Не ўдалося распазнаць адбітак пальца. Паўтарыце спробу."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Ачысціце сканер адбіткаў пальцаў і паўтарыце спробу"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Ачысціце сканер і паўтарыце спробу"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Моцна націсніце на сканер"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Палец рухаўся занадта павольна. Паспрабуйце яшчэ раз."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Паспрабуйце іншы адбітак пальца"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Занадта светла"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Выяўлена моцнае націсканне"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Паспрабуйце наладзіць"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Кожны раз крыху мяняйце пазіцыю пальца"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -603,12 +604,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Твар распазнаны. Націсніце, каб пацвердзіць"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Апаратныя сродкі адбіткаў пальцаў недаступныя."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не ўдалося захаваць адбітак пальца"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Час чакання выйшаў. Паспрабуйце яшчэ раз."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Наладжванне адбітка пальца не завершана. Паўтарыце спробу."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Аперацыя з адбіткамі пальцаў скасавана."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Аўтэнтыфікацыя па адбітках пальцаў скасавана карыстальнікам."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Занадта шмат спроб. Паспрабуйце яшчэ раз пазней."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Занадта шмат спроб. Скарыстайце блакіроўку экрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Занадта шмат спроб. Скарыстайце блакіроўку экрана."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Паўтарыце спробу."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не ўдалося апрацаваць адбітак пальца. Паўтарыце спробу."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Адбіткі пальцаў не зарэгістраваны."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На гэтай прыладзе няма сканера адбіткаў пальцаў."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчык часова выключаны."</string>
@@ -636,8 +637,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Звярніцеся ў сэрвісны цэнтр."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Не ўдалося стварыць мадэль твару. Паўтарыце."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Занадта светла. Прыглушыце асвятленне."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Недастаткова святла"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Перамясціце тэлефон далей"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Перамясціце тэлефон бліжэй"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Перамясціце тэлефон вышэй"</string>
@@ -1251,8 +1251,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск прыкладанняў."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завяршэнне загрузкі."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Вы націснулі кнопку сілкавання. Звычайна ў выніку гэтага дзеяння выключаецца экран.\n\nПадчас наладжвання адбітка пальца злёгку дакраніцеся да кнопкі."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Націсніце, каб выключыць экран"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Выключыць экран"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Каб завяршыць наладку, выключыце экран"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Выключыць"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Працягнуць спраўджанне адбітка пальца?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Вы націснулі кнопку сілкавання. Звычайна ў выніку гэтага дзеяння выключаецца экран.\n\nКаб спраўдзіць адбітак пальца, злёгку дакраніцеся да кнопкі."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Выключыць экран"</string>
@@ -1613,7 +1613,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"ТБ"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Тэлефон"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Дынамікі станцыi"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Знешняя прылада"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Навушнікі"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Сістэма"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 4ae1f21f8a9c..93049691fa60 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ползване на заключв. на екрана"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Въведете опцията си за заключване на екрана, за да продължите"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Натиснете добре върху сензора"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Отпечатъкът не бе обработен. Моля, опитайте отново."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Отпечатъкът не може да бъде разпознат. Опитайте отново."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Почистете сензора за отпечатъци и опитайте отново"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Почистете сензора и опитайте отново"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Натиснете добре върху сензора"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Преместихте пръста си твърде бавно. Моля, опитайте отново."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Опитайте с друг отпечатък"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Твърде светло е"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Установено е натискане на бутона за захранване"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Опитайте да коригирате"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Всеки път променяйте леко позицията на пръста си"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е удостоверено. Моля, натиснете „Потвърждаване“"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хардуерът за отпечатъци не е налице."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не може да се настрои отпечатък"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Времето за изчакване за отпечатък изтече. Опитайте отново."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Настройването на отпечатък не завърши навреме. Опитайте отново."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Операцията за отпечатък е анулирана."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Операцията за удостоверяване чрез отпечатък бе анулирана от потребителя."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Твърде много опити. Пробвайте отново по-късно."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Твърде много опити. Вместо това използвайте опция за заключване на екрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Твърде много опити. Вместо това използвайте опция за заключване на екрана."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Опитайте отново."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Отпечатъкът не може да бъде обработен. Опитайте отново."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Няма регистрирани отпечатъци."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Това устройство няма сензор за отпечатъци."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорът е временно деактивиран."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Посетете оторизиран сервиз."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Моделът на лицето ви не бе създаден. Опитайте пак."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Твърде светло е. Опитайте при по-слабо осветление."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Няма достатъчно светлина"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Отдалечете телефона"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Доближете телефона"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Преместете телефона по-високо"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Приложенията се стартират."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Зареждането завършва."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Натиснахте бутона за включване/изключване – това обикновено изключва екрана.\n\nОпитайте да докоснете леко, докато настройвате отпечатъка си."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Докоснете за изключване на екрана"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Изключване на екрана"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Изключете екрана за изход от настройката"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Изключване"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Напред с потвърждаването на отпечатъка?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Натиснахте бутона за включване/изключване – това обикновено изключва екрана.\n\nОпитайте да докоснете леко, за да потвърдите отпечатъка си."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Изключване на екрана"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Телевизор"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Телефон"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Докинг станц.: Високогов."</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Външно устройство"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Слушалки"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Система"</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 2aac0c9a3391..09033e94ee62 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"স্ক্রিন লক ব্যবহার করুন"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"চালিয়ে যেতে আপনার স্ক্রিন লক ব্যবহার করুন"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"সেন্সর জোরে প্রেস করুন"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"আঙ্গুলের ছাপ প্রক্রিয়া করা যায়নি৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"ফিঙ্গারপ্রিন্ট শনাক্ত করা যায়নি। আবার চেষ্টা করুন।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"আঙ্গুলের ছাপের সেন্সর পরিষ্কার করে আবার চেষ্টা করুন"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"সেন্সর পরিষ্কার করে আবার চেষ্টা করুন"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"সেন্সর জোরে প্রেস করুন"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"আঙ্গুল খুব ধীরে সরানো হয়েছে৷ অনুগ্রহ করে আবার চেষ্টা করুন৷"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"অন্য আঙ্গুলের ছাপ দিয়ে চেষ্টা করুন"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"অত্যন্ত উজ্জ্বল"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"পাওয়ার বোতাম প্রেস করার বিষয়টি শনাক্ত করা হয়েছে"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"অ্যাডজাস্ট করার চেষ্টা করুন"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"প্রতিবার আঙ্গুলের ছাপ সেটআপ করার সময় আপনার আঙ্গুলের অবস্থান সামান্য পরিবর্তন করুন"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ফেস যাচাই করা হয়েছে, \'কনফার্ম করুন\' বোতাম প্রেস করুন"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"আঙ্গুলের ছাপ নেওয়ার হার্ডওয়্যার অনুপলব্ধ৷"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"আঙ্গুলের ছাপ সেট-আপ করতে পারছি না"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"আঙ্গুলের ছাপ নেওয়ার সময়সীমা শেষ হযেছে৷ আবার চেষ্টা করুন৷"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ফিঙ্গারপ্রিন্ট সেট-আপ করার সময় সীমা পেরিয়ে গেছে। আবার চেষ্টা করুন।"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"আঙ্গুলের ছাপ অপারেশন বাতিল করা হয়েছে৷"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ব্যবহারকারী আঙ্গুলের ছাপের অপারেশনটি বাতিল করেছেন।"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"অনেকবার প্রচেষ্টা করা হয়েছে৷ পরে আবার চেষ্টা করুন৷"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"অনেকবার চেষ্টা করেছেন। পরিবর্তে স্ক্রিন লক ব্যবহার করুন।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"অনেকবার চেষ্টা করেছেন। পরিবর্তে স্ক্রিন লক ব্যবহার করুন।"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"আবার চেষ্টা করুন৷"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ফিঙ্গারপ্রিন্ট প্রসেস করা যায়নি। আবার চেষ্টা করুন।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"কোনও আঙ্গুলের ছাপ নথিভুক্ত করা হয়নি।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"এই ডিভাইসে আঙ্গুলের ছাপ নেওয়ার সেন্সর নেই।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"সেন্সর অস্থায়ীভাবে বন্ধ করা আছে।"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"একজন মেরামতি মিস্ত্রির কাছে যান।"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ফেস মডেল তৈরি করা যাচ্ছে না। আবার চেষ্টা করুন।"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"খুব উজ্জ্বল। আলো কমিয়ে চেষ্টা করে দেখুন।"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"পর্যাপ্ত আলো নেই"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ফোন আরও দূরে নিয়ে যান"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ফোন আরও কাছে নিয়ে আসুন"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ফোন আরও উঁচুতে তুলুন"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"অ্যাপ্লিকেশানগুলি শুরু করা হচ্ছে৷"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"চালু করা সম্পূর্ণ হচ্ছে৷"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"আপনি \'পাওয়ার\' বোতাম প্রেস করেছেন — এর ফলে সাধারণত স্ক্রিন বন্ধ হয়ে যায়।\n\nআঙ্গুলের ছাপ সেট-আপ করার সময় হালকাভাবে ট্যাপ করে দেখুন।"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"স্ক্রিন বন্ধ করতে ট্যাপ করুন"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"স্ক্রিন বন্ধ করুন"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"সেট-আপ সম্পূর্ণ করতে, স্ক্রিন বন্ধ করুন"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"বন্ধ করুন"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"আঙ্গুলের ছাপ যাচাই করা চালিয়ে যাবেন?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"আপনি \'পাওয়ার\' বোতাম প্রেস করেছেন — এর ফলে সাধারণত স্ক্রিন বন্ধ হয়ে যায়।\n\nআঙ্গুলের ছাপ যাচাই করতে হালকাভাবে ট্যাপ করে দেখুন।"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"স্ক্রিন বন্ধ করুন"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"টিভি"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ফোন"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ডক স্পিকার"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"এক্সটার্নাল ডিভাইস"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"হেডফোন"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"সিস্টেম"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index 3c87b0d25e57..d741095c0028 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Koristi zaključavanje ekrana"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Unesite zaključavanje ekrana da nastavite"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Čvrsto pritisnite senzor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Nije moguće prepoznati otisak prsta. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Očistite senzor za otisak prsta i pokušajte ponovo"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Očistite senzor i pokušajte ponovo"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Čvrsto pritisnite senzor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Presporo ste pomjerili prst. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Pokušajte s drugim otiskom prsta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Presvijetlo"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Otkriveno je pritiskanje dugmeta za uključivanje"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Pokušajte podesiti"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Svaki put pomalo promijenite položaj prsta"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je provjereno, pritisnite dugme za potvrdu"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardver za otisak prsta nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nije moguće postaviti otisak prsta"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Vrijeme za prepoznavanje otiska prsta je isteklo. Pokušajte ponovo."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vrijeme za postavljanje otiska prsta je isteklo. Pokušajte ponovo."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja s otiskom prsta je otkazana."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Korisnik je otkazao radnju s otiskom prsta."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Umjesto toga koristite zaključavanje ekrana."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Umjesto toga koristite zaključavanje ekrana."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Pokušajte ponovo."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nije moguće obraditi otisak prsta. Pokušajte ponovo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije prijavljen nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor za otisak prsta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Posjetite pružaoca usluga za popravke."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nije moguće kreirati model lica. Pokušajte ponovo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Previše svijetlo. Probajte s blažim osvjetljenjem."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nema dovoljno svjetlosti"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Odmaknite telefon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Primaknite telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Pomjerite telefon naviše"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Pokretanje aplikacija."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Pokretanje pri kraju."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnuli ste dugme za uključivanje. Tako se obično isključuje ekran.\n\nPokušajte ga lagano dodirnuti dok postavljate otisak prsta."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Dodirnite da isključite ekran"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Isključi ekran"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Da završite postavljanje, isključite ekran"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Isključi"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Nastaviti s potvrđivanjem otiska prsta?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnuli ste dugme za uključivanje. Tako se obično isključuje ekran.\n\nPokušajte ga lagano dodirnuti da potvrdite otisak prsta."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Isključi ekran"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Zvučnici priključne stanice"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Vanjski uređaj"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Slušalice"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistem"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 317c00444639..da132d9942df 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Utilitza el bloqueig de pantalla"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Introdueix el teu bloqueig de pantalla per continuar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Prem el sensor de manera ferma"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"No s\'ha pogut processar l\'empremta digital. Torna-ho a provar."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"No es pot reconèixer l\'empremta digital. Torna-ho a provar."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Neteja el sensor d\'empremtes digitals i torna-ho a provar"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Neteja el sensor i torna-ho a provar"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Prem el sensor de manera ferma"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"El dit s\'ha mogut massa lentament. Torna-ho a provar."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prova una altra empremta digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Hi ha massa llum"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"S\'ha premut el botó d\'engegada"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prova d\'ajustar l\'empremta digital"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Canvia lleugerament la posició del dit en cada intent"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Cara autenticada; prem el botó per confirmar"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El maquinari d\'empremtes digitals no està disponible."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"No es pot configurar l\'empremta digital"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"S\'ha esgotat el temps d\'espera per a l\'empremta digital. Torna-ho a provar."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Temps d\'espera esgotat per configurar l\'empremta digital. Torna-ho a provar."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"S\'ha cancel·lat l\'operació d\'empremta digital."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"L\'usuari ha cancel·lat l\'operació d\'empremta digital."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"S\'han produït massa intents. Torna-ho a provar més tard."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Massa intents. Utilitza el bloqueig de pantalla."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Massa intents. Utilitza el bloqueig de pantalla."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Torna-ho a provar."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No es pot processar l\'empremta digital. Torna-ho a provar."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No s\'ha registrat cap empremta digital."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aquest dispositiu no té sensor d\'empremtes digitals."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor està desactivat temporalment."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visita un proveïdor de reparacions."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"No es pot crear el model facial. Torna-ho a provar."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Massa brillant Prova una il·luminació més suau."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"No hi ha prou llum"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Allunya\'t del telèfon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Apropa el telèfon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mou el telèfon més amunt"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"S\'estan iniciant les aplicacions."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"S\'està finalitzant l\'actualització."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Has premut el botó d\'engegada, fet que sol apagar la pantalla.\n\nProva de tocar-lo lleugerament en configurar l\'empremta digital."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Toca per apagar la pantalla"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Apaga la pantalla"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Per finalitzar, apaga la pantalla"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desactiva"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vols continuar verificant l\'empremta?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Has premut el botó d\'engegada, fet que sol apagar la pantalla.\n\nProva de tocar-lo lleugerament per verificar l\'empremta digital."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Apaga la pantalla"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Televisor"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telèfon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Altaveus de la base"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Dispositiu extern"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Auriculars"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistema"</string>
@@ -1857,8 +1857,8 @@
<string name="confirm_battery_saver" msgid="5247976246208245754">"D\'acord"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions a la xarxa."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"Estalvi de bateria activa el tema fosc i limita o desactiva l\'activitat en segon pla, alguns efectes visuals, determinades funcions i algunes connexions a la xarxa."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"Per reduir l\'ús de dades, la funció Economitzador de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a les dades, però menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string>
- <string name="data_saver_enable_title" msgid="7080620065745260137">"Vols activar l\'Economitzador de dades?"</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"Per reduir l\'ús de dades, la funció Estalvi de dades evita que determinades aplicacions enviïn o rebin dades en segon pla. L\'aplicació que estiguis fent servir podrà accedir a les dades, però menys sovint. Això vol dir, per exemple, que les imatges no es mostraran fins que no les toquis."</string>
+ <string name="data_saver_enable_title" msgid="7080620065745260137">"Vols activar l\'Estalvi de dades?"</string>
<string name="data_saver_enable_button" msgid="4399405762586419726">"Activa"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Durant 1 minut (fins a les {formattedTime})}other{Durant # minuts (fins a les {formattedTime})}}"</string>
<string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Durant 1 min (fins a les {formattedTime})}other{Durant # min (fins a les {formattedTime})}}"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index cbd2df6d6e5b..831ccab4b7b6 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -585,13 +585,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Použít zámek obrazovky"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Pokračujte zadáním zámku obrazovky"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Pevně zatlačte na senzor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Zpracování otisku prstu se nezdařilo. Zkuste to znovu."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Otisk prstu se nepodařilo rozpoznat. Zkuste to znovu."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Vyčistěte snímač otisků prstů a zkuste to znovu"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Vyčistěte senzor a zkuste to znovu"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Zatlačte silně na senzor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Pohyb prstem byl příliš pomalý. Zkuste to znovu."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Zkuste jiný otisk prstu"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Je příliš světlo"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Bylo zjištěno stisknutí vypínače"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Zkuste provést úpravu"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Pokaždé lehce změňte polohu prstu"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -603,12 +604,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Obličej byl ověřen, stiskněte tlačítko pro potvrzení"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Není k dispozici hardware ke snímání otisků prstů."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Otisk prstu se nepodařilo nastavit"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Časový limit sejmutí otisku prstu vypršel. Zkuste to znovu."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Časový limit nastavení otisku prstu vypršel. Zkuste to znovu."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operace otisku prstu byla zrušena."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Uživatel operaci s otiskem prstu zrušil."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Příliš mnoho pokusů. Zkuste to později."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Příliš mnoho pokusů. Použijte zámek obrazovky."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Příliš mnoho pokusů. Použijte zámek obrazovky."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Zkuste to znovu."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Otisk prstu nelze zpracovat. Zkuste to znovu."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nejsou zaregistrovány žádné otisky prstů."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zařízení nemá snímač otisků prstů."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasně deaktivován."</string>
@@ -636,8 +637,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Navštivte servis"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Model se nepodařilo vytvořit. Zkuste to znovu."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Je příliš světlo. Zmírněte osvětlení."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nedostatečné osvětlení"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Umístěte telefon dál"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Umístěte telefon blíž"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Umístěte telefon výš"</string>
@@ -1251,8 +1251,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Spouštění aplikací."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Dokončování inicializace."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Stiskli jste vypínač – tím se obvykle vypíná obrazovka.\n\nPři nastavování otisku prstu je třeba klepat lehce."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Klepnutím vypnete obrazovku"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Vypnout obrazovku"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"K dokončení nastavení je nutné vypnout obrazovku"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Vypnout"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Pokračovat v ověřování otisku prstu?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Stiskli jste vypínač – tím se obvykle vypíná obrazovka.\n\nZkuste lehkým klepnutím ověřit svůj otisk prstu."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Vypnout obrazovku"</string>
@@ -1613,7 +1613,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Televize"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Reproduktory doku"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Externí zařízení"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Sluchátka"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Systém"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 083c42c76733..b35719d91b07 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Brug skærmlås"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Angiv din skærmlås for at fortsætte"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Hold fingeren nede på læseren"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Fingeraftrykket kunne ikke behandles. Prøv igen."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Fingeraftrykket kan ikke genkendes. Prøv igen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Rengør fingeraftrykslæseren, og prøv igen"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Rengør læseren, og prøv igen"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Hold fingeren nede på læseren"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Du bevægede fingeren for langsomt. Prøv igen."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prøv med et andet fingeraftryk"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Der er for lyst"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Et tryk på afbryderknappen er blevet registreret"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prøv at justere den"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Flyt fingeren en smule hver gang"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansigtet er godkendt. Tryk på Bekræft."</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardwaren til fingeraftryk er ikke tilgængelig."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Fingeraftrykket kan ikke gemmes"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Registrering af fingeraftryk fik timeout. Prøv igen."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigurationen af fingeraftryk fik timeout. Prøv igen."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeraftrykshandlingen blev annulleret."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeraftrykshandlingen blev annulleret af brugeren."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Du har prøvet for mange gange. Prøv igen senere."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Du har brugt for mange forsøg. Brug skærmlåsen i stedet."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Du har brugt for mange forsøg. Brug skærmlåsen i stedet."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Prøv igen."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Fingeraftrykket kan ikke behandles. Prøv igen."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Der er ikke registreret nogen fingeraftryk."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enhed har ingen fingeraftrykslæser."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidigt deaktiveret."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Få den repareret."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Din ansigtsmodel kan ikke oprettes. Prøv igen."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Der er for lyst. Prøv en mere dæmpet belysning."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Der er ikke nok lys"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Flyt telefonen længere væk"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Flyt telefonen tættere på"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Løft telefonen højere op"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Åbner dine apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Gennemfører start."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du har trykket på afbryderknappen, hvilket som regel slukker skærmen.\n\nPrøv at trykke let på knappen, mens du konfigurerer dit fingeraftryk."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tryk for at slukke skærmen"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Sluk skærm"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Sluk skærmen for at afslutte konfigurationen"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Deaktiver"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vil du bekræfte dit fingeraftryk?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du har trykket på afbryderknappen, hvilket som regel slukker skærmen.\n\nPrøv at trykke let på knappen for at bekræfte dit fingeraftryk."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Sluk skærm"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Tv"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dockstationens højttalere"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Ekstern enhed"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Hovedtelefoner"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"System"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index fc8eadaf0362..2d151d9dc83b 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Displaysperre verwenden"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Displaysperre eingeben, um fortzufahren"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Drücke fest auf den Sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Fingerabdruck konnte nicht verarbeitet werden. Bitte versuche es noch einmal."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Fingerabdruck wurde nicht erkannt. Versuch es noch einmal."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Reinige den Fingerabdrucksensor und versuch es noch einmal"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Reinige den Sensor und versuche es noch einmal"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Drücke fest auf den Sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Finger zu langsam bewegt. Bitte versuche es noch einmal."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Anderen Fingerabdruck verwenden"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Zu hell"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Drücken der Ein-/Aus-Taste erkannt"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Versuche, den Finger anders aufzulegen"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ändere jedes Mal leicht die Position deines Fingers"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gesicht authentifiziert, bitte bestätigen"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerabdruckhardware nicht verfügbar"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Fingerabdruck konnte nicht eingerichtet werden"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Zeitüberschreitung bei Fingerabdruck. Bitte versuche es noch einmal."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Zeitüberschreitung bei Fingerabdruckeinrichtung. Versuch es noch einmal."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerabdruckvorgang abgebrochen"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vorgang der Fingerabdruckauthentifizierung vom Nutzer abgebrochen."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Zu viele Versuche, bitte später noch einmal versuchen"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Zu viele Versuche. Verwende stattdessen die Displaysperre."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Zu viele Versuche. Verwende stattdessen die Displaysperre."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Bitte versuche es noch einmal."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Fingerabdruck kann nicht verarbeitet werden. Versuch es noch einmal."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Keine Fingerabdrücke erfasst."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dieses Gerät hat keinen Fingerabdrucksensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Der Sensor ist vorübergehend deaktiviert."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Suche einen Reparaturdienstleister auf."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Kein Gesichtsmodell möglich. Versuche es erneut."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Zu hell. Schwächere Beleuchtung ausprobieren."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nicht genug Licht"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Bewege das Smartphone weiter weg"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Bewege das Smartphone näher heran"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Bewege das Smartphone nach oben"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Apps werden gestartet..."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Start wird abgeschlossen..."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du hast die Ein-/Aus-Taste gedrückt — damit wird der Bildschirm ausgeschaltet.\n\nTippe die Taste leicht an, um deinen Fingerabdruck einzurichten."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tippen, um Display auszuschalten"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Display ausschalten"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Zum Beenden der Einrichtung Bildschirm deaktivieren"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Deaktivieren"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Mit der Fingerabdruckprüfung fortfahren?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du hast die Ein-/Aus-Taste gedrückt — damit wird der Bildschirm ausgeschaltet.\n\nTippe die Taste leicht an, um mit deinem Fingerabdruck deine Identität zu bestätigen."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ausschalten"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dock-Lautsprecher"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Externes Gerät"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Kopfhörer"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"System"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index c76b2b0352d8..d3a9c1746b95 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Χρήση κλειδώματος οθόνης"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Χρησιμοποιήστε το κλείδωμα οθόνης για να συνεχίσετε"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Πιέστε σταθερά τον αισθητήρα"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Δεν ήταν δυνατή η επεξεργασία του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Δεν είναι δυνατή η αναγνώριση του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Καθαρίστε τον αισθητήρα δακτυλικών αποτυπωμάτων και δοκιμάστε ξανά"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Καθαρίστε τον αισθητήρα και δοκιμάστε ξανά"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Πιέστε σταθερά τον αισθητήρα"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Πολύ αργή κίνηση δαχτύλου. Δοκιμάστε ξανά."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Δοκιμάστε άλλο δακτυλικό αποτύπωμα"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Υπερβολικά έντονος φωτισμός"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Εντοπίστηκε πάτημα του κουμπιού λειτουργίας"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Δοκιμάστε να το προσαρμόσετε"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Αλλάζετε ελαφρώς τη θέση του δακτύλου σας κάθε φορά."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Έγινε έλεγχος ταυτότητας προσώπου, πατήστε \"Επιβεβαίωση\""</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Ο εξοπλισμός δακτυλικού αποτυπώματος δεν είναι διαθέσιμος."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Δεν είναι δυνατή η ρύθμιση του δακτυλικού αποτυπώματος"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Λήξη χρονικού ορίου δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Λήξη χρονικού ορίου ρύθμισης δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Η λειτουργία δακτυλικού αποτυπώματος ακυρώθηκε από τον χρήστη."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Πάρα πολλές προσπάθειες. Δοκιμάστε ξανά αργότερα."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Υπερβολικά πολλές προσπάθειες. Χρησιμοποιήστε εναλλακτικά το κλείδωμα οθόνης."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Υπερβολικά πολλές προσπάθειες. Χρησιμοποιήστε εναλλακτικά το κλείδωμα οθόνης."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Δοκιμάστε ξανά."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Δεν είναι δυνατή η επεξεργασία του δακτυλικού αποτυπώματος. Δοκιμάστε ξανά."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Δεν έχουν καταχωριστεί δακτυλικά αποτυπώματα."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Αυτή η συσκευή δεν διαθέτει αισθητήρα δακτυλικού αποτυπώματος."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Ο αισθητήρας απενεργοποιήθηκε προσωρινά."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Επισκεφτείτε έναν πάροχο υπηρεσιών επισκευής."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Αδύν. η δημιουρ. του μοντ. προσώπ. Δοκιμάστε ξανά."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Υπερβολικά έντονος φωτισμός. Δοκιμάστε πιο ήπιο."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Δεν υπάρχει αρκετό φως"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Απομακρύνετε περισσότερο το τηλέφωνο"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Φέρτε πιο κοντά το τηλέφωνό σας"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Μετακινήστε το τηλέφωνο πιο ψηλά"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Έναρξη εφαρμογών."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Ολοκλήρωση εκκίνησης."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Πατήσατε το κουμπί λειτουργίας. Αυτό συνήθως απενεργοποιεί την οθόνη.\n\nΔοκιμάστε να πατήσετε απαλά κατά τη ρύθμιση του δακτυλικού σας αποτυπώματος."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Πατήστε για απενεργοποίηση οθόνης"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Απενεργοπ. οθόνης"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Απενεργ. την οθόνη για ολοκλήρ. ρύθμισης"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Απενεργοποίηση"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Συνέχιση επαλήθευσης δακτ. αποτυπώματος;"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Πατήσατε το κουμπί λειτουργίας. Αυτό συνήθως απενεργοποιεί την οθόνη.\n\nΔοκιμάστε να πατήστε απαλά για να επαληθεύσετε το δακτυλικό σας αποτύπωμα."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Απενεργοπ. οθόνης"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Τηλεόραση"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Τηλέφωνο"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Ηχεία βάσης σύνδεσης"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Εξωτερική συσκευή"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Ακουστικά"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Σύστημα"</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 8591467ddf33..fcc6cdd574e8 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Use screen lock"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Enter your screen lock to continue"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Press firmly on the sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Couldn\'t process fingerprint. Please try again."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Can’t recognise fingerprint. Try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Clean fingerprint sensor and try again"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Clean sensor and try again"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Press firmly on the sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Finger moved too slow. Please try again."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Try another fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Too bright"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Power press detected"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Try adjusting"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Change the position of your finger slightly each time"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Fingerprint timeout reached. Try again."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Too many attempts. Try again later."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Try again."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Can’t create your face model. Try again."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Too bright. Try gentler lighting."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Not enough light"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Move phone further away"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Move phone closer"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Move phone higher"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tap to turn off screen"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"To end setup, turn off screen"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Turn off"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Phone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dock speakers"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"External device"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Headphones"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"System"</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index 08097a540930..de1516c452b8 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Use screen lock"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Enter your screen lock to continue"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Press firmly on the sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Couldn\'t process fingerprint. Please try again."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Can’t recognise fingerprint. Try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Clean fingerprint sensor and try again"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Clean sensor and try again"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Press firmly on the sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Finger moved too slow. Please try again."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Try another fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Too bright"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Power press detected"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Try adjusting"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Change the position of your finger slightly each time"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Fingerprint timeout reached. Try again."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Too many attempts. Try again later."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Try again."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Can’t create your face model. Try again."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Too bright. Try gentler lighting."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Not enough light"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Move phone further away"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Move phone closer"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Move phone higher"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tap to turn off screen"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"To end setup, turn off screen"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Turn off"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Phone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dock speakers"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"External device"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Headphones"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"System"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index bf82587a5000..ef91ac234ff6 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Use screen lock"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Enter your screen lock to continue"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Press firmly on the sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Couldn\'t process fingerprint. Please try again."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Can’t recognise fingerprint. Try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Clean fingerprint sensor and try again"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Clean sensor and try again"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Press firmly on the sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Finger moved too slow. Please try again."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Try another fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Too bright"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Power press detected"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Try adjusting"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Change the position of your finger slightly each time"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Fingerprint timeout reached. Try again."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Too many attempts. Try again later."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Try again."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Can’t create your face model. Try again."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Too bright. Try gentler lighting."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Not enough light"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Move phone further away"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Move phone closer"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Move phone higher"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tap to turn off screen"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"To end setup, turn off screen"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Turn off"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Phone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dock speakers"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"External device"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Headphones"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"System"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 110e68f3683e..6d7f422e77f7 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Use screen lock"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Enter your screen lock to continue"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Press firmly on the sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Couldn\'t process fingerprint. Please try again."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Can’t recognise fingerprint. Try again."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Clean fingerprint sensor and try again"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Clean sensor and try again"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Press firmly on the sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Finger moved too slow. Please try again."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Try another fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Too bright"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Power press detected"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Try adjusting"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Change the position of your finger slightly each time"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Face authenticated. Please press confirm"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingerprint hardware not available."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Can’t set up fingerprint"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Fingerprint timeout reached. Try again."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingerprint setup timed out. Try again."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingerprint operation cancelled."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingerprint operation cancelled by user."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Too many attempts. Try again later."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Too many attempts. Use screen lock instead."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Too many attempts. Use screen lock instead."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Try again."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Can’t process fingerprint. Try again."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No fingerprints enrolled."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"This device does not have a fingerprint sensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporarily disabled."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visit a repair provider."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Can’t create your face model. Try again."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Too bright. Try gentler lighting."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Not enough light"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Move phone further away"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Move phone closer"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Move phone higher"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starting apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finishing boot."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly while setting up your fingerprint."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tap to turn off screen"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Turn off screen"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"To end setup, turn off screen"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Turn off"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continue verifying your fingerprint?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"You pressed the power button – this usually turns off the screen.\n\nTry tapping lightly to verify your fingerprint."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Turn off screen"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Phone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dock speakers"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"External device"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Headphones"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"System"</string>
diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml
index ddb93a81b80d..4fee71d66aaa 100644
--- a/core/res/res/values-en-rXC/strings.xml
+++ b/core/res/res/values-en-rXC/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‏‏‏‏‎‏‏‏‏‏‎‎‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‎‎Use screen lock‎‏‎‎‏‎"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‏‏‏‏‏‎Enter your screen lock to continue‎‏‎‎‏‎"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‎‎‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‏‎‎‎‎‎‎‏‏‎‎‎‏‎‎‎Press firmly on the sensor‎‏‎‎‏‎"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‎‎‎‎‎‏‏‏‏‎‎‎‎‎‎‎‏‎‎‏‏‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‏‏‎Couldn\'t process fingerprint. Please try again.‎‏‎‎‏‎"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‏‎‏‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‎‎‎‏‎‏‎‎Can’t recognize fingerprint. Try again.‎‏‎‎‏‎"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎‎‏‎‎‎‏‏‎‎‏‏‏‎‏‎‏‏‎‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎Clean fingerprint sensor and try again‎‏‎‎‏‎"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‎‏‏‏‎‎‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‏‎‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‎‎‏‎Clean sensor and try again‎‏‎‎‏‎"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‏‎‎‏‎‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‎‎‎Press firmly on the sensor‎‏‎‎‏‎"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‎‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎‎‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‏‏‎‎‎Finger moved too slow. Please try again.‎‏‎‎‏‎"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‏‎‏‎‎‏‎‏‏‏‏‏‏‎‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‎‎‏‏‏‏‏‏‏‎‎‎‎‎‏‎Try another fingerprint‎‏‎‎‏‎"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‎‎‏‎‎‏‏‎‏‎‏‏‎‏‎‏‎‏‏‏‎‎‎‎‏‎‏‏‏‎Too bright‎‏‎‎‏‎"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‏‏‎‎‏‏‏‏‏‏‎‏‎‏‎‏‏‏‎‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‏‎Power press detected‎‏‎‎‏‎"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‎‎‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‏‏‎‎‏‎‏‎‏‎‎‎Try adjusting‎‏‎‎‏‎"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‏‏‏‎‎‎‎‏‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‎‏‎‎‏‏‎‎‎‎‎Change the position of your finger slightly each time‎‏‎‎‏‎"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‎‏‏‏‏‏‏‏‎‎‎‎‏‏‏‎‏‎‏‏‎‏‎‎‏‏‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‏‏‎‏‏‎Face authenticated, please press confirm‎‏‎‎‏‎"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‎‏‏‎‏‏‎‎‏‏‎‏‎‎‏‎‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎Fingerprint hardware not available.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‏‏‎‏‎‏‏‏‎‎‏‏‎‎‎‎‎‏‎‏‏‎‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‏‏‏‎‎‎Can’t set up fingerprint‎‏‎‎‏‎"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‎‏‏‏‎‎‏‎‎‏‎‎‎‏‏‎‏‏‎‏‎‏‎‎‏‏‎‎‏‎‏‏‎‏‏‎‎‏‎‏‏‎‏‏‏‏‎‏‏‎‏‎‏‎‎‏‎‎Fingerprint time out reached. Try again.‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‎‏‎‏‎‎‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎Fingerprint setup timed out. Try again.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‎‎‏‎‏‏‎‎‏‏‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‏‏‎‎Fingerprint operation canceled.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‎‎‎‏‏‏‎‎‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎Fingerprint operation canceled by user.‎‏‎‎‏‎"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‏‎‏‎‎‏‎‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎Too many attempts. Try again later.‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‎‏‎‏‎‏‏‏‎‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎Too many attempts. Use screen lock instead.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‎‎‏‎‏‏‎Too many attempts. Use screen lock instead.‎‏‎‎‏‎"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‏‏‎‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‎‎‎‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎Try again.‎‏‎‎‏‎"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‎‏‎‏‎‎‎‎Can’t process fingerprint. Try again.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎No fingerprints enrolled.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‎‎‎‎‎‎‏‎‎‎‏‎‏‏‎‏‏‏‏‎‎‎‏‎‎‎‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‎‎‎‎‎‎‎‏‏‎‎‏‎‏‏‎‎This device does not have a fingerprint sensor.‎‏‎‎‏‎"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‏‏‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‎‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‏‎‏‏‏‎‎‎‏‎Sensor temporarily disabled.‎‏‎‎‏‎"</string>
@@ -1248,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‏‎‏‏‎‏‎‏‏‏‎‎‏‏‎‏‎‏‎‏‏‎‏‎‏‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‎Starting apps.‎‏‎‎‏‎"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‏‎‏‏‎‏‎‎‎‏‏‏‎‎‎‏‏‏‏‎‏‎‏‎‎‏‎‎‎‏‎‏‏‎‏‎‎Finishing boot.‎‏‎‎‏‎"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‎‎‏‏‎‎‎‏‎‎‏‎‏‎‏‏‏‏‏‏‎‎‎‏‏‏‎‎‏‎‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‏‏‎‎‎You pressed the power button — this usually turns off the screen.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try tapping lightly while setting up your fingerprint.‎‏‎‎‏‎"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‏‎‎‎‎‎‏‎‏‏‏‎‏‎‎‏‎‏‎‏‏‏‏‏‎‏‎‎‎‏‎‎‏‏‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‎‎Tap to turn off screen‎‏‎‎‏‎"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‎‎‎‏‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‎‏‏‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‏‎‏‎Turn off screen‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‏‎‏‎‎‏‎‏‎‏‏‏‎‎‎‏‎‎‎‏‏‎‏‎‏‏‏‎‏‎‏‏‎‏‏‏‎‎‏‏‏‏‎‏‏‏‎‎‏‎‎‎‎‏‏‎To end setup, turn off screen‎‏‎‎‏‎"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‎‎‎‏‏‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‎‏‏‎‏‎‏‏‏‎‏‏‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‎‎Turn off‎‏‎‎‏‎"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‎‏‏‎‎‎‏‏‎‏‎‎‏‎‏‏‏‎‎‎‏‏‎Continue verifying your fingerprint?‎‏‎‎‏‎"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‏‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‎‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‎‏‎You pressed the power button — this usually turns off the screen.‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎\n‎‏‎‎‏‏‏‎Try tapping lightly to verify your fingerprint.‎‏‎‎‏‎"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‎‏‎‎‎‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‎‎‎‏‎‏‏‏‎‎‎‎‏‏‏‏‎‎‎‏‎‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎‏‏‎Turn off screen‎‏‎‎‏‎"</string>
@@ -1610,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‎‏‎‏‎‏‏‎‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‎‎‏‎‏‎‎‏‏‎‏‎‎‎‎‏‎‎‎‏‏‏‎TV‎‏‎‎‏‎"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‏‎‏‎‏‏‏‏‎‎‎‎‎‎‏‏‎‏‎‎‏‎Phone‎‏‎‎‏‎"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‎‏‏‎‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‏‏‏‎‏‏‏‏‏‎‎‏‏‎‏‎‎‎‎‎‎‏‏‎‎‎‏‏‎‎‏‎Dock speakers‎‏‎‎‏‎"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‎‎‎‎‏‎‎‎‎‏‎‎‎‎‎‎‏‏‎‏‎‎HDMI‎‏‎‎‏‎"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‏‎‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‏‏‏‏‏‎‏‏‏‎‎‏‎‏‏‏‏‏‎External Device‎‏‎‎‏‎"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‎‎‎‎‎‏‏‏‎‏‎‎‏‎‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‎Headphones‎‏‎‎‏‎"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‏‎‎‎‏‎‏‎‏‎‏‏‏‎‎‎‏‎‏‎‏‏‎‎‎‏‎‎‎USB‎‏‎‎‏‎"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‏‏‏‎‎‎‏‏‎‏‎‏‎‏‏‎‎‎‏‎‎‎‎‎‏‏‏‎‎‏‎‏‎‎‏‏‎System‎‏‎‎‏‎"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 933ccfbec174..efaaa9758e53 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar bloqueo de pantalla"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Ingresa tu bloqueo de pantalla para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Presiona con firmeza el sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"No se pudo procesar la huella dactilar. Vuelve a intentarlo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"No se reconoce la huella dactilar. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpia el sensor de huellas dactilares y vuelve a intentarlo"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpia el sensor y vuelve a intentarlo"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Presiona con firmeza el sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Moviste el dedo muy lento. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prueba con otra huella dactilar"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Demasiada luz"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Se detectó una presión del botón de encendido"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prueba ajustarla"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia un poco la posición del dedo cada vez"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se autenticó el rostro; presiona Confirmar"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El hardware para detectar huellas dactilares no está disponible."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"No se puede configurar la huella dactilar"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Finalizó el tiempo de espera para la huella dactilar. Vuelve a intentarlo."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Se agotó el tiempo de espera para configurar la huella dactilar. Vuelve a intentarlo."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Se canceló la operación de huella dactilar."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"El usuario canceló la operación de huella dactilar."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiados intentos. Vuelve a intentarlo más tarde."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiados intentos. Utiliza el bloqueo de pantalla en su lugar."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Utiliza el bloqueo de pantalla en su lugar."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Vuelve a intentarlo."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No se puede procesar la huella dactilar. Vuelve a intentarlo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se registraron huellas digitales."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas dactilares."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Se inhabilitó temporalmente el sensor."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Consulta a un proveedor de reparaciones."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"No se puede crear modelo de rostro. Reinténtalo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Demasiado brillante. Prueba con menos iluminación."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"No hay suficiente luz"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Aleja el teléfono un poco más"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Acerca el teléfono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mueve el teléfono hacia arriba"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando aplicaciones"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalizando el inicio"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Presionaste el botón de encendido. Por lo general, esta acción apaga la pantalla.\n\nPresiona suavemente mientras configuras tu huella dactilar."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Presiona para apagar la pantalla"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Apagar pantalla"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Para finalizar, apaga la pantalla"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Apagar"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"¿Verificar huella dactilar?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Presionaste el botón de encendido. Por lo general, esta acción apaga la pantalla.\n\nPresiona suavemente para verificar tu huella dactilar."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Apagar pantalla"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Dispositivo"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Altavoces del conector"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Dispositivo externo"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Auriculares"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistema"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 9795e238cf31..e6c088fae355 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar bloqueo de pantalla"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Introduce tu bloqueo de pantalla para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Mantén pulsado firmemente el sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"No se ha podido procesar la huella digital. Vuelve a intentarlo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"No se puede reconocer la huella digital. Inténtalo de nuevo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpia el sensor de huellas digitales e inténtalo de nuevo"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpia el sensor e inténtalo de nuevo"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Mantén pulsado firmemente el sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Has movido el dedo demasiado despacio. Vuelve a intentarlo."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prueba con otra huella digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Demasiada luz"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Se ha pulsado el botón de encendido"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prueba a mover el dedo"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia ligeramente el dedo de posición cada vez"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Se ha autenticado la cara, pulsa para confirmar"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"El hardware de huella digital no está disponible."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"No se puede configurar la huella digital"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Se ha alcanzado el tiempo de espera de la huella digital. Vuelve a intentarlo."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Tiempo de espera para configurar la huella digital agotado. Inténtalo de nuevo."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Se ha cancelado la operación de huella digital."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"El usuario ha cancelado la operación de huella digital."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiados intentos. Vuelve a intentarlo más tarde."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiados intentos. Usa el bloqueo de pantalla."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Usa el bloqueo de pantalla."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Vuelve a intentarlo."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"No se puede procesar la huella digital. Inténtalo de nuevo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"No se ha registrado ninguna huella digital."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo no tiene sensor de huellas digitales."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"El sensor está inhabilitado en estos momentos."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visita un proveedor de reparaciones."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"No se puede crear tu modelo. Inténtalo de nuevo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Hay demasiada luz. Busca un sitio menos iluminado."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"No hay suficiente luz"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Aleja el teléfono"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Acerca el teléfono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Sube el teléfono"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando aplicaciones"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalizando inicio..."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Has pulsado el botón de encendido, lo que suele apagar la pantalla.\n\nPrueba a apoyar el dedo ligeramente para verificar tu huella digital."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Toca para apagar la pantalla"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Apagar pantalla"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Apaga la pantalla para finalizar"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Apagar"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"¿Seguir verificando tu huella digital?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Has pulsado el botón de encendido, lo que suele apagar la pantalla.\n\nPrueba a apoyar el dedo ligeramente para verificar tu huella digital."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Apagar pantalla"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Teléfono"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Altavoces de la base"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Dispositivo externo"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Auriculares"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistema"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index fe7549e3434e..7f761c4eded4 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ekraaniluku kasutamine"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Jätkamiseks sisestage oma ekraanilukk"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Vajutage tugevalt andurile"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Sõrmejälge ei õnnestunud töödelda. Proovige uuesti."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Sõrmejälge ei õnnestu tuvastada. Proovige uuesti."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Puhastage sõrmejäljeandur ja proovige uuesti"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Puhastage andur ja proovige uuesti"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Vajutage tugevalt andurile"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Sõrm liikus liiga aeglaselt. Proovige uuesti."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Proovige teist sõrmejälge"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Liiga ere"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Tuvastati toitenupu vajutamine"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Proovige kohandada"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Muutke iga kord pisut oma sõrme asendit"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Nägu on autenditud, vajutage käsku Kinnita"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Sõrmejälje riistvara pole saadaval."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Sõrmejälge ei saa seadistada"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Sõrmejälje riistvara taimeri ajalõpp. Proovige uuesti."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Sõrmejälje seadistamine aegus. Proovige uuesti."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Sõrmejälje toiming tühistati."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Kasutaja tühistas sõrmejälje kasutamise."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Liiga palju katseid. Proovige hiljem uuesti."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Liiga palju katseid. Kasutage selle asemel ekraanilukku."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Liiga palju katseid. Kasutage selle asemel ekraanilukku."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Proovige uuesti."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Sõrmejälge ei õnnestu töödelda. Proovige uuesti."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ühtegi sõrmejälge pole registreeritud."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Selles seadmes pole sõrmejäljeandurit."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Andur on ajutiselt keelatud."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Külastage remonditeenuse pakkujat."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Teie näomudelit ei saa luua. Proovige uuesti."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Liiga ere. Proovige hämaramat valgust."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Pole piisavalt valgust"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Liigutage telefoni kaugemale"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Liigutage telefoni lähemale"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Liigutage telefoni kõrgemale"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Rakenduste käivitamine."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Käivitamise lõpuleviimine."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Vajutasite toitenuppu – tavaliselt lülitab see ekraani välja.\n\nPuudutage õrnalt ja seadistage oma sõrmejälg."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Puudutage ekraani väljalülitamiseks"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Lülita ekraan välja"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Seadistuse lõpet. lülitage ekraan välja"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Lülita välja"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Kas jätkata sõrmejälje kinnitamist?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Vajutasite toitenuppu – tavaliselt lülitab see ekraani välja.\n\nPuudutage õrnalt, et oma sõrmejälg kinnitada."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Lülita ekraan välja"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Teler"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Doki kõlarid"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Väline seade"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Kõrvaklapid"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Süsteem"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 7f424ebd56eb..ee07ce1a4c0c 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Erabili pantailaren blokeoa"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Aurrera egiteko, desblokeatu pantailaren blokeoa"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Sakatu irmo sentsorea"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Ezin izan da prozesatu hatz-marka. Saiatu berriro."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Ezin da hauteman hatz-marka. Saiatu berriro."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Garbitu hatz-marken sentsorea eta saiatu berriro"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Garbitu sentsorea eta saiatu berriro"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Sakatu irmo sentsorea"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Mantsoegi mugitu duzu hatza. Saiatu berriro."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Erabili beste hatz-marka bat"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Argi gehiegi dago"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Hauteman egin da etengailua sakatu dela"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Saiatu doituta"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Aldi bakoitzean, aldatu hatzaren posizioa apur bat"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autentifikatu da aurpegia; sakatu Berretsi"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hatz-marken hardwarea ez dago erabilgarri."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ezin da konfiguratu hatz-marka"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Hatz-markak prozesatzeko denbora-muga gainditu da. Saiatu berriro."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Gainditu egin da hatz-marka konfiguratzeko denbora-muga. Saiatu berriro."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Hatz-markaren eragiketa bertan behera utzi da."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Erabiltzaileak bertan behera utzi du hatz-marka bidezko eragiketa."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Saiakera gehiegi egin dituzu. Saiatu berriro geroago."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Saiakera gehiegi egin dira. Erabili pantailaren blokeoa."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Saiakera gehiegi egin dira. Erabili pantailaren blokeoa."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Saiatu berriro."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ezin da prozesatu hatz-marka. Saiatu berriro."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ez da erregistratu hatz-markarik."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Gailu honek ez du hatz-marken sentsorerik."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sentsorea aldi baterako desgaitu da."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Jarri harremanetan konponketak egiten dituen hornitzaile batekin."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Ezin da sortu aurpegi-eredua. Saiatu berriro."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Argi gehiegi dago. Joan toki ilunago batera."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Ilunegi dago"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Urrundu telefonoa"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Hurbildu telefonoa"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Igo telefonoa"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Aplikazioak abiarazten."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Bertsio-berritzea amaitzen."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Etengailua sakatu duzu; pantaila itzaltzeko balio du horrek.\n\nUki ezazu arin, hatz-marka konfiguratu bitartean."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Pantaila itzaltzeko, sakatu hau"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Itzali pantaila"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Konfiguratzen amaitzeko, itzali pantaila"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Itzali"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Hatz-marka egiaztatzen jarraitu nahi duzu?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Etengailua sakatu duzu; pantaila itzaltzeko balio du horrek.\n\nUki ezazu arin, hatz-marka egiaztatzeko."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Itzali pantaila"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Telebista"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefonoa"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Konektatu bozgorailuak oinarrira"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Kanpoko gailua"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Entzungailuak"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistema"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 83821a3e6ef2..fdda0a70f4c3 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"از قفل صفحه استفاده کنید"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"برای ادامه، قفل صفحه‌تان را وارد کنید"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"محکم روی حسگر فشار دهید"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"اثرانگشت پردازش نشد. لطفاً دوباره امتحان کنید."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"اثر انگشت شناسایی نشد. دوباره امتحان کنید."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"حسگر اثر انگشت را تمیز و دوباره امتحان کنید"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"حسگر را تمیز و دوباره امتحان کنید"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"محکم روی حسگر فشار دهید"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"حرکت انگشت خیلی آهسته بود. لطفاً دوباره امتحان کنید."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"اثر انگشت دیگری را امتحان کنید"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"خیلی روشن است"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"فشردن دکمه روشن/ خاموش شناسایی شد"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"اثر انگشت را تنظیم کنید"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"هربار موقعیت انگشتتان را کمی تغییر دهید"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چهره اصالت‌سنجی شد، لطفاً تأیید را فشار دهید"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"سخت‌افزار اثرانگشت در دسترس نیست."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"اثر انگشت راه‌اندازی نشد"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"مهلت ثبت اثر انگشت به‌پایان رسید. دوباره امتحان کنید."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"مهلت راه‌اندازی اثر انگشت به‌پایان رسید. دوباره امتحان کنید."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"عملکرد اثر انگشت لغو شد."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"کاربر عملیات اثر انگشت را لغو کرد"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"تلاش‌های زیادی انجام شده است. بعداً دوباره امتحان کنید."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"تلاش‌ها از حد مجاز بیشتر شده است. به‌جای آن از قفل صفحه استفاده کنید."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"تلاش‌های بیش‌ازحد. حالا از قفل صفحه استفاده کنید."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"دوباره امتحان کنید."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"اثر انگشت پردازش نشد. دوباره امتحان کنید."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"اثر انگشتی ثبت نشده است."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"این دستگاه حسگر اثر انگشت ندارد."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"حسگر به‌طور موقت غیرفعال است."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"به ارائه‌دهنده خدمات تعمیر مراجعه کنید."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"مدل چهره ایجاد نشد. دوباره امتحان کنید."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"خیلی روشن است. روشنایی‌اش را ملایم‌تر کنید."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"نور کافی نیست"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"تلفن را دورتر ببرید"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"تلفن را نزدیک‌تر بیاورید"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"تلفن را بالاتر ببرید"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"درحال آغاز کردن برنامه‌ها."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"درحال اتمام راه‌اندازی."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"دکمه روشن/ خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nهنگام راه‌اندازی اثر انگشت، آرام ضربه بزنید."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"برای خاموش کردن صفحه‌نمایش، ضربه بزنید"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"خاموش کردن صفحه"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"برای اتمام راه‌اندازی، صفحه را خاموش کنید"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"خاموش کردن"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"تأیید اثر انگشت را ادامه می‌دهید؟"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"دکمه روشن/ خاموش را فشار دادید — این کار معمولاً صفحه‌نمایش را خاموش می‌کند.\n\nبرای تأیید اثر انگشتتان، آرام ضربه بزنید."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"خاموش کردن صفحه"</string>
@@ -1491,8 +1491,8 @@
<string name="vpn_title_long" msgid="6834144390504619998">"‏VPN را <xliff:g id="APP">%s</xliff:g> فعال کرده است"</string>
<string name="vpn_text" msgid="2275388920267251078">"برای مدیریت شبکه ضربه بزنید."</string>
<string name="vpn_text_long" msgid="278540576806169831">"به <xliff:g id="SESSION">%s</xliff:g> متصل شد. برای مدیریت شبکه ضربه بزنید."</string>
- <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"‏در حال اتصال VPN همیشه فعال…"</string>
- <string name="vpn_lockdown_connected" msgid="2853127976590658469">"‏VPN همیشه فعال متصل شد"</string>
+ <string name="vpn_lockdown_connecting" msgid="6096725311950342607">"‏درحال اتصال به VPN همیشه روشن…"</string>
+ <string name="vpn_lockdown_connected" msgid="2853127976590658469">"‏VPN همیشه روشن متصل شد"</string>
<string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"‏از «VPN همیشه روشن» قطع شد"</string>
<string name="vpn_lockdown_error" msgid="4453048646854247947">"‏به «VPN همیشه روشن» متصل نشد"</string>
<string name="vpn_lockdown_config" msgid="8331697329868252169">"‏تغییر شبکه یا تنظیمات VPN"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"تلویزیون"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"تلفن"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"بلندگوهای جایگاه"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"دستگاه خارجی"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"هدفون‌ها"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"سیستم"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 8798ffcbc76e..5988783772f1 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Käytä näytön lukitusta"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Jatka lisäämällä näytön lukituksen avaustapa"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Paina anturia voimakkaasti"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Sormenjäljen prosessointi epäonnistui. Yritä uudelleen."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Sormenjälkeä ei voi tunnistaa. Yritä uudelleen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Puhdista sormenjälkitunnistin ja yritä uudelleen"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Puhdista anturi ja yritä uudelleen"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Paina anturia voimakkaasti"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Liikutit sormea liian hitaasti. Yritä uudelleen."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Kokeile toista sormenjälkeä"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Liian kirkas"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Virtapainikkeen painaminen havaittu"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Kokeile muuttaa asentoa"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Liikuta sormeasi hieman joka kerralla"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Kasvot tunnistettu, valitse Vahvista"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Sormenjälkilaitteisto ei ole käytettävissä."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Sormenjälkeä ei voi ottaa käyttöön"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Sormenjälkitunnistimen toiminta aikakatkaistiin. Yritä uudelleen."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Sormenjäljen käyttöönotto aikakatkaistu. Yritä uudelleen."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Sormenjälkitoiminto peruutettiin."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Käyttäjä peruutti sormenjälkitoiminnon."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Liian monta yritystä. Yritä myöhemmin uudelleen."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Liian monta yritystä. Käytä näytön lukituksen avaustapaa."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Liian monta yritystä. Käytä näytön lukituksen avaustapaa."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Yritä uudelleen."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Sormenjälkeä ei voida käsitellä. Yritä uudelleen."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Sormenjälkiä ei ole otettu käyttöön."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Laitteessa ei ole sormenjälkitunnistinta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tunnistin poistettu väliaikaisesti käytöstä."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Ota yhteys korjauspalveluun."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Kasvomallia ei voi luoda. Yritä uudelleen."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Liian kirkasta. Kokeile pehmeämpää valaistusta."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Ei tarpeeksi valoa"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Vie puhelin kauemmas"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Tuo puhelin lähemmäs"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Siirrä puhelinta ylemmäs"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Käynnistetään sovelluksia."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Viimeistellään päivitystä."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Painoit virtapainiketta, mikä yleensä sammuttaa näytön.\n\nKosketa painiketta kevyesti tallentaessasi sormenjälkeä."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Sammuta näyttö napauttamalla"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Sammuta näyttö"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Lopeta käyttöönotto sammuttamalla näyttö"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Laita pois päältä"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Jatketaanko sormenjäljen vahvistamista?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Painoit virtapainiketta, mikä yleensä sammuttaa näytön.\n\nVahvista sormenjälki koskettamalla painiketta kevyesti."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Sammuta näyttö"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Puhelin"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Telineen kaiuttimet"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Ulkoinen laite"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Kuulokkeet"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Järjestelmä"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 87861aecf423..75ec39a65f21 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Utiliser le verrouillage de l\'écran"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Entrez votre verrouillage d\'écran pour continuer"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Appuyez fermement sur le capteur"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Impossible de reconnaître l\'empreinte digitale. Veuillez réessayer."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Empreinte digitale non reconnue. Réessayez."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Nettoyez le capteur d\'empreintes digitales et réessayez"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Nettoyez le capteur et réessayez"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Appuyez fermement sur le capteur"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Essayez une autre empreinte digitale"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Trop lumineux"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Forte pression détectée"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Essayez de l\'ajuster"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Modifiez légèrement la position de votre doigt chaque fois"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur le bouton Confirmer"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Lecteur d\'empreintes digitales indisponible."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossible de configurer l\'empreinte digitale"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Le temps attribué pour lire l\'empreinte digitale est écoulé. Veuillez réessayer."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Délai dépassé pour configurer l\'empreinte digitale. Réessayez."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale numérique annulée."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"L\'opération d\'empreinte digitale a été annulée par l\'utilisateur."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Trop de tentatives. Veuillez réessayer plus tard."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Réessayer."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Empreinte digitale non traitable. Réessayez."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Cet appareil ne possède pas de capteur d\'empreintes digitales."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Le capteur a été désactivé temporairement."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Consultez un fournisseur de services de réparation."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Impossible de créer votre modèle facial. Réessayez."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Trop lumineux. Essayez un éclairage plus faible."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Éclairage insuffisant"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Éloignez le téléphone"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Rapprochez le téléphone"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Tenez le téléphone plus haut"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Lancement des applications…"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalisation de la mise à jour."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Vous avez appuyé sur le l\'interrupteur – cette action éteint habituellement l\'écran.\n\nEssayez de toucher légèrement pendant la configuration de votre empreinte digitale."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Toucher pour éteindre l\'écran"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Éteindre l\'écran"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Pour terminer, éteignez l\'écran"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Désactiver"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Poursuivre vérifica. empreinte digitale?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Vous avez appuyé sur le l\'interrupteur – cette action éteint habituellement l\'écran.\n\nEssayez de toucher légèrement pour vérifier votre empreinte digitale."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Éteindre l\'écran"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Télévision"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Téléphone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Haut-parleurs de la station d\'accueil"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Appareil externe"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Oreillettes"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Système"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index a9dccbc0c264..427ae2d7e763 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Utiliser verrouillage écran"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Utilisez le verrouillage de l\'écran pour continuer"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Appuyez bien sur le lecteur"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Impossible de reconnaître l\'empreinte digitale. Veuillez réessayer."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Impossible de reconnaître l\'empreinte digitale. Réessayez."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Nettoyez le lecteur d\'empreinte digitale et réessayez"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Nettoyez le lecteur et réessayez"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Appuyez bien sur le lecteur"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Vous avez déplacé votre doigt trop lentement. Veuillez réessayer."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Essayez une autre empreinte"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Trop de lumière"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Utilisation du bouton Marche/Arrêt détectée"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Essayez de repositionner le doigt"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Changez légèrement de position à chaque fois"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Visage authentifié, veuillez appuyer sur \"Confirmer\""</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Matériel d\'empreinte digitale indisponible."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossible de configurer l\'empreinte"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Délai de détection de l\'empreinte digitale expiré. Veuillez réessayer."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Délai de configuration de l\'empreinte dépassé. Réessayez."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Opération d\'empreinte digitale annulée."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Opération d\'authentification par empreinte digitale annulée par l\'utilisateur."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Trop de tentatives. Veuillez réessayer plus tard."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Trop de tentatives. Utilisez plutôt le verrouillage de l\'écran."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Veuillez réessayer."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Impossible de reconnaître l\'empreinte digitale. Réessayez."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Aucune empreinte digitale enregistrée."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Aucun lecteur d\'empreinte digitale n\'est installé sur cet appareil."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Capteur temporairement désactivé."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Contactez un réparateur."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Impossible de créer l\'empreinte faciale. Réessayez."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Trop lumineux. Essayez de baisser la lumière."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Lumière insuffisante"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Éloignez le téléphone."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Rapprochez le téléphone"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Déplacez le téléphone vers le haut"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Lancement des applications…"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Finalisation de la mise à jour."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Vous avez appuyé sur le bouton Marche/Arrêt, ce qui éteint généralement l\'écran.\n\nEssayez d\'appuyer doucement pendant la configuration de votre empreinte digitale."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Appuyer pour éteindre l\'écran"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Éteindre l\'écran"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Éteindre l\'écran pour achever la config."</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Éteindre"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuer de valider votre empreinte ?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Vous avez appuyé sur le bouton Marche/Arrêt, ce qui éteint généralement l\'écran.\n\nPour valider votre empreinte digitale, appuyez plus doucement."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Éteindre l\'écran"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Téléviseur"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Téléphone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Haut-parleurs de la station d\'accueil"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Appareil externe"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Écouteurs"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Système"</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index ae29e50eb4ad..f99b755bb2b9 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar credencial do dispositivo"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Desbloquea a pantalla para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Preme o sensor con firmeza"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Non se puido procesar a impresión dixital. Téntao de novo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Non se puido recoñecer a impresión dixital. Téntao de novo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpa o sensor de impresión dixital e téntao de novo"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpa o sensor e téntao de novo"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Preme o sensor con firmeza"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"O dedo moveuse demasiado lento. Téntao de novo."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Proba con outra impresión dixital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Hai demasiada luz"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Premeuse o botón de acendido"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Proba a axustar a impresión dixital"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia lixeiramente a posición do dedo en cada intento"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Autenticouse a cara, preme Confirmar"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impresión dixital non dispoñible."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Non se puido configurar a impresión dixital"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Esgotouse o tempo de espera da impresión dixital. Téntao de novo."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Esgotouse o tempo para configurar a impresión dixital Téntao de novo."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Cancelouse a operación da impresión dixital."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"O usuario cancelou a operación da impresión dixital."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiados intentos. Téntao de novo máis tarde."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Houbo demasiados intentos. Mellor usa o bloqueo de pantalla."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiados intentos. Mellor usa o bloqueo de pantalla."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Téntao de novo."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Non se pode procesar a impresión dixital Téntao de novo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Non se rexistraron impresións dixitais."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo non ten sensor de impresión dixital."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Desactivouse o sensor temporalmente."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visita un provedor de reparacións."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Non se puido crear o modelo facial. Téntao de novo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Hai demasiada iluminación. Proba cunha máis suave."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Non hai luz suficiente"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Afasta o teléfono"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Achega o teléfono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Sube o teléfono"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando aplicacións."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Está finalizando o arranque"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiches o botón de acendido, o que adoita facer que se apague a pantalla.\n\nProba a dar un toque suave namentres configuras a impresión dixital."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Toca para desactivar a pantalla"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Desactivar pantalla"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Apaga a pantalla e acaba a configuración"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desactivar"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Queres seguir verificando a impresión?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiches o botón de acendido, o que adoita facer que se apague a pantalla.\n\nProba a dar un toque suave para verificar a impresión dixital."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desactivar pantalla"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Televisión"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Teléfono"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Conectar altofalantes á base"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Dispositivo externo"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Auriculares"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistema"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 36812e81bb82..96b9e0fbcda1 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"સ્ક્રીન લૉકનો ઉપયોગ કરો"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"આગળ વધવા માટે તમારું સ્ક્રીન લૉક દાખલ કરો"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"સેન્સર પર જોરથી દબાવો"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ફિંગરપ્રિન્ટ પ્રક્રિયા કરી શકાઈ નથી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"ફિંગરપ્રિન્ટ ઓળખી શકતા નથી. ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ફિંગરપ્રિન્ટ સેન્સર સાફ કરો અને ફરી પ્રયાસ કરો"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"સેન્સર સાફ કરો અને ફરી પ્રયાસ કરો"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"સેન્સર પર જોરથી દબાવો"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"આંગળી બહુ જ ધીમેથી ખસેડી. કૃપા કરીને ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"અન્ય ફિંગરપ્રિન્ટ અજમાવી જુઓ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"અતિશય પ્રકાશિત"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"પાવર બટન દબાવ્યું હોવાની જાણ થઈ છે"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ગોઠવણી કરી જુઓ"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"દરેક વખતે સ્કૅનર પર તમારી આંગળીની સ્થિતિ સહેજ બદલતા રહો"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ચહેરા પ્રમાણિત, કૃપા કરીને કન્ફર્મ કરો"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ફિંગરપ્રિન્ટ હાર્ડવેર ઉપલબ્ધ નથી."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ફિંગરપ્રિન્ટનું સેટઅપ કરી શકતા નથી"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"ફિંગરપ્રિન્ટનો સમય બાહ્ય થયો. ફરી પ્રયાસ કરો."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ફિંગરપ્રિન્ટનું સેટઅપ કરવાનો સમય સમાપ્ત થઈ ગયો. ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ફિંગરપ્રિન્ટ ઓપરેશન રદ કર્યું."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ફિંગરપ્રિન્ટ ચકાસવાની પ્રક્રિયા વપરાશકર્તાએ રદ કરી."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"ઘણા બધા પ્રયત્નો. પછીથી ફરી પ્રયાસ કરો."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ઘણા બધા પ્રયાસો. તેને બદલે સ્ક્રીન લૉકનો ઉપયોગ કરો."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ઘણા બધા પ્રયાસો. વિકલ્પ તરીકે સ્ક્રીન લૉકનો ઉપયોગ કરો."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ફરી પ્રયાસ કરો."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ફિંગરપ્રિન્ટની પ્રક્રિયા કરી શકતા નથી. ફરી પ્રયાસ કરો."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"કોઈ ફિંગરપ્રિન્ટની નોંધણી કરવામાં આવી નથી."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"આ ડિવાઇસમાં કોઈ ફિંગરપ્રિન્ટ સેન્સર નથી."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"સેન્સર હંગામી રૂપે બંધ કર્યું છે."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"રિપેર કરવાની સેવા આપતા પ્રદાતાની મુલાકાત લો."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"તમારા ચહેરાનું મૉડલ ન બનાવી શકાય. ફરી પ્રયાસ કરો."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"અતિશય પ્રકાશિત. થોડો હળવો પ્રકાશ અજમાવી જુઓ."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"પૂરતો પ્રકાશ નથી"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ફોનને વધુ દૂર લઈ જાઓ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ફોનને વધુ નજીક લાવો"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ફોનને વધુ ઊંચે લઈ જાઓ"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ઍપ્લિકેશનો શરૂ કરી રહ્યાં છે."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"બૂટ સમાપ્ત કરી રહ્યાં છે."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"તમે પાવર બટન દબાવ્યું છે — જેનાથી સામાન્ય રીતે સ્ક્રીન બંધ થઈ જાય છે.\n\nતમારી ફિંગરપ્રિન્ટનું સેટઅપ કરતી વખતે હળવેથી ટૅપ કરવાનો પ્રયાસ કરો."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"સ્ક્રીન બંધ કરવા માટે ટૅપ કરો"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"સ્ક્રીન બંધ કરો"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"સેટઅપ સમાપ્ત કરવા માટે, સ્ક્રીન બંધ કરો"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"બંધ કરો"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"શું તમારી ફિંગરપ્રિન્ટની ચકાસણી કરીએ?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"તમે પાવર બટન દબાવ્યું છે — જેનાથી સામાન્ય રીતે સ્ક્રીન બંધ થઈ જાય છે.\n\nતમારી ફિંગરપ્રિન્ટની ચકાસણી કરવા માટે, તેને હળવેથી ટૅપ કરવાનો પ્રયાસ કરો."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"સ્ક્રીન બંધ કરો"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ફોન"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"સ્પીકર્સ ડૉક કરો"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"બહારનું ડિવાઇસ"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"હેડફોન"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"સિસ્ટમ"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 2a9a44a1a12a..4e05cd493818 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"स्क्रीन लॉक का क्रेडेंशियल इस्तेमाल करें"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"जारी रखने के लिए, अपने स्क्रीन लॉक की पुष्टि करें"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"सेंसर को उंगली से ज़ोर से दबाएं"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"फ़िंगरप्रिंट प्रोसेस नहीं हो सका. कृपया दोबारा कोशिश करें."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"फ़िंगरप्रिंट की पहचान नहीं की जा सकी. फिर से कोशिश करें."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"फ़िंगरप्रिंट सेंसर को साफ़ करके फिर से कोशिश करें"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"फ़िंगरप्रिंट सेंसर को साफ़ करके फिर से कोशिश करें"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"सेंसर को उंगली से ज़ोर से दबाएं"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"उंगली बहुत धीरे चलाई गई. कृपया फिर से कोशिश करें."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"किसी दूसरे फ़िंगरप्रिंट से कोशिश करें"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"बहुत रोशनी है"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"पावर बटन दबाया गया"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"सेंसर पर सही तरीके से उंगली लगाने की कोशिश करें"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"फ़िंगरप्रिंट सेट अप करते समय, अपनी उंगली को हर बार थोड़ी अलग स्थिति में रखें"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"फ़िंगरप्रिंट हार्डवेयर उपलब्ध नहीं है."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"फ़िंगरप्रिंट सेट अप नहीं किया जा सका"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"फ़िंगरप्रिंट का समय खत्म हो गया. फिर से कोशिश करें."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"फ़िंगरप्रिंट सेटअप करने का समय खत्म हो गया. फिर से कोशिश करें."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"फ़िंगरप्रिंट ऑपरेशन रोक दिया गया."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"उपयोगकर्ता ने फिंगरप्रिंट की पुष्टि की कार्रवाई रद्द कर दी है."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"बहुत ज़्यादा प्रयास कर लिए गए हैं. बाद में फिर से प्रयास करें."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"कई बार कोशिश की जा चुकी है. इसके बजाय, स्क्रीन लॉक का इस्तेमाल करें."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"इससे ज़्यादा बार कोशिश नहीं की जा सकती. इसके बजाय, स्क्रीन लॉक का इस्तेमाल करें."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"फिर से कोशिश करें."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फ़िंगरप्रिंट की पहचान नहीं की जा सकी. फिर से कोशिश करें."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोई फ़िंगरप्रिंट रजिस्टर नहीं किया गया है."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"इस डिवाइस में फ़िंगरप्रिंट सेंसर नहीं है."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेंसर कुछ समय के लिए बंद कर दिया गया है."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"फ़िंगरप्रिंट सेंसर को रिपेयर करने की सेवा देने वाली कंपनी से संपर्क करें."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"चेहरे का माॅडल नहीं बन सका. फिर से कोशिश करें."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"बहुत रोशनी है. हल्की रोशनी आज़माएं."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"ज़रूरत के मुताबिक रोशनी नहीं है"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"फ़ोन को दूर ले जाएं"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"फ़ोन को नज़दीक लाएं"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"फ़ोन को थोड़ा और ऊपर ले जाएं"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ऐप्स प्रारंभ होने वाले हैं"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बूट खत्म हो रहा है."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"आपने पावर बटन दबाया - आम तौर पर, इससे स्क्रीन बंद हो जाती है.\n\nअपना फ़िंगरप्रिंट सेट अप करते समय, बटन को हल्के से टैप करके देखें."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"स्क्रीन बंद करने के लिए टैप करें"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"स्क्रीन बंद करें"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"सेटअप पूरा होने पर, स्क्रीन बंद करें"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"बंद करें"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"फ़िंगरप्रिंट की पुष्टि करना जारी रखना है?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"आपने पावर बटन दबाया - आम तौर पर, इससे स्क्रीन बंद हो जाती है.\n\nअपने फ़िंगरप्रिंट की पुष्टि करने के लिए, बटन पर हल्के से टैप करके देखें."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"स्क्रीन बंद करें"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"टीवी"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"फ़ोन"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"डॉक स्‍पीकर"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"बाहरी डिवाइस"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"हेडफ़ोन"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"यूएसबी"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"सिस्‍टम"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 0a2702a256b9..0a557d6bb107 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Upotreba zaključavanja zaslona"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Upotrijebite zaključavanje zaslona da biste nastavili"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Čvrsto pritisnite senzor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Prepoznavanje otiska prsta nije uspjelo. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Očistite senzor otiska prsta i pokušajte ponovno"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Očistite senzor i pokušajte ponovno"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Čvrsto pritisnite senzor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Presporo pomicanje prsta. Pokušajte ponovo."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Isprobajte drugi otisak prsta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Presvijetlo"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Detektiran je pritisak na tipku za uključivanje"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Pokušajte ga prilagoditi"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Svaki put lagano promijenite položaj prsta"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Lice je autentificirano, pritisnite Potvrdi"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardver za otisak prsta nije dostupan."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Postavljanje otiska prsta nije uspjelo"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Isteklo je vrijeme čekanja za otisak prsta. Pokušajte ponovo."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Vrijeme za postavljanje otiska prsta je isteklo. Pokušajte ponovo."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Radnja otiska prsta otkazana je."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Radnju s otiskom prsta otkazao je korisnik."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Previše pokušaja. Pokušajte ponovo kasnije."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Previše pokušaja. Umjesto toga upotrijebite zaključavanje zaslona."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Previše pokušaja. Umjesto toga upotrijebite zaključavanje zaslona."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Pokušajte ponovo."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Obrada otiska prsta nije uspjela. Pokušajte ponovo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nije registriran nijedan otisak prsta."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ovaj uređaj nema senzor otiska prsta."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je privremeno onemogućen."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Posjetite davatelja usluga popravaka."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Izrada modela lica nije uspjela. Pokušajte ponovo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Presvijetlo je. Pokušajte sa slabijim svjetlom."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nema dovoljno svjetla"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Udaljite telefon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Približite telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Pomaknite telefon prema gore"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Pokretanje aplikacija."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Završetak inicijalizacije."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnuli ste tipku za uključivanje/isključivanje, čime se obično isključuje zaslon.\n\nPokušajte lagano dodirnuti dok postavljate svoj otisak prsta."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Dodirnite da biste isključili zaslon"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Isključi zaslon"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Za prekid postavljanja isključite zaslon"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Isključi"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Nastaviti s potvrđivanjem otiska prsta?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnuli ste tipku za uključivanje/isključivanje, čime se obično isključuje zaslon.\n\nPokušajte lagano dodirnuti da biste potvrdili svoj otisak prsta."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Isključi zaslon"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Televizor"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Zvučnici postolja"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Vanjski uređaj"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Slušalice"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sustav"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 01b6f90aa676..f1388f6739d8 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Képernyőzár használata"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"A folytatáshoz adja meg a képernyőzár hitelesítési adatait"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Nyomja meg határozottan az érzékelőt"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Az ujjlenyomat nem ismerhető fel. Próbálkozzon újra."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Tisztítsa meg az ujjlenyomat-érzékelőt, majd próbálja újra"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Tisztítsa meg az érzékelőt, majd próbálja újra"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Nyomja meg határozottan az érzékelőt"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Túl lassan húzta az ujját. Próbálkozzon újra."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Próbálkozzon másik ujjlenyomattal"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Túl világos"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Bekapcsológomb lenyomása észlelve"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Próbálja beállítani"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Módosítsa minden alkalommal kis mértékben ujja helyzetét."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Arc hitelesítve; nyomja meg a Megerősítés lehetőséget"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Az ujjlenyomathoz szükséges hardver nem érhető el."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nem sikerült beállítani az ujjlenyomatot"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Az ujjlenyomat-beolvasási műveletkor időtúllépés történt. Próbálkozzon újra."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Lejárt az ujjlenyomat-beállítás időkorlátja. Próbálkozzon újra."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Ujjlenyomattal kapcsolatos művelet megszakítva"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Az ujjlenyomattal kapcsolatos műveletet a felhasználó megszakította."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Túl sok próbálkozás. Próbálja újra később."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Túl sokszor próbálkozott. Használja inkább a képernyőzárat."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Túl sok próbálkozás. Használja inkább a képernyőzárat."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Próbálkozzon újra."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nem sikerült feldolgozni az ujjlenyomatot. Próbálkozzon újra."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nincsenek regisztrált ujjlenyomatok."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ez az eszköz nem rendelkezik ujjlenyomat-érzékelővel."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Az érzékelő átmenetileg le van tiltva."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Keresse fel a szervizt."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nem lehet létrehozni az arcmodellt. Próbálja újra."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Túl világos. Próbálja kevésbé erős világítással."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nincs elég fény"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Tartsa távolabb a telefont"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Tartsa közelebb a telefont"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Emelje magasabbra a telefont"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Kezdő alkalmazások."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Rendszerindítás befejezése."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Megnyomta a bekapcsológombot — ezzel általában kikapcsol a képernyő.\n\nPróbáljon finoman rákoppintani az ujjlenyomat beállítása közben."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Koppintson a képernyő kikapcsolásához"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Képernyő kikapcsolása"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"A képernyő kikapcsolása befejezi a beállítást"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Kikapcsolás"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Folytatja az ujjlenyomat ellenőrzését?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Megnyomta a bekapcsológombot — ezzel általában kikapcsol a képernyő.\n\nPróbáljon finoman rákoppintani az ujjlenyomat ellenőrzéséhez."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Kikapcsolom"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dokkolóegység hangszórója"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Külső eszköz"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Fejhallgató"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Rendszer"</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 86560bef7f76..14fb55c020c6 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Էկրանի կողպում"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Շարունակելու համար ապակողպեք էկրանը"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Մատը ուժեղ սեղմեք սկաների վրա"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Չհաջողվեց մշակել մատնահետքը: Նորից փորձեք:"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Մատնահետքը չի հաջողվում ճանաչել։ Նորից փորձեք։"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Մաքրեք մատնահետքերի սկաները և նորից փորձեք"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Մաքրեք սկաները և նորից փորձեք"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Մատը ուժեղ սեղմեք սկաների վրա"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Շատ դանդաղ անցկացրիք մատը: Փորձեք նորից:"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Փորձեք մեկ այլ մատնահետք"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Շատ լուսավոր է"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Հայտնաբերվել է սնուցման կոճակի սեղմում"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Փորձեք փոխել մատի դիրքը"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ամեն անգամ թեթևակի փոխեք մատի դիրքը"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Դեմքը ճանաչվեց: Սեղմեք «Հաստատել»:"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Մատնահետքի սարքն անհասանելի է:"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Հնարավոր չէ կարգավորել մատնահետքը"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Մատնահետքի գրանցման ժամանակը սպառվել է: Փորձեք նորից:"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Մատնահետքի կարգավորման ժամանակը սպառվել է։ Նորից փորձեք։"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Իսկորոշումը մատնահետքի միջոցով չեղարկվեց:"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Մատնահետքով նույնականացման գործողությունը չեղարկվել է օգտատիրոջ կողմից:"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Չափից շատ փորձ եք կատարել: Փորձեք նորից քիչ հետո:"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Չափազանց շատ փորձեր են արվել։ Օգտագործեք էկրանի կողպումը։"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Փորձեք նորից:"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Չի հաջողվում մշակել մատնահետքը։ Նորից փորձեք։"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Գրանցված մատնահետք չկա:"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Այս սարքը չունի մատնահետքերի սկաներ։"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Տվիչը ժամանակավորապես անջատված է:"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Այցելեք սպասարկման կենտրոն։"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Չհաջողվեց ստեղծել ձեր դեմքի մոդելը։ Նորից փորձեք։"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Շատ լուսավոր է։ Փորձեք ավելի թեթև լուսավորություն։"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Թույլ լուսավորություն"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Փոքր-ինչ հեռու պահեք հեռախոսը"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Մոտեցրեք հեռախոսը"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Բարձրացրեք հեռախոսը"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Հավելվածները մեկնարկում են:"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Բեռնումն ավարտվում է:"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Դուք սեղմել եք սնուցման կոճակը։ Սովորաբար դրա արդյունքում էկրանն անջատվում է։\n\nՄատնահետքը ավելացնելու համար թեթևակի հպեք կոճակին։"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Հպեք՝ էկրանն անջատելու համար"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Անջատել էկրանը"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Ավարտեք կարգավորումը՝ անջատելով էկրանը"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Անջատել"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Շարունակե՞լ մատնահետքի սկանավորումը"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Դուք սեղմել եք սնուցման կոճակը։ Սովորաբար դրա արդյունքում էկրանն անջատվում է։\n\nՄատնահետքը սկանավորելու համար թեթևակի հպեք կոճակին։"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Անջատել էկրանը"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Հեռուստացույց"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Հեռախոս"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Դոկ-կայանի բարձրախոսներ"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Արտաքին սարք"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Ականջակալներ"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Համակարգ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b145e200c54a..58d91c80ff87 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Gunakan kunci layar"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Masukkan kunci layar untuk melanjutkan"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Tekan sensor dengan kuat"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Tidak dapat memproses sidik jari. Coba lagi."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Tidak dapat mengenali sidik jari. Coba lagi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Bersihkan sensor sidik jari lalu coba lagi"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Bersihkan sensor lalu coba lagi"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Tekan sensor dengan kuat"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Jari digerakkan terlalu lambat. Coba lagi."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Coba sidik jari lain"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Terlalu terang"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Penekanan tombol daya terdeteksi"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Coba sesuaikan"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ubah sedikit posisi jari di setiap percobaan"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah diautentikasi, silakan tekan konfirmasi"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware sidik jari tidak tersedia."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Tidak dapat menyiapkan sidik jari"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Waktu sidik jari habis. Coba lagi."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Waktu penyiapan sidik jari habis. Coba lagi."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operasi sidik jari dibatalkan."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operasi sidik jari dibatalkan oleh pengguna."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Terlalu banyak upaya. Coba lagi nanti."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Terlalu banyak upaya gagal. Gunakan kunci layar."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Terlalu banyak upaya gagal. Gunakan kunci layar."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Coba lagi."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Tidak dapat memproses sidik jari. Coba lagi."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tidak ada sidik jari yang terdaftar."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Perangkat ini tidak memiliki sensor sidik jari."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor dinonaktifkan untuk sementara."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Kunjungi penyedia reparasi."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Tidak dapat membuat model wajah Anda. Coba lagi."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Terlalu terang. Coba cahaya yang lebih lembut."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Cahaya tidak cukup"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Jauhkan ponsel"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Dekatkan ponsel"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Gerakkan ponsel ke atas"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Memulai aplikasi."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Menyelesaikan boot."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Anda menekan tombol daya; tindakan ini biasanya akan menonaktifkan layar.\n\nCoba ketuk lembut saat menyiapkan sidik jari Anda."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Ketuk untuk menonaktifkan layar"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Nonaktifkan layar"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Untuk mengakhiri penyiapan, nonaktifkan layar"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Nonaktifkan"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Lanjutkan verifikasi sidik jari?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Anda menekan tombol daya; tindakan ini biasanya akan menonaktifkan layar.\n\nCoba ketuk lembut untuk memverifikasi sidik jari Anda."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Nonaktifkan layar"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Ponsel"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Pengeras suara dok"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Perangkat Eksternal"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Headphone"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistem"</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 6f48de292a26..7466295729d1 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Nota skjálás"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Sláðu inn skjálásinn þinn til að halda áfram"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Ýttu ákveðið á lesarann"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Ekki var hægt að vinna úr fingrafarinu. Reyndu aftur."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Fingrafar þekkist ekki. Reyndu aftur."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Hreinsaðu fingrafaralesarann og reyndu aftur"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Hreinsaðu lesarann og reyndu aftur"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Ýttu ákveðið á lesarann"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Fingurinn hreyfðist of hægt. Reyndu aftur."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prófaðu annað fingrafar"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Of bjart"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Þrýstingur á aflrofa greindist"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prófaðu að breyta stöðu fingursins"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Breyttu stöðu fingursins örlítið í hvert skipti"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Andlit staðfest, ýttu til að staðfesta"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Fingrafarsvélbúnaður ekki til staðar."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ekki er hægt að setja upp fingrafar"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Tímamörk runnu út fyrir fingrafar. Reyndu aftur."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingrafarsuppsetning rann út á tíma. Reyndu aftur."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Hætt við fingrafarsaðgerð."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Notandi hætti við að nota fingrafar."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Of margar tilraunir. Reyndu aftur síðar."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Of margar tilraunir. Notaðu skjálás í staðinn."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Of margar tilraunir. Notaðu skjálás í staðinn."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Reyndu aftur."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ekki tekst að vinna úr fingrafari. Reyndu aftur."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Engin fingraför hafa verið skráð."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Þetta tæki er ekki með fingrafaralesara."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Slökkt tímabundið á skynjara."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Þú verður að fara á verkstæði."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Ekki tekst að búa til andlitslíkan. Reyndu aftur."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Of bjart. Prófaðu mýkri lýsingu."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Of lítið ljós"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Færðu símann lengra frá"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Færðu símann nær"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Færðu símann hærra"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Ræsir forrit."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Lýkur ræsingu."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Þú ýttir á aflrofann. Yfirleitt slekkur það á skjánum.\n\nPrófaðu að ýta laust þegar þú setur upp fingrafarið."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Ýttu til að slökkva á skjá"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Slökkva á skjá"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Slökktu á skjá til að ljúka uppsetningu"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Slökkva"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Halda áfram að staðfesta fingrafarið?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Þú ýttir á aflrofann. Yfirleitt slekkur það á skjánum.\n\nPrófaðu að ýta laust til að staðfesta fingrafarið þitt."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Slökkva á skjá"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Sjónvarp"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Sími"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dokkuhátalarar"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Ytra tæki"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Heyrnartól"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Kerfi"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index fb7763a12994..3484165405a4 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usa il blocco schermo"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Inserisci il blocco schermo per continuare"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Premi con decisione sul sensore"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Impossibile elaborare l\'impronta. Riprova."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Impossibile riconoscere l\'impronta. Riprova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Pulisci il sensore di impronte digitali e riprova"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Pulisci il sensore e riprova"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Premi con decisione sul sensore"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Movimento del dito troppo lento. Riprova."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prova con un\'altra impronta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Troppa luce"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Rilevata pressione tasto di accensione"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prova a regolare"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Cambia leggermente la posizione del dito ogni volta"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Volto autenticato, premi Conferma"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware per l\'impronta non disponibile."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Impossibile configurare l\'impronta"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Timeout impronta. Riprova."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Timeout configurazione impronta. Riprova."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operazione associata all\'impronta annullata."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operazione di autenticazione dell\'impronta annullata dall\'utente."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Troppi tentativi. Riprova più tardi."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Troppi tentativi. Usa il blocco schermo."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Troppi tentativi. Usa il blocco schermo."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Riprova."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Impossibile elaborare l\'impronta. Riprova."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nessuna impronta digitale registrata."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Questo dispositivo non dispone di sensore di impronte."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensore temporaneamente disattivato."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Contatta un fornitore di servizi di riparazione."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Impossibile creare il modello del volto. Riprova."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Troppa luce. Prova con una luce più soft."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Luce insufficiente"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Allontana il telefono"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Avvicina il telefono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Sposta il telefono più in alto"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Avvio app."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Conclusione dell\'avvio."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Hai premuto il tasto di accensione; in genere questa azione disattiva lo schermo.\n\nProva a toccare leggermente il tasto di accensione durante la configurazione della tua impronta."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tocca per disattivare lo schermo"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Disattiva lo schermo"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Per terminare, disattiva lo schermo"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Disattiva"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vuoi continuare a verificare l\'impronta?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Hai premuto il tasto di accensione; in genere questa azione disattiva lo schermo.\n\nProva a toccare leggermente il tasto di accensione per verificare la tua impronta."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Disattiva lo schermo"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefono"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Altoparlanti dock"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Dispositivo esterno"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Cuffie audio"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistema"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 7a460a334b68..0c62ea211595 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -585,13 +585,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"שימוש בנעילת מסך"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"יש לבטל את נעילת המסך כדי להמשיך"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"לוחצים לחיצה חזקה על החיישן"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"לא ניתן היה לעבד את טביעת האצבע. אפשר לנסות שוב."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"לא ניתן לזהות את טביעת האצבע. יש לנסות שוב."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"עליך לנקות את חיישן טביעות האצבע ולנסות שוב"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"עליך לנקות את החיישן ולנסות שוב"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"לוחצים לחיצה חזקה על החיישן"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"הזזת את האצבע לאט מדי. יש לנסות שוב."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"יש להשתמש בטביעת אצבע אחרת"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"בהיר מדי"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"זוהתה לחיצה על לחצן ההפעלה"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"יש לנסות ולשנות את תנוחת האצבע"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"צריך לשנות מעט את תנוחת האצבע בכל פעם"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -603,12 +604,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"זיהוי הפנים בוצע. יש ללחוץ על אישור"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"החומרה לזיהוי טביעות אצבעות אינה זמינה."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"לא ניתן להגדיר טביעת אצבע"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"נגמר הזמן הקצוב לטביעת אצבע. אפשר לנסות שוב."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"הזמן שהוקצב להגדרה של טביעת האצבע פג. יש לנסות שוב."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"פעולת טביעת האצבע בוטלה."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"פעולת טביעת האצבע בוטלה על ידי המשתמש."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"יותר מדי ניסיונות. יש לנסות שוב מאוחר יותר."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"בוצעו יותר מדי ניסיונות. יש להשתמש בנעילת המסך במקום זאת."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"בוצעו יותר מדי ניסיונות. יש להשתמש בנעילת המסך במקום זאת."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"כדאי לנסות שוב."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"לא ניתן לעבד את טביעת האצבע. יש לנסות שוב."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"לא נסרקו טביעות אצבע."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"במכשיר הזה אין חיישן טביעות אצבע."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"החיישן מושבת באופן זמני."</string>
@@ -636,8 +637,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"צריך ליצור קשר עם ספק תיקונים."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"לא ניתן ליצור את התבנית לזיהוי הפנים. יש לנסות שוב."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"בהירה מדי. צריך תאורה עדינה יותר."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"אין מספיק אור"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"צריך להרחיק את הטלפון"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"צריך לקרב את הטלפון"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"צריך להגביה את הטלפון"</string>
@@ -1251,8 +1251,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"מתבצעת הפעלה של אפליקציות."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"תהליך האתחול בשלבי סיום."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"לחצת על לחצן ההפעלה – בדרך כלל הפעולה הזו מכבה את המסך.\n\nעליך לנסות להקיש בעדינות במהלך ההגדרה של טביעת האצבע שלך."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"יש להקיש כדי לכבות את המסך"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"כיבוי המסך"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"כדי לסיים את ההגדרה צריך לכבות את המסך"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"השבתה"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"להמשיך לאמת את טביעת האצבע שלך?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"לחצת על לחצן ההפעלה – בדרך כלל הפעולה הזו מכבה את המסך.\n\nעליך לנסות להקיש בעדינות כדי לאמת את טביעת האצבע שלך."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"כיבוי המסך"</string>
@@ -1613,7 +1613,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"טלוויזיה"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"טלפון"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"רמקולים של מעגן"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"מכשיר חיצוני"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"אוזניות"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"מערכת"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index cf8521f84b7b..94c6bb00f2fa 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"画面ロックの使用"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"続行するには画面ロックを入力してください"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"センサーにしっかりと押し当ててください"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"指紋を処理できませんでした。もう一度お試しください。"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"指紋を認識できません。もう一度お試しください。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"指紋認証センサーの汚れを取り除いて、もう一度お試しください"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"センサーの汚れを取り除いて、もう一度お試しください"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"センサーにしっかりと押し当ててください"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"指の動きが遅すぎました。もう一度お試しください。"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"別の指紋をお試しください"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"明るすぎます"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"電源ボタンが押されました"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"調整してみてください"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"毎回、指を置く位置を少し変えてください"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"顔を認証しました。[確認] を押してください"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指紋認証ハードウェアは使用できません。"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"指紋を設定できません"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"指紋の読み取りがタイムアウトになりました。もう一度お試しください。"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋の設定がタイムアウトしました。もう一度お試しください。"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋の操作をキャンセルしました。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"指紋の操作がユーザーによりキャンセルされました。"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"所定の回数以上間違えました。しばらくしてからもう一度お試しください。"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"もう一度お試しください。"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"指紋を処理できません。もう一度お試しください。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"指紋が登録されていません。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"このデバイスには指紋認証センサーがありません。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"センサーが一時的に無効になっています。"</string>
@@ -1248,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"アプリを起動しています。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ブートを終了しています。"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"電源ボタンを押しました。通常、この操作で画面が OFF になります。\n\n指紋を設定する場合は電源ボタンに軽く触れてみましょう。"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"タップして画面を OFF にする"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"画面を OFF にする"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"設定を終了するには画面を OFF にします"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"OFF にする"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"指紋の確認を続行しますか?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"電源ボタンを押しました。通常、この操作で画面が OFF になります。\n\n指紋を確認するには、電源ボタンに軽く触れてみましょう。"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"画面を OFF にする"</string>
@@ -1610,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"テレビ"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"モバイル デバイス"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ホルダーのスピーカー"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"外部デバイス"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ヘッドホン"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"システム"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index ae14cc9ac6ae..576c61e28f8a 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"გამოიყენეთ ეკრანის დაბლოკვა"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"გასაგრძელებლად შედით ეკრანის დაბლოკვაში"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"მაგრად დააჭირეთ სენსორს"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"თითის ანაბეჭდის დამუშავება ვერ მოხერხდა. გთხოვთ, ცადოთ ხელახლა."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"თითის ანაბეჭდის ამოცნობა ვერ ხერხდება. ცადეთ ხელახლა."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"გაწმინდეთ თითის ანაბეჭდის სენსორი და ხელახლა ცადეთ"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"გაწმინდეთ სენსორი და ხელახლა ცადეთ"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"მაგრად დააჭირეთ სენსორს"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"თითის აღება მეტისმეტად ნელა მოხდა. გთხოვთ, სცადოთ ხელახლა."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ცადეთ სხვა თითის ანაბეჭდი"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ზედმეტად ნათელია"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"აღმოჩენილია Ძლიერი დაჭერა"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ცადეთ დარეგულირება"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ოდნავ შეცვალეთ თითის დაჭერის ადგილი ყოველ ჯერზე"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"სახე ავტორიზებულია, დააჭირეთ დადასტურებას"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"თითის ანაბეჭდის აპარატურა არ არის ხელმისაწვდომი."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"თითის ანაბეჭდის დაყენება ვერ ხერხდება"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"თითის ანაბეჭდის ლოდინის დრო ამოიწურა. სცადეთ ხელახლა."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"თითის ანაბეჭდის დაყენების დრო ამოიწურა. ცადეთ ხელახლა."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"თითის ანაბეჭდის აღების ოპერაცია გაუქმდა."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"თითის ანაბეჭდის ოპერაცია გააუქმა მომხმარებელმა."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"ძალიან ბევრი მცდელობა იყო. სცადეთ მოგვიანებით."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"მეტისმეტად ბევრი მცდელობა იყო. სანაცვლოდ გამოიყენეთ ეკრანის დაბლოკვა."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"მეტისმეტად ბევრი მცდელობა იყო. სანაცვლოდ გამოიყენეთ ეკრანის დაბლოკვა."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ხელახლა სცადეთ"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"თითის ანაბეჭდის დამუშავება შეუძლებელია. ცადეთ ხელახლა."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"თითის ანაბეჭდები რეგისტრირებული არ არის."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ამ მოწყობილობას არ აქვს თითის ანაბეჭდის სენსორი."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"სენსორი დროებით გათიშულია."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ეწვიეთ შეკეთების სერვისის პროვაიდერს."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"თქვენი სახის მოდელი ვერ იქმნება. ცადეთ ხელახლა."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"მეტისმეტად ნათელია. ცადეთ უფრო სუსტი განათება."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"განათება არასაკმარისია"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"გაწიეთ ტელეფონი უფრო შორს"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"მიიტანეთ ტელეფონი უფრო ახლოს"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"აწიეთ ტელეფონი ზემოთ"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"აპების ჩართვა"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ჩატვირთვის დასასრული."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"თქვენ დააჭირეთ ჩართვის ღილაკს — ჩვეულებრივ, ის ეკრანს გამორთავს.\n\nთქვენი თითის ანაბეჭდის დაყენებისას ცადეთ მსუბუქად შეხება."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"შეეხეთ ეკრანის გამოსართავად"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"ეკრანის გამორთვა"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"დაყენების დასასრულებლად გამორთეთ ეკრანი"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"გამორთვა"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"გსურთ ანაბეჭდის დადასტურების გაგრძელება?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"თქვენ დააჭირეთ ჩართვის ღილაკს — ჩვეულებრივ, ის ეკრანს გამორთავს.\n\nთქვენი თითის ანაბეჭდის დასადასტურებლად ცადეთ მსუბუქად შეხება."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ეკრანის გამორთვა"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"ტელევიზია"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ტელეფონი"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"სპიკერების მიმაგრება"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"გარე მოწყობილობა"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ყურსასმენები"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"სისტემა"</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 54edba31e8da..cc393ab66533 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -72,7 +72,7 @@
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Қоңырау шалушының жеке анықтағышы бастапқы бойынша шектелмеген. Келесі қоңырау: Шектелмеген"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Қызмет ұсынылмаған."</string>
<string name="CLIRPermanent" msgid="166443681876381118">"Қоңырау шалушы идентификаторы параметрін өзгерту мүмкін емес."</string>
- <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобильдік деректер қызметі жоқ"</string>
+ <string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Мобильдік интернет қызметі жоқ"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Жедел қызметке қоңырау шалу қолжетімді емес"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Дауыстық қоңыраулар қызметі жоқ"</string>
<string name="RestrictedOnAllVoiceTitle" msgid="3982069078579103087">"Дауыс қызметі немесе жедел қызметке қоңырау шалу мүмкіндігі жоқ"</string>
@@ -85,7 +85,7 @@
<string name="notification_channel_network_alert" msgid="4788053066033851841">"Дабылдар"</string>
<string name="notification_channel_call_forward" msgid="8230490317314272406">"Қоңырауды басқа нөмірге бағыттау"</string>
<string name="notification_channel_emergency_callback" msgid="54074839059123159">"Шұғыл кері қоңырау шалу режимі"</string>
- <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Мобильдік деректер күйі"</string>
+ <string name="notification_channel_mobile_data_status" msgid="1941911162076442474">"Мобильдік интернет күйі"</string>
<string name="notification_channel_sms" msgid="1243384981025535724">"SMS хабарлары"</string>
<string name="notification_channel_voice_mail" msgid="8457433203106654172">"Дауыстық пошта хабарлары"</string>
<string name="notification_channel_wfc" msgid="9048240466765169038">"Wi-Fi қоңыраулары"</string>
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Экран құлпын пайдалану"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Жалғастыру үшін экран құлпын енгізіңіз."</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Сканерді қатты басыңыз."</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Саусақ ізін өңдеу мүмкін емес. Әрекетті қайталаңыз."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Саусақ ізін тану мүмкін емес. Қайталап көріңіз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Саусақ ізін оқу сканерін тазалап, әрекетті қайталаңыз."</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Сканерді тазалап, әрекетті қайталаңыз."</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Сканерді қатты басыңыз."</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Саусағыңызды тым баяу қозғалттыңыз. Әрекетті қайталап көріңіз."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Басқа саусақ ізін байқап көріңіз."</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Тым жарық."</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Қуат түймесін басу әрекеті анықталды."</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Дұрыстап қойып көріңіз."</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Саусағыңыздың қалпын аздап өзгертіп тұрыңыз."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,13 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Бет танылды, \"Растау\" түймесін басыңыз"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Саусақ ізі жабдығы қолжетімді емес."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Саусақ ізін орнату мүмкін емес."</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Саусақ ізін күту уақыты бітті. Әрекетті қайталаңыз."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Саусақ ізін реттеу уақыты өтіп кетті. Қайталап көріңіз."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Саусақ ізі операциясынан бас тартылған."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Пайдаланушы саусақ ізі операциясынан бас тартты."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Талпыныстар тым көп. Кейінірек қайталап көріңіз."</string>
- <!-- no translation found for fingerprint_error_lockout_permanent (9060651300306264843) -->
- <skip />
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Әрекетті қайталаңыз."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Тым көп әрекет жасалды. Орнына экран құлпын пайдаланыңыз."</string>
+ <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Тым көп әрекет жасалды. Орнына экран құлпын пайдаланыңыз."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Саусақ ізін өңдеу мүмкін емес. Қайталап көріңіз."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Саусақ іздері тіркелмеген."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бұл құрылғыда саусақ ізін оқу сканері жоқ."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик уақытша өшірулі."</string>
@@ -635,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Жөндеу қызметіне барыңыз."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Бет үлгісі жасалмады. Қайталап көріңіз."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Тым ашық. Күңгірттеу жарық керек."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Жарық жеткіліксіз"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Телефонды алшақ ұстаңыз."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Телефонды жақынырақ ұстаңыз."</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Телефонды жоғарырақ ұстаңыз."</string>
@@ -1250,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Қолданбалар іске қосылуда."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Қосуды аяқтауда."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Қуат түймесін бастыңыз. Бұл әдетте экранды өшіреді.\n\nСаусақ ізін реттеу үшін, оны жайлап түртіп көріңіз."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Экранды өшіру үшін түртіңіз"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Экранды өшіру"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Реттеуді аяқтау үшін экранды өшіріңіз"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Өшіру"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Саусақ ізін растауды жалғастырасыз ба?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Қуат түймесін бастыңыз. Бұл әдетте экранды өшіреді.\n\nСаусақ ізін растау үшін, оны жайлап түртіп көріңіз."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Экранды өшіру"</string>
@@ -1307,7 +1306,7 @@
<string name="network_switch_metered_detail" msgid="1358296010128405906">"Құрылғы <xliff:g id="PREVIOUS_NETWORK">%2$s</xliff:g> желісінде интернетпен байланыс жоғалған жағдайда <xliff:g id="NEW_NETWORK">%1$s</xliff:g> желісін пайдаланады. Деректер ақысы алынуы мүмкін."</string>
<string name="network_switch_metered_toast" msgid="501662047275723743">"<xliff:g id="PREVIOUS_NETWORK">%1$s</xliff:g> желісінен <xliff:g id="NEW_NETWORK">%2$s</xliff:g> желісіне ауысты"</string>
<string-array name="network_switch_type_name">
- <item msgid="2255670471736226365">"мобильдік деректер"</item>
+ <item msgid="2255670471736226365">"мобильдік интернет"</item>
<item msgid="5520925862115353992">"Wi-Fi"</item>
<item msgid="1055487873974272842">"Bluetooth"</item>
<item msgid="1616528372438698248">"Ethernet"</item>
@@ -1574,7 +1573,7 @@
<string name="extract_edit_menu_button" msgid="63954536535863040">"Өзгерту"</string>
<string name="data_usage_warning_title" msgid="9034893717078325845">"Дерек шығыны туралы ескерту"</string>
<string name="data_usage_warning_body" msgid="1669325367188029454">"Деректің <xliff:g id="APP">%s</xliff:g> пайдаландыңыз"</string>
- <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Мобильдік деректер шегіне жетті"</string>
+ <string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"Мобильдік интернет шегіне жетті"</string>
<string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Wi-Fi деректер шегіне жеттіңіз"</string>
<string name="data_usage_limit_body" msgid="3567699582000085710">"Деректер жіберу қалған цикл үшін тоқтатылды"</string>
<string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"Мобильдік дерек шегінен астыңыз"</string>
@@ -1582,7 +1581,7 @@
<string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Сіз <xliff:g id="SIZE">%s</xliff:g> шегінен асып кеттіңіз"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"Фондық деректер шектелген"</string>
<string name="data_usage_restricted_body" msgid="5338694433686077733">"Шектеуді жою үшін түртіңіз."</string>
- <string name="data_usage_rapid_title" msgid="2950192123248740375">"Мобильдік деректер көп жұмсалды"</string>
+ <string name="data_usage_rapid_title" msgid="2950192123248740375">"Мобильдік интернет көп жұмсалды"</string>
<string name="data_usage_rapid_body" msgid="3886676853263693432">"Қолданбаларыңыз деректерді әдеттегіден көбірек пайдаланды"</string>
<string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> қолданбасы деректерді әдеттегіден көбірек пайдаланды"</string>
<string name="ssl_certificate" msgid="5690020361307261997">"Қауіпсіздік сертификаты"</string>
@@ -1612,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"ТД"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Телефон"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Үндеткіштерді қондыру"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Сыртқы құрылғы"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Құлақаспаптар"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Жүйе"</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 0d3d07dc3bee..09c4478cc05d 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ប្រើ​ការ​ចាក់​សោ​អេក្រង់"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"បញ្ចូលការចាក់សោអេក្រង់របស់អ្នក ដើម្បីបន្ត"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"សង្កត់លើ​ឧបករណ៍​ចាប់សញ្ញា​ឱ្យណែន"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"មិនអាចសម្គាល់​ស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"សម្អាត​ឧបករណ៍​ចាប់ស្នាមម្រាមដៃ រួចព្យាយាម​ម្ដងទៀត"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"សម្អាត​ឧបករណ៍​ចាប់សញ្ញា រួចព្យាយាម​ម្ដងទៀត"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"សង្កត់លើ​ឧបករណ៍​ចាប់សញ្ញា​ឱ្យណែន"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"ចលនាម្រាមដៃយឺតពេកហើយ។ សូមព្យាយាមម្តងទៀត។"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"សាកល្បងប្រើ​ស្នាមម្រាមដៃផ្សេងទៀត"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ភ្លឺពេក"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"ចាប់ដឹងថាចុចប៊ូតុងថាមពល"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"សាកល្បង​កែតម្រូវ"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ប្ដូរទីតាំងម្រាមដៃ​របស់អ្នកតិចៗ​គ្រប់ពេល"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"បានផ្ទៀងផ្ទាត់​មុខ សូម​ចុច​បញ្ជាក់"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ផ្នែករឹងស្នាមម្រាមដៃមិនមានទេ។"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"មិនអាចរៀបចំ​ស្នាមម្រាមដៃបានទេ"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"ស្នាមម្រាមដៃបានអស់ម៉ោង។ សូមព្យាយាមម្តងទៀត។"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"កា​ររៀបចំ​ស្នាមម្រាមដៃបានអស់ម៉ោង។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"បានបោះបង់ប្រតិបត្តិការស្នាមម្រាមដៃ។"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ប្រតិបត្តិការ​ស្នាម​ម្រាម​ដៃ​ត្រូវ​បាន​បោះ​បង់​ដោយ​អ្នក​ប្រើប្រាស់។"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"ព្យាយាមចូលច្រើនពេកហើយ។ សូមព្យាយាមម្តងទៀតពេលក្រោយ។"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ព្យាយាម​ច្រើនដងពេក។ សូមប្រើការចាក់សោ​អេក្រង់ជំនួសវិញ។"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ព្យាយាម​ច្រើនដងពេក។ សូមប្រើការចាក់សោ​អេក្រង់ជំនួសវិញ។"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ព្យាយាមម្ដងទៀត។"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"មិនអាចដំណើរការស្នាមម្រាមដៃបានទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"មិន​មាន​ការ​ចុះឈ្មោះស្នាម​ម្រាមដៃទេ។"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ឧបករណ៍នេះ​មិនមាន​ឧបករណ៍ចាប់​ស្នាមម្រាមដៃទេ។"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"បានបិទ​ឧបករណ៍​ចាប់សញ្ញាជា​បណ្តោះអាសន្ន។"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ទាក់ទងក្រុមហ៊ុន​ផ្ដល់ការជួសជុល។"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"មិនអាចបង្កើតគំរូមុខរបស់អ្នកបានទេ។ សូមព្យាយាមម្ដងទៀត។"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ភ្លឺពេក។ សូមសាកល្បង​ប្រើ​ពន្លឺស្រាលជាងនេះ។"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"មិនមាន​ពន្លឺគ្រប់គ្រាន់"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ដាក់​ទូរសព្ទឱ្យឆ្ងាយ​ជាងមុន"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ដាក់​ទូរសព្ទ​ឱ្យជិត​ជាងមុន"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ដាក់​ទូរសព្ទ​ឱ្យខ្ពស់​ជាងមុន"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ចាប់ផ្ដើម​កម្មវិធី។"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"បញ្ចប់​ការ​ចាប់ផ្ដើម។"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"អ្នកបានចុចប៊ូតុងថាមពល — ជាធម្មតាការធ្វើបែបនេះនឹងបិទអេក្រង់។\n\nសាកល្បងចុចថ្នមៗ ខណៈពេលកំពុងរៀបចំស្នាមម្រាមដៃរបស់អ្នក។"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"ចុច ដើម្បីបិទអេក្រង់"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"បិទ​អេក្រង់"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"ដើម្បីបញ្ចប់ការរៀបចំ សូមបិទអេក្រង់"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"បិទ"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"បន្តផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃរបស់អ្នកឬ?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"អ្នកបានចុចប៊ូតុងថាមពល — ជាធម្មតាការធ្វើបែបនេះនឹងបិទអេក្រង់។\n\nសាកល្បងចុចថ្នមៗ ដើម្បីផ្ទៀងផ្ទាត់ស្នាមម្រាមដៃរបស់អ្នក។"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"បិទ​អេក្រង់"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"ទូរទស្សន៍"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ទូរសព្ទ"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ភ្ជាប់​អូប៉ាល័រ"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"ឧបករណ៍ខាងក្រៅ"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"កាស"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"ប្រព័ន្ធ"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index 9925a261ad8a..7fd8aef1b9b9 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಬಳಸಿ"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ಮುಂದುವರಿಯಲು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಲಾಕ್‌ ಅನ್ನು ನಮೂದಿಸಿ"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"ಸೆನ್ಸರ್ ಮೇಲೆ ದೃಢವಾಗಿ ಒತ್ತಿರಿ"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ದಯವಿಟ್ಟು ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಗುರುತಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ಫಿಂಗರ್‌ ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌‌ ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"ಸೆನ್ಸರ್‌‌ ಸ್ವಚ್ಛಗೊಳಿಸಿ ಹಾಗೂ ಪುನಃ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"ಸೆನ್ಸರ್ ಮೇಲೆ ದೃಢವಾಗಿ ಒತ್ತಿರಿ"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"ಬೆರಳನ್ನು ತುಂಬಾ ನಿಧಾನವಾಗಿ ಸರಿಸಲಾಗಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ಮತ್ತೊಂದು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ತುಂಬಾ ಪ್ರಕಾಶಮಾನವಾಗಿದೆ"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"ಪವರ್ ಬಟನ್ ಒತ್ತುವುದು ಪತ್ತೆಯಾಗಿದೆ"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ಹೊಂದಿಸಲು ಪ್ರಯತ್ನಿಸಿ"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ಪ್ರತಿ ಬಾರಿಯೂ ನಿಮ್ಮ ಬೆರಳಿನ ಸ್ಥಾನವನ್ನು ಸ್ವಲ್ಪ ಮಟ್ಟಿಗೆ ಬದಲಾಯಿಸಿ"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ಮುಖವನ್ನು ದೃಢೀಕರಿಸಲಾಗಿದೆ, ದೃಢೀಕರಣವನ್ನು ಒತ್ತಿ"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಹಾರ್ಡ್‌ವೇರ್‌ ಲಭ್ಯವಿಲ್ಲ."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಸೆಟಪ್ ಮಾಡಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅವಧಿ ಮೀರಿದೆ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ಫಿಂಗರ್‌ ಪ್ರಿಂಟ್ ಸೆಟಪ್ ಮಾಡುವ ಅವಧಿ ಮುಗಿದಿದೆ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಮಾಡಲಾಗಿದೆ."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ಬಳಕೆದಾರರು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಕಾರ್ಯಾಚರಣೆಯನ್ನು ರದ್ದುಪಡಿಸಿದ್ದಾರೆ."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"ಹಲವಾರು ಪ್ರಯತ್ನಗಳು. ನಂತರ ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಬದಲಾಗಿ ಸ್ಕ್ರೀನ್‌ಲಾಕ್ ಬಳಸಿ."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ಹಲವು ಬಾರಿ ಪ್ರಯತ್ನಿಸಿದ್ದೀರಿ. ಬದಲಾಗಿ ಪರದೆಲಾಕ್ ಬಳಸಿ."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ಯಾವುದೇ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್‌ ಅನ್ನು ನೋಂದಣಿ ಮಾಡಿಲ್ಲ."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ಈ ಸಾಧನವು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್‌‌ ಅನ್ನು ಹೊಂದಿಲ್ಲ."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ಸೆನ್ಸಾರ್ ಅನ್ನು ತಾತ್ಕಾಲಿಕವಾಗಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ರಿಪೇರಿ ಮಾಡುವವರನ್ನು ಸಂಪರ್ಕಿಸಿ."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ಫೇಸ್ ಮಾಡೆಲ್ ರಚಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಪುನಃ ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ತುಂಬಾ ಪ್ರಕಾಶಮಾನವಾಗಿದೆ ಮಂದ ಪ್ರಕಾಶಮಾನವಿರುವ ಲೈಟ್ ಬಳಸಿ"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"ಸಾಕಷ್ಟು ಬೆಳಕು ಇಲ್ಲ"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ಫೋನ್ ಅನ್ನು ದೂರಕ್ಕೆ ಸರಿಸಿ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ಫೋನ್ ಅನ್ನು ಸಮೀಪಕ್ಕೆ ತನ್ನಿ"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ಫೋನ್ ಅನ್ನು ಮೇಲಕ್ಕೆ ಎತ್ತಿ ಹಿಡಿಯಿರಿ."</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಪ್ರಾರಂಭಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ಬೂಟ್ ಪೂರ್ಣಗೊಳಿಸಲಾಗುತ್ತಿದೆ."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ನೀವು ಪವರ್ ಬಟನ್ ಒತ್ತಿದ್ದೀರಿ — ಇದು ಸಾಮಾನ್ಯವಾಗಿ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ.\n\nನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಹೊಂದಿಸುವಾಗ ಲಘುವಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಿ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"ಸೆಟಪ್ ಪೂರ್ಣಗೊಳಿಸಲು, ಸ್ಕ್ರೀನ್‌ ಆಫ್ ಮಾಡಿ"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"ಆಫ್ ಮಾಡಿ"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಪರಿಶೀಲನೆ ಮುಂದುವರಿಸುವುದೇ?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"ನೀವು ಪವರ್ ಬಟನ್ ಒತ್ತಿದ್ದೀರಿ — ಇದು ಸಾಮಾನ್ಯವಾಗಿ ಸ್ಕ್ರೀನ್ ಅನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸುತ್ತದೆ.\n\nನಿಮ್ಮ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಪರಿಶೀಲಿಸಲು ಲಘುವಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸಿ."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ಸ್ಕ್ರೀನ್ ಆಫ್ ಮಾಡಿ"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"ಟಿವಿ"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ಫೋನ್"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ಡಾಕ್ ಸ್ಪೀಕರ್‍‌ಗಳು"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"ಬಾಹ್ಯ ಸಾಧನ"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ಹೆಡ್‌ಫೋನ್‌ಗಳು"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"ಸಿಸ್ಟಂ"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 40c545fd2ea4..35e9e85a5d4d 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"화면 잠금 사용"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"계속하려면 화면 잠금용 사용자 인증 정보를 입력하세요"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"센서 위에 손가락을 좀 더 오래 올려놓으세요."</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"지문을 인식할 수 없습니다. 다시 시도해 주세요."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"지문을 인식할 수 없습니다. 다시 시도해 주세요."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"지문 센서를 닦은 후 다시 시도해 보세요."</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"센서를 닦은 후 다시 시도해 보세요."</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"센서 위에 손가락을 좀 더 오래 올려놓으세요."</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"손가락을 너무 느리게 움직였습니다. 다시 시도해 주세요."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"다른 지문으로 시도"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"너무 밝음"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"전원 누름이 감지되었습니다."</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"조정 시도"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"지문을 등록할 때마다 손가락을 조금씩 이동하세요"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"얼굴이 인증되었습니다. 확인을 누르세요"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"지문 인식 하드웨어를 사용할 수 없습니다."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"지문을 설정할 수 없음"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"지문 인식 시간이 초과되었습니다. 다시 시도하세요."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"지문 설정 시간이 초과되었습니다. 다시 시도해 주세요."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"지문 인식 작업이 취소되었습니다."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"사용자가 지문 인식 작업을 취소했습니다."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"시도 횟수가 너무 많습니다. 나중에 다시 시도하세요."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"시도 횟수가 너무 많습니다. 화면 잠금을 대신 사용하세요."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"시도 횟수가 너무 많습니다. 화면 잠금을 대신 사용하세요."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"다시 시도해 보세요."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"지문을 처리할 수 없습니다. 다시 시도해 주세요."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"등록된 지문이 없습니다."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"기기에 지문 센서가 없습니다."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"센서가 일시적으로 사용 중지되었습니다."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"수리업체에 방문하세요."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"얼굴 모델을 만들 수 없습니다. 다시 시도해 주세요."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"너무 밝습니다. 조명 밝기를 조금 낮춰보세요."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"조명이 부족합니다."</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"휴대전화를 얼굴에서 더 멀리 떨어뜨려 주세요."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"휴대전화를 얼굴에 더 가까이 가져와 주세요."</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"휴대전화를 위로 이동하세요"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"앱을 시작하는 중입니다."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"부팅 완료"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"전원 버튼을 눌렀습니다. 이러면 보통 화면이 꺼집니다.\n\n지문 설정 중에 가볍게 탭하세요."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"탭하여 화면 끄기"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"화면 끄기"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"설정을 완료하려면 화면을 끄세요"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"사용 중지"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"지문 인증을 계속할까요?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"전원 버튼을 눌렀습니다. 이러면 보통 화면이 꺼집니다.\n\n지문을 인식하려면 화면을 가볍게 탭하세요."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"화면 끄기"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"휴대전화"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"도크 스피커"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"외부 기기"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"헤드폰"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"시스템"</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index f27b0d63fecf..176a49d18d33 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Экран кулпусун колдонуу"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Улантуу үчүн экрандын кулпусун киргизиңиз"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Сенсорду катуу басыңыз"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Манжа изи иштелбей койду. Кайталап көрүңүз."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Манжа изи таанылбай жатат. Кайра аракет кылыңыз."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Манжа изинин сенсорун тазалап, кайра аракет кылыңыз"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Сенсорду тазалап, кайра аракет кылыңыз"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Сенсорду катуу басыңыз"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Манжа өтө жай жылды. Кайталап көрүңүз."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Башка манжа изин байкап көрүңүз"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Өтө жарык"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Кубат баскычы басылганы аныкталды"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Тууралап көрүңүз"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Манжаңыздын абалын ар жолкусунда бир аз өзгөртүп туруңуз"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Жүздүн аныктыгы текшерилди, эми \"Ырастоону\" басыңыз"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Манжа изинин аппараттык камсыздоосу жеткиликтүү эмес."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Манжа изи жөндөлбөй жатат"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Манжа изин күтүү мөөнөтү бүттү. Кайталап көрүңүз."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Манжа изин тууралоо убакыты бүтүп калды. Кайра аракет кылыңыз."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Манжа изи иш-аракети жокко чыгарылды."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Манжа изи операциясын колдонуучу жокко чыгарды."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Аракеттер өтө көп болду. Бир аздан кийин кайталап көрүңүз."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Өтө көп жолу аракет кылдыңыз. Экранды кулпулоо функциясын колдонуңуз."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Өтө көп жолу аракет кылдыңыз. Экранды кулпулоо функциясын колдонуңуз."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Кайра бир аракеттениңиз."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Манжа изи иштетилген жок. Кайра аракет кылыңыз."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бир да манжа изи катталган эмес."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Бул түзмөктө манжа изинин сенсору жок."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сенсор убактылуу өчүрүлгөн."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Тейлөө кызматына кайрылыңыз."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Жүзүңүздүн үлгүсү түзүлгөн жок. Кайталаңыз."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Өтө жарык. Жарыктыкты азайтып көрүңүз."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Жарык жетишсиз"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Телефонду алыстатыңыз"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Телефонду жакындатыңыз"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Телефонду жогору жылдырыңыз"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Колдонмолорду иштетип баштоо"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Жүктөлүүдө"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Кубат баскычын бастыңыз — адатта, бул экранды өчүрөт.\n\nМанжаңыздын изин жөндөп жатканда аны акырын басып көрүңүз."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Экранды өчүрүү үчүн таптаңыз"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Экранды өчүрүү"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Тууралап бүтүрүү үчүн экранды өчүрүңүз"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Өчүрүү"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Манжаңыздын изин ырастоону улантасызбы?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Кубат баскычын бастыңыз — адатта, бул экранды өчүрөт.\n\nМанжаңыздын изин ырастоо үчүн аны акырын басып көрүңүз."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Экранды өчүрүү"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Сыналгы"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Телефон"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Аудио док бекет"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Тышкы түзмөк"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Кулакчын"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Тутум"</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index 7c9f8154a42f..7ca4b56938e0 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ໃຊ້ການລັອກໜ້າຈໍ"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ໃສ່ການລັອກໜ້າຈໍຂອງທ່ານເພື່ອສືບຕໍ່"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"ກົດຢູ່ເຊັນເຊີໃຫ້ແໜ້ນ"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ບໍ່​ສາ​ມາດ​ດຳ​ເນີນ​ການ​ລາຍ​ນີ້ວ​ມື​ໄດ້. ກະ​ລຸ​ນາ​ລອງ​ໃໝ່​ອີກ."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"ບໍ່ສາມາດຈຳແນກລາຍນິ້ວມືໄດ້. ກະລຸນາລອງໃໝ່."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ໃຫ້ອະນາໄມເຊັນ​ເຊີລາຍນິ້ວ​ມືແລ້ວລອງໃໝ່"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"ໃຫ້ອະນາໄມເຊັນ​ເຊີແລ້ວລອງໃໝ່"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"ກົດຢູ່ເຊັນເຊີໃຫ້ແໜ້ນ"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"ຍ້າຍ​ນີ້ວ​ມື​ໄປຊ້າ​ເກີນ​ໄປ. ກະ​ລຸ​ນາ​ລອງ​ໃໝ່​ອີກ."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ລອງໃຊ້ລາຍນິ້ວມືອື່ນ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ແຈ້ງເກີນໄປ"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"ກວດພົບແຮງກົດ"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ກະລຸນາລອງປັບແກ້"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ປ່ຽນຕຳແໜ່ງຂອງນິ້ວມືຂອງທ່ານເລັກນ້ອຍໃນແຕ່ລະເທື່ອ"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ພິສູດຢືນຢັນໃບໜ້າແລ້ວ, ກະລຸນາກົດຢືນຢັນ"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ບໍ່​ມີ​ຮາດ​ແວລາຍ​ນີ້ວ​ມື​ໃຫ້​ຢູ່."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ບໍ່ສາມາດຕັ້ງຄ່າລາຍນິ້ວມືໄດ້"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"ເວ​ລາ​ລາຍ​ນີ້ວ​ມື​ບໍ່​ເຂົ້າ​ເຖິງ​ໄດ້. ລອງ​ໃໝ່​ອີກ."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ໝົດເວລາຕັ້ງຄ່າລາຍນິ້ວມື. ກະລຸນາລອງໃໝ່."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ຍົກ​ເລີກ​ການ​ດຳ​ເນີນ​ການ​ລາຍ​ນີ້ວ​ມື​ແລ້ວ."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ຜູ້ໃຊ້ໄດ້ຍົກເລີກຄຳສັ່ງລາຍນິ້ວມືແລ້ວ."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"ມີ​ຄວາມ​ພະ​ຍາ​ຍາມ​ຫຼາຍ​ຄັ້ງ​ເກີນ​ໄປ. ລອງ​ໃໝ່​ພາຍ​ຫຼັງ."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາໃຊ້ການລອກໜ້າຈໍແທນ."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ພະຍາຍາມຫຼາຍເທື່ອເກີນໄປ. ກະລຸນາໃຊ້ການລອກໜ້າຈໍແທນ."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ລອງໃໝ່ອີກຄັ້ງ."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ບໍ່ສາມາດປະມວນຜົນລາຍນິ້ວມືໄດ້. ກະລຸນາລອງໃໝ່."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ບໍ່ມີການລົງທະບຽນລາຍນິ້ວມື."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ອຸປະກອນນີ້ບໍ່ມີເຊັນເຊີລາຍນິ້ວມື."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ປິດການເຮັດວຽກຂອງເຊັນເຊີໄວ້ຊົ່ວຄາວແລ້ວ."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ກະລຸນາໄປຫາຜູ້ໃຫ້ບໍລິການສ້ອມແປງ."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ບໍ່ສາມາດສ້າງຮູບແບບໃບໜ້າຂອງທ່ານໄດ້. ກະລຸນາລອງໃໝ່."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ແຈ້ງເກີນໄປ. ລອງຄ່ອຍແສງໄຟລົງ."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"ແສງບໍ່ພໍ"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ເລື່ອນໂທລະສັບອອກໄປໄກຂຶ້ນ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ເລື່ອນໂທລະສັບເຂົ້າໄປໃກ້ຂຶ້ນ"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ຍົກໂທລະສັບໃຫ້ສູງຂຶ້ນ"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ກຳລັງເປີດແອັບຯ."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ກຳລັງສຳເລັດການເປີດລະບົບ."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ທ່ານກົດປຸ່ມເປີດປິດ, ປົກກະຕິນີ້ຈະປິດໜ້າຈໍ.\n\nລອງແຕະຄ່ອຍໆໃນລະຫວ່າງຕັ້ງຄ່າລາຍນິ້ວມືຂອງທ່ານ."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"ແຕະເພື່ອປິດໜ້າຈໍ"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"ປິດໜ້າຈໍ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"ເພື່ອສິ້ນສຸດການຕັ້ງຄ່າ, ໃຫ້ປິດໜ້າຈໍ"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"ປິດໄວ້"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"ສືບຕໍ່ການຢັ້ງຢືນລາຍນິ້ວມືຂອງທ່ານບໍ?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"ທ່ານກົດປຸ່ມເປີດປິດ, ປົກກະຕິນີ້ຈະເປັນການປິດໜ້າຈໍ.\n\nໃຫ້ລອງແຕະຄ່ອຍໆເພື່ອຢັ້ງຢືນລາຍນິ້ວມືຂອງທ່ານ."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ປິດໜ້າຈໍ"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"ໂທລະພາບ"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ໂທລະສັບ"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ບ່ອນຕັ້ງລຳໂພງ"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"ອຸປະກອນພາຍນອກ"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ຫູຟັງ"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"ລະບົບ"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 2954f9c192c1..9f646f0c77db 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -585,13 +585,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Naudoti ekrano užraktą"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Jei norite tęsti, įveskite ekrano užraktą"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Tvirtai paspauskite jutiklį"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nepavyko apdoroti piršto antspaudo. Bandykite dar kartą."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Nepavyko atpažinti kontrolinio kodo. Bandykite dar kartą."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Nuvalykite kontrolinio kodo jutiklį ir bandykite dar kartą"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Nuvalykite jutiklį ir bandykite dar kartą"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Tvirtai paspauskite jutiklį"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Per lėtai judinate pirštą. Bandykite dar kartą."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Pabandykite kitą kontrolinį kodą"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Per šviesu"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Aptiktas maitinimo mygtuko paspaudimas"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Pabandykite koreguoti"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Kaskart šiek tiek pakeiskite piršto poziciją"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -603,12 +604,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Veidas autentifikuotas, paspauskite patvirtinimo mygtuką"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Piršto antspaudo aparatinė įranga nepasiekiama."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nepavyko nustatyti kontrolinio kodo"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Baigėsi piršto antspaudo nustatymo skirtasis laikas. Bandykite dar kartą."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Baigėsi kontrolinio kodo sąrankos skirtasis laikas. Bandykite dar kartą."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Piršto antspaudo operacija atšaukta."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Piršto antspaudo operaciją atšaukė naudotojas."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Per daug bandymų. Vėliau bandykite dar kartą."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Per daug bandymų. Naudokite ekrano užraktą."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Per daug bandymų. Naudokite ekrano užraktą."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Bandykite dar kartą."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nepavyko apdoroti kontrolinio kodo. Bandykite dar kartą."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neužregistruota jokių kontrolinių kodų."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šiame įrenginyje nėra kontrolinio kodo jutiklio."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Jutiklis laikinai išjungtas."</string>
@@ -636,8 +637,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Apsilankykite pas taisymo paslaugos teikėją."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nepavyko sukurti veido modelio. Band. dar kartą."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Per šviesu. Išbandykite mažesnį apšvietimą."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nepakanka apšvietimo"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Laikykite telefoną toliau"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Laikykite telefoną arčiau"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Laikykite telefoną aukščiau"</string>
@@ -1251,8 +1251,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Paleidžiamos programos."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Užbaigiamas paleidimas."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Paspaudėte maitinimo mygtuką, taip paprastai išjungiamas ekranas.\n\nNustatydami kontrolinį kodą, pabandykite jį švelniai paliesti."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Palieskite, kad išjungtumėte ekraną"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Išjungti ekraną"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Sąrankos užbaigimas išjungus ekraną"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Išjungti"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Tęsti kontrolinio kodo patvirtinimą?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Paspaudėte maitinimo mygtuką, taip paprastai išjungiamas ekranas.\n\nNorėdami patvirtinti kontrolinį kodą, pabandykite jį švelniai paliesti."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Išjungti ekraną"</string>
@@ -1613,7 +1613,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefonas"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Doko garsiakalbiai"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Išorinis įrenginys"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Ausinės"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistema"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 853a490c9d39..3a078dbf1cd9 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ekrāna bloķēšanas metodes izmantošana"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Lai turpinātu, ievadiet ekrāna bloķēšanas informāciju"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Stingri spiediet pirkstu pie sensora"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nevarēja apstrādāt pirksta nospiedumu. Lūdzu, mēģiniet vēlreiz."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Nevar atpazīt pirksta nospiedumu. Mēģiniet vēlreiz."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Notīriet pirkstu nospiedumu sensoru un mēģiniet vēlreiz"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Notīriet sensoru un mēģiniet vēlreiz"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Stingri spiediet pirkstu pie sensora"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Pārāk lēna pirksta kustība. Lūdzu, mēģiniet vēlreiz."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Izmēģiniet citu pirksta nospiedumu"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Pārāk spilgts"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Konstatēta barošanas pogas nospiešana"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Mēģiniet mainīt pozīciju"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Katru reizi mazliet mainiet pirksta pozīciju."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Seja ir autentificēta. Nospiediet pogu Apstiprināt."</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Nospieduma aparatūra nav pieejama."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nevar iestatīt pirksta nospiedumu"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Pirkstu nospiedumu nolasīšanas aparatūras noildze. Mēģiniet vēlreiz."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Iestatot pirksta nospiedumu, iestājās noildze. Mēģiniet vēlreiz."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Nospieduma darbība neizdevās."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Lietotājs atcēla pirksta nospieduma darbību."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Pārāk daudz mēģinājumu. Vēlāk mēģiniet vēlreiz."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Pārāk daudz mēģinājumu. Izmantojiet ekrāna bloķēšanu."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Pārāk daudz mēģinājumu. Izmantojiet ekrāna bloķēšanu."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Mēģiniet vēlreiz."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nevar apstrādāt pirksta nospiedumu. Mēģiniet vēlreiz."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nav reģistrēts neviens pirksta nospiedums."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Šajā ierīcē nav pirksta nospieduma sensora."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensors ir īslaicīgi atspējots."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Sazinieties ar remonta pakalpojumu sniedzēju."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nevar izveidot sejas modeli. Mēģiniet vēlreiz."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Pārāk spilgts. Izmēģiniet maigāku apgaismojumu."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nepietiekams apgaismojums"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Pārvietojiet tālruni tālāk."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Pārvietojiet tālruni tuvāk."</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Paceliet tālruni augstāk."</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Notiek lietotņu palaišana."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Tiek pabeigta sāknēšana."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Jūs nospiedāt barošanas pogu — tādējādi parasti tiek izslēgts ekrāns.\n\nMēģiniet viegli pieskarties, iestatot pirksta nospiedumu."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Pieskarieties, lai izslēgtu ekrānu"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Izslēgt ekrānu"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Lai beigtu iestatīt, izslēdziet ekrānu"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Izslēgt"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vai apstiprināt pirksta nospiedumu?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Jūs nospiedāt barošanas pogu — tādējādi parasti tiek izslēgts ekrāns.\n\nMēģiniet viegli pieskarties, lai apstiprinātu pirksta nospiedumu."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Izslēgt ekrānu"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Tālrunis"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Doka skaļruņi"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Ārēja ierīce"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Austiņas"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistēma"</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index 9ce43d1e74ad..c67819e6c1a2 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Користи заклучување екран"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Внесете го заклучувањето на екранот за да продолжите"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Цврсто притиснете на сензорот"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Отпечатокот не може да се обработи. Обидете се повторно."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Не се препознава отпечатокот. Обидете се повторно."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Исчистете го сензорот за отпечатоци и обидете се повторно"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Исчистете го сензорот и обидете се повторно"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Цврсто притиснете на сензорот"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Прстот се движеше премногу бавно. Обидете се повторно."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Пробајте со друг отпечаток"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Премногу светло"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Откриено е притискање на копчето за вклучување"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Пробајте да го приспособите прстот"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Менувајте ја положбата на прстот по малку секој пат"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицето е проверено, притиснете го копчето „Потврди“"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хардверот за отпечатоци не е достапен."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не може да се постави отпечаток"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Се достигна времето на истекување на отпечатокот. Обидете се повторно."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Времето за поставување отпечаток истече. Обидете се повторно."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Операцијата со отпечаток се откажа."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Корисникот ја откажа потврдата со отпечаток."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Премногу обиди. Обидете се повторно подоцна."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Премногу обиди. Користете заклучување екран."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Премногу обиди. Користете заклучување екран."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Обидете се повторно."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не може да се обработи отпечатокот од прст. Обидете се повторно."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Не се запишани отпечатоци."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Уредов нема сензор за отпечатоци."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензорот е привремено оневозможен."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Однесете го на поправка."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Не може да создаде модел на лик. Обидете се пак."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Премногу светла. Пробајте со послабо осветлување."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Нема доволно светлина"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Оддалечете го телефонот"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Доближете го телефонот"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Кренете го телефонот погоре"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Се стартуваат апликациите."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Подигањето завршува."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Го притиснавте копчето за вклучување — така обично се исклучува екранот.\n\nДопрете лесно додека го поставувате отпечатокот."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Допрете за да го исклучите екранот"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Исклучи го екранот"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"За да завршите со поставувањето, исклучете го екранот."</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Исклучи"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Да продолжи потврдувањето на отпечаток?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Го притиснавте копчето за вклучување — така обично се исклучува екранот.\n\nДопрете лесно за да го потврдите отпечатокот."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Исклучи го екранот"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Телевизор"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Телефон"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Приклучи звучници"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Надворешен уред"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Слушалки"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Систем"</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index c71047d421f2..63a5e0d5e490 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"തുടരാൻ നിങ്ങളുടെ സ്‌ക്രീൻ ലോക്ക് നൽകുക"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"സെൻസറിന് മുകളിൽ ശക്തിയായി അമർത്തുക"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ഫിംഗർപ്രിന്റ് പ്രോസസ് ചെയ്യാനായില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"ഫിംഗർപ്രിന്റ് തിരിച്ചറിയാനാകുന്നില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ഫിംഗർപ്രിന്റ് സെൻസർ വൃത്തിയാക്കിയ ശേഷം വീണ്ടും ശ്രമിക്കുക"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"സെൻസർ വൃത്തിയാക്കിയ ശേഷം വീണ്ടും ശ്രമിക്കുക"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"സെൻസറിന് മുകളിൽ ശക്തിയായി അമർത്തുക"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"വിരൽ വളരെ പതുക്കെ നീക്കി. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"മറ്റൊരു ഫിംഗർപ്രിന്റ് ഉപയോഗിച്ച് നോക്കുക"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"തെളിച്ചം വളരെയധികമാണ്"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"പവർ ബട്ടൺ അമർത്തിയത് തിരിച്ചറിഞ്ഞു"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"അൽപ്പം നീക്കി നോക്കൂ"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ഓരോ തവണയും നിങ്ങളുടെ വിരലിന്റെ സ്ഥാനം ചെറുതായി മാറ്റുക"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"മുഖം പരിശോധിച്ചുറപ്പിച്ചു, സ്ഥിരീകരിക്കുക അമർത്തുക"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ഫിംഗർപ്രിന്റ് ഹാർഡ്‌വെയർ ലഭ്യമല്ല."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ഫിംഗർപ്രിന്റ് സജ്ജീകരിക്കാനാകില്ല"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"ഫിംഗർപ്രിന്റ് നൽകേണ്ട സമയം കഴിഞ്ഞു. വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ഫിംഗർപ്രിന്റ് സജ്ജീകരണം ടൈംഔട്ടായി. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ഫിംഗർപ്രിന്റ് പ്രവർത്തനം റദ്ദാക്കി."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ഉപയോക്താവ് റദ്ദാക്കിയ ഫിംഗർപ്രിന്‍റ് പ്രവർത്തനം."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"നിരവധി തവണ ശ്രമിച്ചു. പിന്നീട് വീണ്ടും ശ്രമിക്കുക."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"നിരവധി ശ്രമങ്ങൾ. പകരം സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"നിരവധി ശ്രമങ്ങൾ. പകരം സ്‌ക്രീൻ ലോക്ക് ഉപയോഗിക്കുക."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"വീണ്ടും ശ്രമിക്കൂ."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ഫിംഗർപ്രിന്റ് പ്രോസസ് ചെയ്യാനാകില്ല. വീണ്ടും ശ്രമിക്കുക."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"വിരലടയാളങ്ങൾ എൻറോൾ ചെയ്തിട്ടില്ല."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ഈ ഉപകരണത്തിൽ ഫിംഗർപ്രിന്റ് സെൻസറില്ല."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"സെൻസർ താൽക്കാലികമായി പ്രവർത്തനരഹിതമാക്കി."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"റിപ്പയർ കേന്ദ്രം സന്ദർശിക്കുക."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"മുഖ മോഡൽ സൃഷ്ടിക്കാനാകുന്നില്ല. വീണ്ടും ശ്രമിക്കൂ."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"വളരെയധികം തെളിച്ചം. സൗമ്യതയേറിയ പ്രകാശം ശ്രമിക്കൂ."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"വേണ്ടത്ര പ്രകാശമില്ല"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ഫോൺ കൂടുതൽ ദൂരേയ്ക്ക് നീക്കുക"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ഫോൺ അടുത്തേക്ക് നീക്കുക"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ഫോൺ മുകളിലേക്ക് ഉയർത്തുക"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"അപ്ലിക്കേഷനുകൾ ആരംഭിക്കുന്നു."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ബൂട്ട് ചെയ്യൽ പൂർത്തിയാകുന്നു."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"നിങ്ങൾ പവർ ബട്ടൺ അമർത്തി — സാധാരണയായി ഇത് സ്ക്രീൻ ഓഫാകുന്നതിന് കാരണമാകും.\n\nനിങ്ങളുടെ ഫിംഗർപ്രിന്റ് സജ്ജീകരിക്കുമ്പോൾ മൃദുവായി ടാപ്പ് ചെയ്ത് നോക്കുക."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"സ്ക്രീൻ ഓഫാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"സ്ക്രീൻ ഓഫാക്കുക"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"സജ്ജീകരണം നിർത്താൻ, സ്ക്രീൻ ഓഫാക്കുക"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"ഓഫാക്കുക"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിക്കൽ തുടരണോ?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"നിങ്ങൾ പവർ ബട്ടൺ അമർത്തി — സാധാരണയായി ഇത് സ്ക്രീൻ ഓഫാകുന്നതിന് കാരണമാകും.\n\nനിങ്ങളുടെ ഫിംഗർപ്രിന്റ് പരിശോധിച്ചുറപ്പിക്കാൻ മൃദുവായി ടാപ്പ് ചെയ്ത് നോക്കുക."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"സ്ക്രീൻ ഓഫാക്കുക"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"ടിവി"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ഫോണ്‍"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ഡോക്ക് സ്‌പീക്കറുകൾ"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"ബാഹ്യ ഉപകരണം"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ഹെഡ്‌ഫോണുകൾ"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"സിസ്റ്റം"</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 417500db62c3..9be2b2c81c16 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Дэлгэцийн түгжээг ашиглах"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Үргэлжлүүлэхийн тулд дэлгэцийн түгжээгээ оруулна уу"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Мэдрэгч дээр чанга дарна уу"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Хурууны хээ боловсруулж чадахгүй байна. Дахин оролдоно уу."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Хурууны хээг таних боломжгүй. Дахин оролдоно уу."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Хурууны хээ мэдрэгчийг цэвэрлээд, дахин оролдоно уу"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Мэдрэгчийг цэвэрлээд, дахин оролдоно уу"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Мэдрэгч дээр чанга дарна уу"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Хуруу хэт удаан хөдөлгөсөн байна. Дахин оролдоно уу."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Өөр хурууны хээ туршина уу"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Хэт гэрэлтэй байна"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Цахилгаан даралтыг илрүүлсэн"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Тохируулж үзнэ үү"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Хурууныхаа байрлалыг тухай бүрд бага зэрэг өөрчилнө үү"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Царайг баталгаажууллаа. Баталгаажуулах товчлуурыг дарна уу"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хурууны хээний төхөөрөмж бэлэн бус байна."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Хурууны хээ тохируулах боломжгүй"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Хурууны хээ оруулах хугацаа өнгөрсөн байна. Дахин оруулна уу."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Хурууны хээний тохируулга завсарласан. Дахин оролдоно уу."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Хурууны хээний бүртгэл амжилтгүй боллоо."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Хэрэглэгч хурууны хээний баталгаажуулалтыг цуцалсан байна."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Хэтэрхий олон оролдлоо. Түр хүлээгээд дахин оролдоно уу."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Хэт олон удаа оролдлоо. Оронд нь дэлгэцийн түгжээ ашиглана уу."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Хэт олон удаа оролдлоо. Оронд нь дэлгэцийн түгжээ ашиглана уу."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Дахин оролдно уу."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Хурууны хээг боловсруулах боломжгүй. Дахин оролдоно уу."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Бүртгүүлсэн хурууны хээ алга."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Энэ төхөөрөмжид хурууны хээ мэдрэгч алга."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Мэдрэгчийг түр хугацаанд идэвхгүй болгосон."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Засварын үйлчилгээ үзүүлэгчид зочилно уу."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Нүүрний загвар үүсгэж чадсангүй. Дахин оролдоно уу."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Хэт цайвар байна. Гэрэл багатай газар оролдоно уу."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Гэрэл хангалтгүй байна"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Утсаа холдуулна уу"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Утсаа ойртуулна уу"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Утсаа дээшлүүлнэ үү"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Апп-г эхлүүлж байна."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Эхлэлийг дуусгаж байна."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Та асаах/унтраах товчийг дарсан байна — энэ нь ихэвчлэн дэлгэцийг унтраадаг.\n\nХурууны хээгээ тохируулж байх үедээ зөөлөн товшиж үзнэ үү."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Дэлгэцийг унтраахын тулд товшино уу"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Дэлгэцийг унтраах"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Тохиргоог дуусгахын тулд дэлгэцийг унтраана уу"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Унтраах"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Хурууны хээгээ үргэлжлүүлэн баталгаажуулах уу?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Та асаах/унтраах товчийг дарсан байна — энэ нь ихэвчлэн дэлгэцийг унтраадаг.\n\nХурууны хээгээ баталгаажуулахын тулд зөөлөн товшиж үзнэ үү."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Дэлгэцийг унтраах"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Tелевиз"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Утас"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Чанга яригчийг суулгах"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Гадаад төхөөрөмж"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Чихэвч"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Систем"</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index f91a18f96466..5729355b24bc 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"स्क्रीन लॉक वापरा"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"पुढे सुरू ठेवण्यासाठी तुमचे स्क्रीन लॉक एंटर करा"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"सेन्सरवर जोरात दाबा"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"फिंगरप्रिंटवर प्रक्रिया करणे शक्य झाले नाही. कृपया पुन्हा प्रयत्न करा."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"फिंगरप्रिंट ओळखता आली नाही. पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"फिंगरप्रिंट सेन्सर स्वच्छ करा आणि पुन्हा प्रयत्न करा"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"सेन्सर स्वच्छ करा आणि पुन्हा प्रयत्न करा"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"सेन्सरवर जोरात दाबा"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"बोट खूप सावकाश हलविले. कृपया पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"दुसरी फिंगरप्रिंट वापरून पहा"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"खूप प्रखर"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"पॉवर बटण दाबले गेल्याचे डिटेक्ट केले"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"अ‍ॅडजस्ट करण्याचा प्रयत्न करा"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"तुमच्या बोटाची स्थिती प्रत्येक वेळी थोडीशी बदला"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"चेहरा ऑथेंटिकेशन केलेला आहे, कृपया कंफर्म दाबा"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"फिंगरप्रिंट हार्डवेअर उपलब्‍ध नाही."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"फिंगरप्रिंट सेट करता आली नाही"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"फिंगरप्रिंट टाइमआउट झाले. पुन्हा प्रयत्न करा."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"फिंगरप्रिट सेट करण्याची वेळ संपली आहे. पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिंट ऑपरेशन रद्द झाले."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"वापरकर्त्याने फिंगरप्रिंट ऑपरेशन रद्द केले."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"खूप प्रयत्न केले. नंतर पुन्हा प्रयत्न करा."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"खूप जास्त प्रयत्न. त्याऐवजी स्क्रीन लॉक वापरा."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"खूप जास्त प्रयत्न. त्याऐवजी स्क्रीन लॉक वापरा."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"पुन्हा प्रयत्न करा."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फिंगरप्रिंटवर प्रक्रिया करू शकत नाही. पुन्हा प्रयत्न करा."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कोणत्याही फिंगरप्रिंटची नोंद झाली नाही"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"या डिव्हाइसमध्ये फिंगरप्रिंट सेन्सर नाही."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"सेन्सर तात्पुरता बंद केला आहे."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"दुरुस्तीच्या सेवा पुरवठादाराला भेट द्या."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"फेस मॉडेल तयार करू शकत नाही. पुन्हा प्रयत्न करा."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"खूप प्रखर. आणखी सौम्य प्रकाश वापरून पहा."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"पुरेसा प्रकाश नाही"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"फोन आणखी दूर हलवा"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"फोन आणखी जवळ हलवा"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"फोन आणखी वर हलवा"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"अ‍ॅप्स सुरू करत आहे."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बूट समाप्त होत आहे."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"तुम्ही पॉवर बटण दाबले — हे सहसा स्क्रीन बंद करते.\n\nतुमचे फिंगरप्रिंट सेट करताना हलके टॅप करून पहा."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"स्क्रीन बंद करण्यासाठी टॅप करा"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"स्क्रीन बंद करा"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"सेटअप संपवण्यासाठी, स्क्रीन बंद करा"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"बंद करा"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"फिंगरप्रिंट पडताळणी सुरू ठेवायची का?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"तुम्ही पॉवर बटण दाबले — हे सहसा स्क्रीन बंद करते.\n\nतुमच्या फिंगरप्रिंटची पडताळणी करण्यासाठी हलके टॅप करून पहा."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"स्क्रीन बंद करा"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"टीव्ही"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"फोन"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"स्पीकर डॉक करा"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"बाह्य डिव्हाइस"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"हेडफोन"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"सिस्टम"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 9b76a16c492c..3595acc7bcbd 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Gunakan kunci skrin"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Masukkan kunci skrin untuk teruskan"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Tekan dengan kuat pada penderia"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Tidak dapat memproses cap jari. Sila cuba lagi."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Tidak dapat mengecam cap jari. Cuba lagi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Bersihkan penderia cap jari dan cuba lagi"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Bersihkan penderia dan cuba lagi"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Tekan dengan kuat pada penderia"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Jari digerakkan terlalu perlahan. Sila cuba lagi."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Cuba cap jari lain"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Terlalu terang"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Penekanan Kuasa dikesan"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Cuba selaraskan"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Tukar sedikit kedudukan jari anda setiap kali pergerakan dilakukan"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Wajah disahkan, sila tekan sahkan"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Perkakasan cap jari tidak tersedia."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Tidak dapat menyediakan cap jari"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Sudah tamat masa untuk cap jari. Cuba lagi"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Persediaan cap jari telah tamat masa. Cuba lagi."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Pengendalian cap jari dibatalkan."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Pengendalian cap jari dibatalkan oleh pengguna."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Terlalu banyak percubaan. Cuba sebentar lagi."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Terlalu banyak percubaan. Gunakan kunci skrin."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Terlalu banyak percubaan. Gunakan kunci skrin."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Cuba lagi."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Tidak dapat memproses cap jari. Cuba lagi."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Tiada cap jari didaftarkan."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Peranti ini tiada penderia cap jari."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Penderia dilumpuhkan sementara."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Lawati penyedia pembaikan."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Tidak dapat membuat model wajah anda. Cuba lagi."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Terlalu terang. Cuba pencahayaan yang lebih lembut."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Cahaya tidak mencukupi"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Jauhkan telefon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Dekatkan telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Tinggikan lagi telefon"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Memulakan apl."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"But akhir."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Anda menekan butang kuasa — tindakan ini biasanya mematikan skrin.\n\nCuba ketik dengan perlahan semasa menetapkan cap jari anda."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Ketik untuk mematikan skrin"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Matikan skrin"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Untuk tamatkan persediaan, matikan skrin"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Matikan"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Terus mengesahkan cap jari anda?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Anda menekan butang kuasa — tindakan ini biasanya mematikan skrin.\n\nCuba ketik dengan perlahan untuk mengesahkan cap jari anda."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Matikan skrin"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Pembesar suara dok"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Peranti Luar"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Fon kepala"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistem"</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 87916c444458..da34c950fee3 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ဖန်သားပြင်လော့ခ်ချခြင်းကို သုံးခြင်း"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ရှေ့ဆက်ရန် သင်၏ဖန်သားပြင် လော့ခ်ချခြင်းကို ထည့်ပါ"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"အာရုံခံကိရိယာပေါ်တွင် သေချာဖိပါ"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"လက်ဗွေယူ၍ မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"လက်ဗွေကို မမှတ်မိပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"လက်ဗွေ အာရုံခံကိရိယာကို သန့်ရှင်းပြီး ထပ်စမ်းကြည့်ပါ"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"အာရုံခံကိရိယာကို သန့်ရှင်းပြီး ထပ်စမ်းကြည့်ပါ"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"အာရုံခံကိရိယာပေါ်တွင် သေချာဖိပါ"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"လက်ညှိုးအလွန်နှေးကွေးစွာ ရွေ့ခဲ့သည်။ ကျေးဇူးပြု၍ ထပ်မံကြိုးစားပါ။"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"အခြားလက်ဗွေဖြင့် စမ်းကြည့်ပါ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"အလွန် လင်းသည်"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"ဖွင့်ပိတ်ခလုတ် နှိပ်လိုက်သည်"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ပြင်ဆင်ကြည့်ပါ"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"အကြိမ်တိုင်း သင့်လက်ချောင်း၏ အနေအထားကို အနည်းငယ်ပြောင်းပါ"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"မျက်နှာ အထောက်အထားစိစစ်ပြီးပြီ၊ အတည်ပြုရန်ကို နှိပ်ပါ"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"လက်ဗွေ စက်ပစ္စည်းမရနိုင်ပါ။"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"လက်ဗွေကို စနစ်ထည့်သွင်း၍ မရပါ"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"လက်ဗွေယူချိန်ကုန် သွားပါသည်။ ထပ်စမ်းကြည့်ပါ။"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"လက်ဗွေစနစ်ထည့်သွင်းချိန် ကုန်သွားပါပြီ။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"လက်ဗွေယူခြင်း ပယ်ဖျက်လိုက်သည်။"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"လက်ဗွေဖြင့် အထောက်အထားစိစစ်ခြင်းကို အသုံးပြုသူက ပယ်ဖျက်ထားသည်။"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"ကြိုးစာမှု အကြိမ်များနေ၏။ နောက်မှ ထပ်မံကြိုးစားပါ။"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ကြိုးပမ်းမှုအကြိမ်ရေ များလွန်းသည်။ ဖန်သားပြင်လော့ခ်ချခြင်းကို အစားထိုးသုံးပါ။"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ကြိုးပမ်းမှုအကြိမ်ရေ များလွန်းသည်။ ဖန်သားပြင်လော့ခ်ချခြင်းကို အစားထိုးသုံးပါ။"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ပြန်ကြိုးစားပါ"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"လက်ဗွေကို လုပ်ဆောင်နိုင်ခြင်းမရှိပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"မည်သည့် လက်ဗွေကိုမျှ ထည့်သွင်းမထားပါ။"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ဤစက်တွင် လက်ဗွေအာရုံခံကိရိယာ မရှိပါ။"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"အာရုံခံကိရိယာကို ယာယီပိတ်ထားသည်။"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ပြုပြင်ရေး ဝန်ဆောင်မှုပေးသူထံသို့ သွားပါ။"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"သင့်မျက်နှာနမူနာ ပြုလုပ်၍မရပါ။ ထပ်စမ်းကြည့်ပါ။"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"အလွန် လင်းသည်။ အလင်းလျှော့ကြည့်ပါ။"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"အလင်းရောင် အားနည်းသည်"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ဖုန်းကို အဝေးသို့ခွာပါ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ဖုန်းကို အနားသို့ပိုတိုးပါ"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ဖုန်းကို ပိုမြှင့်လိုက်ပါ"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"အက်ပ်များကို စတင်နေ"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"လုပ်ငန်းစနစ်ထည့်သွင်း၍ ပြန်လည်စတင်ရန် ပြီးပါပြီ"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ဖွင့်ပိတ်ခလုတ်ကို သင်နှိပ်ခဲ့သည် — ၎င်းက ပုံမှန်အားဖြင့် စခရင်ကို ပိတ်စေသည်။\n\nသင့်လက်ဗွေကို ထည့်သွင်းသောအခါ ဖွဖွတို့ကြည့်ပါ။"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"ဖန်သားပြင်ပိတ်ရန် တို့ပါ"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"ဖန်သားပြင် ပိတ်ရန်"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"စနစ်ထည့်သွင်းမှုရပ်ရန် ဖန်သားပြင်ပိတ်ပါ"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"ပိတ်ရန်"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"သင့်လက်ဗွေကို ဆက်၍ အတည်ပြုမလား။"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"ဖွင့်ပိတ်ခလုတ်ကို သင်နှိပ်ခဲ့သည် — ၎င်းက ပုံမှန်အားဖြင့် စခရင်ကို ပိတ်စေသည်။\n\nသင့်လက်ဗွေကို အတည်ပြုရန် ဖွဖွတို့ကြည့်ပါ။"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"စခရင် ပိတ်ရန်"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ဖုန်း"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"အထိုင်ရှိသော စပီကာများ"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"ပြင်ပစက်"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"နားကြပ်"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"စနစ်"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index e1015480d721..87e206d38972 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Bruk skjermlås"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Skriv inn skjermlåsen for å fortsette"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Trykk godt på sensoren"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Kunne ikke registrere fingeravtrykket. Prøv på nytt."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Fingeravtrykket gjenkjennes ikke. Prøv på nytt."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Rengjør fingeravtrykkssensoren og prøv igjen"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Rengjør sensoren og prøv igjen"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Trykk godt på sensoren"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Du flyttet fingeren for sakte. Prøv på nytt."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Prøv et annet fingeravtrykk"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"For lyst"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Trykk på av/på-knappen er registrert"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Prøv å justere"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Endre posisjonen til fingeren litt hver gang"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet er autentisert. Trykk på Bekreft"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Maskinvare for fingeravtrykk er ikke tilgjengelig."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan ikke konfigurere fingeravtrykk"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Tidsavbrudd for fingeravtrykk er nådd. Prøv på nytt."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigureringen av fingeravtrykk er tidsavbrutt. Prøv på nytt."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrykk-operasjonen ble avbrutt."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrykk-operasjonen ble avbrutt av brukeren."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"For mange forsøk. Prøv på nytt senere."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"For mange forsøk. Bruk skjermlås i stedet."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"For mange forsøk. Bruk skjermlås i stedet."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Prøv på nytt."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan ikke behandle fingeravtrykket. Prøv på nytt."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ingen fingeravtrykk er registrert."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Denne enheten har ikke fingeravtrykkssensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensoren er midlertidig slått av."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Gå til en reparasjonsleverandør."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Kan ikke lage ansiktsmodell. Prøv på nytt."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"For lyst. Prøv svakere belysning."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Ikke nok lys"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Flytt telefonen lengre unna"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Flytt telefonen nærmere"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Flytt telefonen høyere"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Starter apper."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Ferdigstiller oppstart."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du har trykket på av/på-knappen – dette slår vanligvis av skjermen.\n\nPrøv å trykke lett mens du konfigurerer fingeravtrykket ditt."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Trykk for å slå av skjermen"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Slå av skjermen"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Slå av skjerm for å stoppe konfigurering"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Slå av"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Fortsett bekreftelse av fingeravtrykket?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du har trykket på av/på-knappen – dette slår vanligvis av skjermen.\n\nPrøv å trykke lett for å bekrefte fingeravtrykket ditt."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Slå av skjermen"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Google TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dokkhøyttalere"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Ekstern enhet"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Hodetelefoner"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"System"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index b2bf381a3753..5df7009329a0 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"स्क्रिन लक प्रयोग गर्नुहोस्"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"जारी राख्न आफ्नो स्क्रिन लक हाल्नुहोस्"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"सेन्सरमा बेसरी थिच्नुहोस्"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"फिंगरप्रिन्ट प्रशोधन गर्न सकिएन। कृपया फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"फिंगरप्रिन्ट पहिचान गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"फिंगरप्रिन्ट सेन्सर सफा गरेर फेरि प्रयास गर्नुहोस्"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"सेन्सर सफा गरेर फेरि प्रयास गर्नुहोस्"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"सेन्सरमा बेसरी थिच्नुहोस्"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"औंला निकै सुस्त सारियो। कृपया फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"अर्को फिंगरप्रिन्ट प्रयोग गरी हेर्नुहोस्"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ज्यादै उज्यालो छ"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"पावर बटन प्रेस गरेको कुरा पत्ता लाग्यो"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"सेन्सरमा सही तरिकाले औँला राखेर हेर्नुहोस्"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"हरेक पटक आफ्नो औँला थोरै यताउता सार्नुहोस्"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"अनुहार प्रमाणीकरण गरियो, कृपया पुष्टि गर्नुहोस् थिच्नुहोस्"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"औँठाछाप हार्डवेयर उपलब्ध छैन।"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"फिंगरप्रिन्ट सेटअप गर्न सकिएन"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"फिंगरप्रिन्ट समय सकिएको छ। फेरि प्रयास गर्नुहोस्।"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"फिंगरप्रिन्ट सेट अप गर्ने समय सकियो। फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"फिंगरप्रिन्ट सञ्चालन रद्द गरियो।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"प्रयोगकर्ताले फिंगरप्रिन्टसम्बन्धी कारबाही रद्द गर्नुभयो।"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"धेरै प्रयासहरू। केहि समय पछि पुन: प्रयास गर्नुहोला"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"निकै धेरै पटक प्रयास गरिसकिएको छ। बरु स्क्रिन लक प्रयोग गर्नुहोस्।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"निकै धेरै पटक प्रयास गरिसकिएको छ। बरु स्क्रिन लक प्रयोग गर्नुहोस्।"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"पुन: प्रयास गर्नुहोला।"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"फिंगरप्रिन्ट पहिचान गर्ने प्रक्रिया अघि बढाउन सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"कुनै पनि फिंगरप्रिन्ट दर्ता गरिएको छैन।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"यो डिभाइसमा कुनै पनि फिंगरप्रिन्ट सेन्सर छैन।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"केही समयका लागि सेन्सर असक्षम पारियो।"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"फिंगरप्रिन्ट सेन्सर मर्मत गर्ने सेवा प्रदायक कम्पनीमा सम्पर्क गर्नुहोस्।"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"तपाईंको फेस मोडेल सिर्जना गर्न सकिएन। फेरि प्रयास गर्नुहोस्।"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ज्यादै चम्किलो। अझ मधुरो प्रकाश प्रयोग गरी हेर्नु…"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"पर्याप्त उज्यालो छैन"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"फोन अझै पर सार्नुहोस्"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"फोन अझै नजिक सार्नुहोस्"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"फोन अझ माथि उठाउनुहोस्"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"सुरुवात एपहरू।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"बुट पुरा हुँदै।"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"तपाईंले पावर बटन थिच्नुभयो — सामान्यतया स्क्रिन अफ गर्न यो बटन थिच्ने गरिन्छ।\n\nफिंगरप्रिन्ट सेटअप भइन्जेल हल्का तरिकाले यो बटन ट्याप गर्नुहोस्।"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"स्क्रिन अफ गर्न ट्याप गर्नुहोस्"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"स्क्रिन अफ गर्नुहोस्"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"सेट अप गरिसक्न स्क्रिन अफ गर्नुहोस्"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"अफ गर्नुहोस्"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"फिंगरप्रिन्ट पुष्टि गर्ने क्रम जारी राख्ने हो?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"तपाईंले पावर बटन थिच्नुभयो — सामान्यतया स्क्रिन अफ गर्न यो बटन थिच्ने गरिन्छ।\n\nतपाईं आफ्नो फिंगरप्रिन्ट पुष्टि गर्न चाहनुहुन्छ भने हल्का तरिकाले यो बटन ट्याप गरी हेर्नुहोस्।"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"स्क्रिन अफ गर्नुहोस्"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"टिभी"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"फोन"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"डक स्पिकरहरू"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"बााह्य डिभाइस"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"हेडफोनहरू"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"प्रणाली"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 9811e2701b36..f89bc78de88c 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Schermvergrendeling gebruiken"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Voer je schermvergrendeling in om door te gaan"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Druk stevig op de sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Vingerafdruk niet herkend. Probeer het opnieuw."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Reinig de vingerafdruksensor en probeer het opnieuw"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Reinig de sensor en probeer het opnieuw"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Druk stevig op de sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Vinger te langzaam bewogen. Probeer het opnieuw."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Probeer een andere vingerafdruk"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Te veel licht"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Druk op aan/uit-knop waargenomen"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Verplaats je vinger"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Verander de positie van je vinger steeds een beetje"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Gezicht geverifieerd. Druk op Bevestigen."</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware voor vingerafdruk niet beschikbaar."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Kan vingerafdruk niet instellen"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Time-out bereikt voor vingerafdruk. Probeer het opnieuw."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Time-out bij instellen van vingerafdruk. Probeer het opnieuw."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Vingerafdrukbewerking geannuleerd."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Vingerafdrukverificatie geannuleerd door gebruiker."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Te veel pogingen. Probeer het later opnieuw."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Te veel pogingen. Gebruik in plaats daarvan de schermvergrendeling."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Te veel pogingen. Gebruik in plaats daarvan de schermvergrendeling."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Probeer het opnieuw."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Kan vingerafdruk niet verwerken. Probeer het opnieuw."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Geen vingerafdrukken geregistreerd."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dit apparaat heeft geen vingerafdruksensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor staat tijdelijk uit."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Ga naar een reparateur."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Kan gezichtsmodel niet maken. Probeer het opnieuw."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Overbelicht. Probeer een minder felle belichting."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Te weinig licht"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Houd de telefoon verder weg"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Houd de telefoon dichterbij"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Houd de telefoon hoger"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Apps starten."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Opstarten afronden."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Je hebt op de aan/uit-knop gedrukt. Zo zet je meestal het scherm uit.\n\nRaak de knop voorzichtig aan terwijl je je vingerafdruk instelt."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tik om het scherm uit te zetten"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Scherm uitzetten"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Zet het scherm uit om het instellen te beëindigen"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Uitzetten"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Doorgaan met verificatie van je vingerafdruk?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Je hebt op de aan/uit-knop gedrukt. Zo zet je meestal het scherm uit.\n\nRaak de knop voorzichtig aan om je vingerafdruk te verifiëren."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Scherm uitzetten"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Tv"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefoon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dockluidsprekers"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Extern apparaat"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Hoofdtelefoon"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Systeem"</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index d3cbaeca1936..9b38ea812d75 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ସ୍କ୍ରିନ୍ ଲକ୍ ବ୍ୟବହାର କରନ୍ତୁ"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ଜାରି ରଖିବାକୁ ଆପଣଙ୍କ ସ୍କ୍ରିନ୍ ଲକ୍ ଏଣ୍ଟର୍ କରନ୍ତୁ"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"ସେନ୍ସର ଉପରେ ଦୃଢ଼ ଭାବେ ଦବାନ୍ତୁ"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ଟିପଚିହ୍ନ ପ୍ରୋସେସ୍‍ କରାଯାଇପାରିଲା ନାହିଁ। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"ଟିପଚିହ୍ନକୁ ଚିହ୍ନଟ କରାଯାଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ପରିଷ୍କାର କରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"ସେନ୍ସରକୁ ପରିଷ୍କାର କରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"ସେନ୍ସର ଉପରେ ଦୃଢ଼ ଭାବେ ଦବାନ୍ତୁ"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"ଆଙ୍ଗୁଠି ଖୁବ୍‍ ଧୀରେ ନିଆଗଲା। ଦୟାକରି ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ଅନ୍ୟ ଏକ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରି ଦେଖନ୍ତୁ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ବହୁତ ଉଜ୍ଜ୍ୱଳ"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"ପାୱାର ବଟନ ଦବାଇବା ଚିହ୍ନଟ କରାଯାଇଛି"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ଆଡଜଷ୍ଟ କରି ଦେଖନ୍ତୁ"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ପ୍ରତି ଥର ଆପଣଙ୍କ ଆଙ୍ଗୁଠିର ସ୍ଥାନ ସାମାନ୍ୟ ପରିବର୍ତ୍ତନ କରନ୍ତୁ"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ମୁହଁ ଚିହ୍ନଟ ହୋଇଛି, ଦୟାକରି ସୁନିଶ୍ଚିତ ଦବାନ୍ତୁ"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ଟିପଚିହ୍ନ ହାର୍ଡୱେର୍‍ ଉପଲବ୍ଧ ନାହିଁ।"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ଟିପଚିହ୍ନକୁ ସେଟ୍ ଅପ୍ କରାଯାଇପାରିବ ନାହିଁ"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"ଟିପଚିହ୍ନର ସମୟ ଶେଷ ହେଲା । ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ଫିଙ୍ଗରପ୍ରିଣ୍ଟ ସେଟଅପର ସମୟସୀମା ସମାପ୍ତ ହୋଇଯାଇଛି। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରାଗଲା।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ଉପଯୋଗକର୍ତ୍ତା ଟିପଚିହ୍ନ କାର୍ଯ୍ୟ ବାତିଲ୍ କରିଛନ୍ତି।"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"ବହୁତ ପ୍ରୟାସ କରାଗଲା। ପରେ ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ଅନେକଗୁଡ଼ିଏ ପ୍ରଚେଷ୍ଟା। ଏହା ପରିବର୍ତ୍ତେ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ଅନେକଗୁଡ଼ିଏ ପ୍ରଚେଷ୍ଟା। ଏହା ପରିବର୍ତ୍ତେ ସ୍କ୍ରିନ ଲକ ବ୍ୟବହାର କରନ୍ତୁ।"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ଟିପଚିହ୍ନକୁ ପ୍ରକ୍ରିୟାନ୍ୱିତ କରାଯାଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"କୌଣସି ଆଙ୍ଗୁଠି ଚିହ୍ନ ପଞ୍ଜୀକୃତ ହୋଇନାହିଁ।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ଏହି ଡିଭାଇସ୍‌ରେ ଟିପଚିହ୍ନ ସେନ୍‍ସର୍ ନାହିଁ।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ସେନ୍ସରକୁ ଅସ୍ଥାୟୀ ଭାବେ ଅକ୍ଷମ କରାଯାଇଛି।"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ଏକ ମରାମତି କେନ୍ଦ୍ରକୁ ଭିଜିଟ୍ କରନ୍ତୁ।"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ଫେସର ମଡେଲ ତିଆରି କରାଯାଇପାରିବ ନାହିଁ। ପୁଣି ଚେଷ୍ଟା କର।"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ଅତ୍ୟଧିକ ଉଜ୍ଵଳ। କମ୍ ଉଜ୍ବଳକରଣରେ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"ଯଥେଷ୍ଟ ଆଲୋକ ନାହିଁ"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ଫୋନକୁ ଟିକେ ଦୂରକୁ ନିଅନ୍ତୁ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ଫୋନକୁ ପାଖକୁ ଆଣନ୍ତୁ"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ଫୋନକୁ ଉପରକୁ ମୁଭ କରନ୍ତୁ"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ଆପ୍‍ ଆରମ୍ଭ କରାଯାଉଛି।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ବୁଟ୍‍ ସମାପ୍ତ କରୁଛି।"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ଆପଣ ପାୱାର ବଟନ ଦବାଇଛନ୍ତି — ଏହା ସାଧାରଣତଃ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ବନ୍ଦ କରିଥାଏ।\n\nଆପଣଙ୍କ ଟିପଚିହ୍ନ ସେଟ ଅପ କରିବା ସମୟରେ ଧୀରେ ଟାପ କରିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"ସ୍କ୍ରିନ ବନ୍ଦ କରିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"ସ୍କ୍ରିନ ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"ସେଟଅପ ସମାପ୍ତ କରିବାକୁ ସ୍କ୍ରିନ ବନ୍ଦ କରନ୍ତୁ"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"ବନ୍ଦ କରନ୍ତୁ"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"ଆପଣଙ୍କ ଟିପଚିହ୍ନ ଯାଞ୍ଚ କରିବା ଜାରି ରଖିବେ?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"ଆପଣ ପାୱାର ବଟନ ଦବାଇଛନ୍ତି — ଏହା ସାଧାରଣତଃ ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ବନ୍ଦ କରିଥାଏ।\n\nଆପଣଙ୍କ ଟିପଚିହ୍ନ ଯାଞ୍ଚ କରିବା ପାଇଁ ଧୀରେ ଟାପ କରିବାକୁ ଚେଷ୍ଟା କରନ୍ତୁ।"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ସ୍କ୍ରିନ ବନ୍ଦ କରନ୍ତୁ"</string>
@@ -1273,7 +1273,7 @@
<string name="volume_ringtone" msgid="134784084629229029">"ରିଙ୍ଗର୍‌ ଭଲ୍ୟୁମ୍"</string>
<string name="volume_music" msgid="7727274216734955095">"ମିଡିଆ ଭଲ୍ୟୁମ୍‌"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"ବ୍ଲୁଟୂଥ୍‍ ମାଧ୍ୟମରେ ଚାଲୁଛି"</string>
- <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"ରିଙ୍ଗଟୋନ୍‍‍କୁ ନିରବ ଭାବେ ସେଟ୍ କରାଯାଇଛି"</string>
+ <string name="volume_music_hint_silent_ringtone_selected" msgid="1514829655029062233">"ସାଇଲେଣ୍ଟ ରିଂଟୋନ ସେଟ ହୋଇଛି"</string>
<string name="volume_call" msgid="7625321655265747433">"ଇନ୍‍-କଲ୍‍ ଭଲ୍ୟୁମ୍‌"</string>
<string name="volume_bluetooth_call" msgid="2930204618610115061">"ବ୍ଲୁଟୂଥ୍‍ ଇନ୍-କଲ୍ ଭଲ୍ୟୁମ୍‌"</string>
<string name="volume_alarm" msgid="4486241060751798448">"ଆଲାରାମ୍ ଭଲ୍ୟୁମ୍‌"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ଫୋନ"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ଡକ୍‌ ସ୍ପିକର୍‌"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"ଏକ୍ସଟର୍ନଲ ଡିଭାଇସ"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ହେଡଫୋନ୍‍"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"ସିଷ୍ଟମ"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 2ffeef46abb9..ed8278bea8f1 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰੋ"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ਜਾਰੀ ਰੱਖਣ ਲਈ ਆਪਣਾ ਸਕ੍ਰੀਨ ਲਾਕ ਦਾਖਲ ਕਰੋ"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"ਸੈਂਸਰ ਨੂੰ ਜ਼ੋਰ ਨਾਲ ਦਬਾਓ"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ਫਿੰਗਰਪ੍ਰਿੰਟ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਹੋ ਸਕੀ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪਛਾਣ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਾਫ਼ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"ਸੈਂਸਰ ਨੂੰ ਸਾਫ਼ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"ਸੈਂਸਰ ਨੂੰ ਜ਼ੋਰ ਨਾਲ ਦਬਾਓ"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"ਉਂਗਲ ਕਾਫ਼ੀ ਹੌਲੀ ਮੂਵ ਹੋਈ। ਕਿਰਪਾ ਕਰਕੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ਕੋਈ ਹੋਰ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤ ਕੇ ਦੇਖੋ"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"ਬਹੁਤ ਜ਼ਿਆਦਾ ਚਮਕ"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"ਪਾਵਰ ਬਟਨ ਦਬਾਏ ਜਾਣ ਦਾ ਪਤਾ ਲੱਗਿਆ ਹੈ"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ਵਿਵਸਥਿਤ ਕਰਕੇ ਦੇਖੋ"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ਹਰ ਵਾਰ ਆਪਣੀ ਉਂਗਲ ਨੂੰ ਥੋੜ੍ਹਾ ਹਿਲਾਓ"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ਚਿਹਰਾ ਪੁਸ਼ਟੀਕਰਨ, ਕਿਰਪਾ ਕਰਕੇ \'ਪੁਸ਼ਟੀ ਕਰੋ\' ਦਬਾਓ"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਹਾਰਡਵੇਅਰ ਉਪਲਬਧ ਨਹੀਂ ਹੈ।"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸਮਾਂ ਸਮਾਪਤ ਹੋ ਗਿਆ ਹੈ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਨ ਲਈ ਸਮਾਂ ਸਮਾਪਤ ਹੋਇਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ਫਿੰਗਰਪ੍ਰਿੰਟ ਓਪਰੇਸ਼ਨ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਰੱਦ ਕੀਤਾ ਗਿਆ।"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ. ਬਾਅਦ ਵਿੱਚ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸਦੀ ਬਜਾਏ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ।"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ਬਹੁਤ ਸਾਰੀਆਂ ਕੋਸ਼ਿਸ਼ਾਂ। ਇਸਦੀ ਬਜਾਏ ਸਕ੍ਰੀਨ ਲਾਕ ਵਰਤੋ।"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ਫਿੰਗਰਪ੍ਰਿੰਟ \'ਤੇ ਪ੍ਰਕਿਰਿਆ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ਕੋਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਰਜ ਨਹੀਂ ਕੀਤੇ ਗਏ।"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ਇਸ ਡੀਵਾਈਸ ਵਿੱਚ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨਹੀਂ ਹੈ।"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ਸੈਂਸਰ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ਮੁਰੰਮਤ ਪ੍ਰਦਾਨਕ \'ਤੇ ਜਾਓ।"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ਤੁਹਾਡੇ ਚਿਹਰੇ ਦਾ ਮਾਡਲ ਨਹੀਂ ਬਣਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"ਬਹੁਤ ਜ਼ਿਆਦਾ ਚਮਕ। ਹਲਕੀ ਚਮਕ ਵਰਤ ਕੇ ਦੇਖੋ।"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"ਲੋੜੀਂਦੀ ਰੋਸ਼ਨੀ ਨਹੀਂ ਹੈ"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ਫ਼ੋਨ ਨੂੰ ਦੂਰ ਲਿਜਾਓ"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ਫ਼ੋਨ ਨੇੜੇ ਲਿਜਾਓ"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ਫ਼ੋਨ ਨੂੰ ਥੋੜ੍ਹਾ ਉੱਤੇ ਲਿਜਾਓ"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ਐਪਸ ਚਾਲੂ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ਬੂਟ ਪੂਰਾ ਕਰ ਰਿਹਾ ਹੈ।"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ਤੁਸੀਂ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦਬਾਇਆ ਹੈ — ਇਹ ਆਮ ਤੌਰ \'ਤੇ ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰ ਦਿੰਦਾ ਹੈ।\n\nਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦਾ ਸੈੱਟਅੱਪ ਕਰਦੇ ਸਮੇਂ ਹਲਕਾ ਜਿਹਾ ਟੈਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"ਸਕ੍ਰੀਨ ਬੰਦ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"ਸਕ੍ਰੀਨ ਬੰਦ ਕਰੋ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"ਸੈੱਟਅੱਪ ਸਮਾਪਤ ਕਰਨ ਲਈ, ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰੋ"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"ਬੰਦ ਕਰੋ"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"ਕੀ ਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨਾ ਜਾਰੀ ਰੱਖਣਾ ਹੈ?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"ਤੁਸੀਂ ਪਾਵਰ ਬਟਨ ਨੂੰ ਦਬਾਇਆ ਹੈ — ਇਹ ਆਮ ਤੌਰ \'ਤੇ ਸਕ੍ਰੀਨ ਨੂੰ ਬੰਦ ਕਰ ਦਿੰਦਾ ਹੈ।\n\nਆਪਣੇ ਫਿੰਗਰਪ੍ਰਿੰਟ ਦੀ ਪੁਸ਼ਟੀ ਕਰਨ ਲਈ ਹਲਕਾ ਜਿਹਾ ਟੈਪ ਕਰਨ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ਸਕ੍ਰੀਨ ਬੰਦ ਕਰੋ"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ਫ਼ੋਨ ਕਰੋ"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ਡੌਕ ਸਪੀਕਰਸ"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"ਬਾਹਰੀ ਡੀਵਾਈਸ"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ਹੈੱਡਫ਼ੋਨ"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"ਸਿਸਟਮ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 88b54b0671e9..a3fbbadc6cb6 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -585,13 +585,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Używaj blokady ekranu"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Użyj blokady ekranu, aby kontynuować"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Mocno naciśnij czujnik"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Nie udało się przetworzyć odcisku palca. Spróbuj ponownie."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Nie rozpoznaję odcisku palca. Spróbuj ponownie."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Wyczyść czytnik linii papilarnych i spróbuj ponownie"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Wyczyść czujnik i spróbuj ponownie"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Mocno naciśnij czujnik"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Palec został obrócony zbyt wolno. Spróbuj ponownie."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Użyj odcisku innego palca"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Zbyt jasno"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Wykryto naciśnięcie przycisku zasilania"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Popraw"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Za każdym razem lekko zmieniaj ułożenie palca"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -603,12 +604,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Twarz rozpoznana, kliknij Potwierdź"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Czytnik linii papilarnych nie jest dostępny."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nie można skonfigurować odcisku palca"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Osiągnięto limit czasu odczytu odcisków palców. Spróbuj ponownie."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Upłynął limit czasu konfiguracji odcisku palca. Spróbuj ponownie."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Odczyt odcisku palca został anulowany."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Odczyt odcisku palca został anulowany przez użytkownika."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Zbyt wiele prób. Spróbuj ponownie później."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Zbyt wiele prób. Użyj blokady ekranu."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Zbyt wiele prób. Użyj blokady ekranu."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Spróbuj ponownie."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nie udało się przetworzyć odcisku palca. Spróbuj ponownie."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nie zarejestrowano odcisków palców."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"To urządzenie nie jest wyposażone w czytnik linii papilarnych."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Czujnik jest tymczasowo wyłączony."</string>
@@ -636,8 +637,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Odwiedź serwis."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Nie można utworzyć modelu twarzy. Spróbuj ponownie."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Zbyt jasno. Spróbuj przy słabszym świetle."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Za mało światła"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Odsuń telefon"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Przybliż telefon"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Przesuń telefon wyżej"</string>
@@ -1251,8 +1251,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Uruchamianie aplikacji."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Kończenie uruchamiania."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Naciśnięto przycisk zasilania — zwykle powoduje to wyłączenie ekranu.\n\nKlikaj delikatnie podczas konfigurowania odcisku palca."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Kliknij, aby wyłączyć ekran"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Wyłącz ekran"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Aby zakończyć konfigurację, wyłącz ekran"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Wyłącz"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Kontynuować weryfikację odcisku palca?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Naciśnięto przycisk zasilania — zwykle powoduje to wyłączenie ekranu.\n\nKliknij delikatnie, aby zweryfikować odcisk palca."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Wyłącz ekran"</string>
@@ -1613,7 +1613,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Telewizor"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Głośniki stacji dokującej"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Urządzenie zewnętrzne"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Słuchawki"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"System"</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 7d5721a893e0..55f5bcfad9ab 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar bloqueio de tela"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Insira seu bloqueio de tela para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Pressione o sensor com firmeza"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Não foi possível processar a impressão digital. Tente novamente."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Não foi possível reconhecer a impressão digital. Tente de novo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpe o sensor de impressão digital e tente novamente"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpe o sensor e tente novamente"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Pressione o sensor com firmeza"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"O movimento do dedo está muito lento. Tente novamente."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Use outra impressão digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Claro demais"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"O botão liga/desliga foi pressionado"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Ajuste a posição do dedo"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Mude a posição do dedo ligeiramente a cada momento"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impressão digital não disponível."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não foi possível configurar a impressão digital"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Tempo máximo para captura da impressão digital atingido. Tente novamente."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"A configuração da impressão digital expirou. Tente de novo."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo usuário."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Excesso de tentativas. Tente novamente mais tarde."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Excesso de tentativas. Use o bloqueio de tela."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tente novamente."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não foi possível processar a impressão digital. Tente de novo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Entre em contato com uma assistência técnica."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Falha ao criar o modelo de rosto. Tente de novo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Muito iluminado. Diminua a iluminação."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Não há luz suficiente"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Afaste o smartphone"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Aproxime o smartphone do seu rosto"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mova o smartphone para cima"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Concluindo a inicialização."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela durante a configuração da impressão digital."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Toque para desligar a tela"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Desligar a tela"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Desligue a tela para encerrar a configuração"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desativar"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a verificação da digital?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela para verificar sua impressão digital."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar a tela"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Alto-falantes na base"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Dispositivo externo"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Fones de ouvido"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistema"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 1fe0db7a55c6..2938b96663aa 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -53,7 +53,7 @@
<string name="imei" msgid="2157082351232630390">"IMEI"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
<string name="ClipMmi" msgid="4110549342447630629">"ID do Autor da Chamada"</string>
- <string name="ClirMmi" msgid="6752346475055446417">"Ocultar identificação do autor da chamada efetuada"</string>
+ <string name="ClirMmi" msgid="6752346475055446417">"Ocultar identificação do autor da chamada feita"</string>
<string name="ColpMmi" msgid="4736462893284419302">"ID de linha ligada"</string>
<string name="ColrMmi" msgid="5889782479745764278">"Restrição de ID de linha ligada"</string>
<string name="CfMmi" msgid="8390012691099787178">"Encaminhamento de chamadas"</string>
@@ -350,7 +350,7 @@
<string name="permlab_uninstall_shortcut" msgid="295263654781900390">"desinstalar atalhos"</string>
<string name="permdesc_uninstall_shortcut" msgid="1924735350988629188">"Permite que a app remova atalhos do Ecrã principal sem a intervenção do utilizador."</string>
<string name="permlab_processOutgoingCalls" msgid="4075056020714266558">"redirecionar as chamadas efetuadas"</string>
- <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Permite que a app veja o número que é marcado durante uma chamada efetuada, com a opção de redirecionar a chamada para um número diferente ou terminar a chamada."</string>
+ <string name="permdesc_processOutgoingCalls" msgid="7833149750590606334">"Permite que a app veja o número que é marcado durante uma chamada feita, com a opção de redirecionar a chamada para um número diferente ou terminar a chamada."</string>
<string name="permlab_answerPhoneCalls" msgid="4131324833663725855">"atender chamadas telefónicas"</string>
<string name="permdesc_answerPhoneCalls" msgid="894386681983116838">"Permite que a app atenda chamadas recebidas."</string>
<string name="permlab_receiveSms" msgid="505961632050451881">"receber mensagens de texto (SMS)"</string>
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Utilizar o bloqueio de ecrã"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Introduza o bloqueio de ecrã para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Prima firmemente o sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Não foi possível processar a impressão digital. Tente novamente."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Não é possível reconhecer a impressão digital. Tente novamente."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpe o sensor de impressões digitais e tente novamente"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpe o sensor e tente novamente"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Prima firmemente o sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Moveu o dedo demasiado lentamente. Tente novamente."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Experimente outra impressão digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Está demasiado claro"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Detetou-se que o botão ligar/desligar foi premido"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Experimente ajustar"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Altere a posição do seu dedo ligeiramente de cada vez"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado. Prima Confirmar."</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impressão digital não disponível."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não é possível configurar a impressão digital"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Foi atingido o limite de tempo da impressão digital. Tente novamente."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"A configuração da impressão digital expirou. Tente novamente."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo utilizador."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Demasiadas tentativas. Tente novamente mais tarde."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Demasiadas tentativas. Em alternativa, use o bloqueio de ecrã."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Demasiadas tentativas. Em alternativa, use o bloqueio de ecrã."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tente novamente."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não é possível processar a impressão digital. Tente novamente."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem sensor de impressões digitais."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor temporariamente desativado."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Visite um fornecedor de serviços de reparação."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Impossível criar modelo de rosto. Tente novamente."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Demasiado clara. Experimente uma luz mais suave."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Não há luz suficiente"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Afaste ainda mais o telemóvel"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Aproxime o telemóvel do rosto"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mova o telemóvel mais para cima"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"A iniciar aplicações"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"A concluir o arranque."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente ao configurar a sua impressão digital."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Toque para desligar o ecrã"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Desligar ecrã"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Termine a configuração ao desligar ecrã"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desativar"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a validar a impressão digital?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente para validar a sua impressão digital."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar ecrã"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telemóvel"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Altif. estação ancoragem"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Dispositivo externo"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Auscultadores"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistema"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 7d5721a893e0..55f5bcfad9ab 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Usar bloqueio de tela"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Insira seu bloqueio de tela para continuar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Pressione o sensor com firmeza"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Não foi possível processar a impressão digital. Tente novamente."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Não foi possível reconhecer a impressão digital. Tente de novo."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Limpe o sensor de impressão digital e tente novamente"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Limpe o sensor e tente novamente"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Pressione o sensor com firmeza"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"O movimento do dedo está muito lento. Tente novamente."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Use outra impressão digital"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Claro demais"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"O botão liga/desliga foi pressionado"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Ajuste a posição do dedo"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Mude a posição do dedo ligeiramente a cada momento"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Rosto autenticado, pressione \"Confirmar\""</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware de impressão digital não disponível."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Não foi possível configurar a impressão digital"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Tempo máximo para captura da impressão digital atingido. Tente novamente."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"A configuração da impressão digital expirou. Tente de novo."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operação de impressão digital cancelada."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operação de impressão digital cancelada pelo usuário."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Excesso de tentativas. Tente novamente mais tarde."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Excesso de tentativas. Use o bloqueio de tela."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Excesso de tentativas. Use o bloqueio de tela."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tente novamente."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Não foi possível processar a impressão digital. Tente de novo."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nenhuma impressão digital registrada."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Este dispositivo não tem um sensor de impressão digital."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor desativado temporariamente."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Entre em contato com uma assistência técnica."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Falha ao criar o modelo de rosto. Tente de novo."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Muito iluminado. Diminua a iluminação."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Não há luz suficiente"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Afaste o smartphone"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Aproxime o smartphone do seu rosto"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Mova o smartphone para cima"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Iniciando apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Concluindo a inicialização."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela durante a configuração da impressão digital."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Toque para desligar a tela"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Desligar a tela"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Desligue a tela para encerrar a configuração"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desativar"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a verificação da digital?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Você pressionou o botão liga/desliga. Normalmente, essa ação desliga a tela.\n\nToque levemente na tela para verificar sua impressão digital."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar a tela"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefone"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Alto-falantes na base"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Dispositivo externo"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Fones de ouvido"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistema"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 27b04d6cf662..791958b12df8 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -29,7 +29,7 @@
<string name="defaultMsisdnAlphaTag" msgid="2285034592902077488">"MSISDN1"</string>
<string name="mmiError" msgid="2862759606579822246">"Problemă de conexiune sau cod MMI nevalid."</string>
<string name="mmiFdnError" msgid="3975490266767565852">"Operația este limitată la numerele cu apelări restricționate."</string>
- <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"Nu puteți schimba setările de redirecționare a apelurilor de pe telefon când sunteți în roaming."</string>
+ <string name="mmiErrorWhileRoaming" msgid="1204173664713870114">"Nu poți schimba setările de redirecționare a apelurilor de pe telefon când ești în roaming."</string>
<string name="serviceEnabled" msgid="7549025003394765639">"Serviciul a fost activat."</string>
<string name="serviceEnabledFor" msgid="1463104778656711613">"Serviciul a fost activat pentru:"</string>
<string name="serviceDisabled" msgid="641878791205871379">"Serviciul a fost dezactivat."</string>
@@ -40,11 +40,11 @@
<string name="badPin" msgid="888372071306274355">"Codul PIN vechi introdus nu este corect."</string>
<string name="badPuk" msgid="4232069163733147376">"Codul PUK introdus nu este corect."</string>
<string name="mismatchPin" msgid="2929611853228707473">"Codurile PIN introduse nu se potrivesc."</string>
- <string name="invalidPin" msgid="7542498253319440408">"Introduceți un cod PIN alcătuit din 4 până la 8 cifre."</string>
- <string name="invalidPuk" msgid="8831151490931907083">"Introduceți un cod PUK care să aibă 8 cifre sau mai mult."</string>
- <string name="needPuk" msgid="7321876090152422918">"Cardul SIM este blocat cu codul PUK. Introduceți codul PUK pentru a-l debloca."</string>
- <string name="needPuk2" msgid="7032612093451537186">"Introduceți codul PUK2 pentru a debloca cardul SIM."</string>
- <string name="enablePin" msgid="2543771964137091212">"Operațiunea nu a reușit. Activați blocarea cardului SIM/RUIM."</string>
+ <string name="invalidPin" msgid="7542498253319440408">"Introdu un cod PIN alcătuit din 4 până la 8 cifre."</string>
+ <string name="invalidPuk" msgid="8831151490931907083">"Introdu un cod PUK care să aibă 8 cifre sau mai mult."</string>
+ <string name="needPuk" msgid="7321876090152422918">"Cardul SIM este blocat cu codul PUK. Introdu codul PUK pentru a-l debloca."</string>
+ <string name="needPuk2" msgid="7032612093451537186">"Introdu codul PUK2 pentru a debloca cardul SIM."</string>
+ <string name="enablePin" msgid="2543771964137091212">"Operațiunea nu a reușit. Activează blocarea cardului SIM/RUIM."</string>
<plurals name="pinpuk_attempts" formatted="false" msgid="1619867269012213584">
<item quantity="few">V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> încercări până la blocarea cardului SIM.</item>
<item quantity="other">V-au mai rămas <xliff:g id="NUMBER_1">%d</xliff:g> de încercări până la blocarea cardului SIM.</item>
@@ -53,7 +53,7 @@
<string name="imei" msgid="2157082351232630390">"IMEI"</string>
<string name="meid" msgid="3291227361605924674">"MEID"</string>
<string name="ClipMmi" msgid="4110549342447630629">"ID apelant de primire"</string>
- <string name="ClirMmi" msgid="6752346475055446417">"Ascundeți ID-ul apelantului"</string>
+ <string name="ClirMmi" msgid="6752346475055446417">"Ascunde ID-ul apelantului"</string>
<string name="ColpMmi" msgid="4736462893284419302">"ID-ul liniei conectate"</string>
<string name="ColrMmi" msgid="5889782479745764278">"Restricționarea ID-ului liniei conectate"</string>
<string name="CfMmi" msgid="8390012691099787178">"Redirecționarea apelurilor"</string>
@@ -66,13 +66,13 @@
<string name="ThreeWCMmi" msgid="2436550866139999411">"Apelare de tip conferință"</string>
<string name="RuacMmi" msgid="1876047385848991110">"Respingere apeluri supărătoare nedorite"</string>
<string name="CndMmi" msgid="185136449405618437">"Se apelează serviciul de furnizare a numerelor"</string>
- <string name="DndMmi" msgid="8797375819689129800">"Nu deranjați"</string>
+ <string name="DndMmi" msgid="8797375819689129800">"Nu deranja"</string>
<string name="CLIRDefaultOnNextCallOn" msgid="4511621022859867988">"ID-ul apelantului este restricționat în mod prestabilit. Apelul următor: restricționat"</string>
<string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID-ul apelantului este restricționat în mod prestabilit. Apelul următor: nerestricționat"</string>
<string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: Restricționat."</string>
<string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID-ul apelantului este nerestricționat în mod prestabilit. Apelul următor: nerestricționat"</string>
<string name="serviceNotProvisioned" msgid="8289333510236766193">"Nu se asigură accesul la acest serviciu."</string>
- <string name="CLIRPermanent" msgid="166443681876381118">"Nu puteți să modificați setarea pentru ID-ul apelantului."</string>
+ <string name="CLIRPermanent" msgid="166443681876381118">"Nu poți modifica setarea pentru ID-ul apelantului."</string>
<string name="RestrictedOnDataTitle" msgid="1500576417268169774">"Fără serviciu de date mobile"</string>
<string name="RestrictedOnEmergencyTitle" msgid="2852916906106191866">"Apelurile de urgență nu sunt disponibile"</string>
<string name="RestrictedOnNormalTitle" msgid="7009474589746551737">"Fără servicii vocale"</string>
@@ -120,7 +120,7 @@
<string name="roamingTextSearching" msgid="5323235489657753486">"Se caută serviciul"</string>
<string name="wfcRegErrorTitle" msgid="3193072971584858020">"Nu s-a putut configura apelarea prin Wi-Fi"</string>
<string-array name="wfcOperatorErrorAlertMessages">
- <item msgid="468830943567116703">"Pentru a efectua apeluri și a trimite mesaje prin Wi-Fi, mai întâi solicitați configurarea acestui serviciu la operator. Apoi, activați din nou apelarea prin Wi-Fi din Setări. (Cod de eroare: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
+ <item msgid="468830943567116703">"Pentru a face apeluri și a trimite mesaje prin Wi-Fi, mai întâi solicită configurarea acestui serviciu la operator. Apoi, activează din nou apelarea prin Wi-Fi din Setări. (Cod de eroare: <xliff:g id="CODE">%1$s</xliff:g>)"</item>
</string-array>
<string-array name="wfcOperatorErrorNotificationMessages">
<item msgid="4795145070505729156">"A apărut o problemă la înregistrarea apelării prin Wi‑Fi la operatorul dvs.: <xliff:g id="CODE">%1$s</xliff:g>"</item>
@@ -139,7 +139,7 @@
<string name="wfcSpnFormat_wifi_calling_wo_hyphen" msgid="7178561009225028264">"Apelare prin Wi-Fi"</string>
<string name="wfcSpnFormat_vowifi" msgid="8371335230890725606">"VoWifi"</string>
<string name="wifi_calling_off_summary" msgid="5626710010766902560">"Dezactivată"</string>
- <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Apelați prin Wi-Fi"</string>
+ <string name="wfc_mode_wifi_preferred_summary" msgid="1035175836270943089">"Apelează prin Wi-Fi"</string>
<string name="wfc_mode_cellular_preferred_summary" msgid="4958965609212575619">"Apelați prin rețeaua mobilă"</string>
<string name="wfc_mode_wifi_only_summary" msgid="104951993894678665">"Numai Wi-Fi"</string>
<!-- no translation found for crossSimFormat_spn (9125246077491634262) -->
@@ -159,7 +159,7 @@
<string name="httpErrorAuth" msgid="469553140922938968">"Nu s-a realizat autentificarea."</string>
<string name="httpErrorProxyAuth" msgid="7229662162030113406">"Autentificarea prin intermediul serverului proxy nu a reușit."</string>
<string name="httpErrorConnect" msgid="3295081579893205617">"Nu s-a putut stabili conexiunea cu serverul."</string>
- <string name="httpErrorIO" msgid="3860318696166314490">"Nu s-a putut efectua comunicarea cu serverul. Încercați din nou mai târziu."</string>
+ <string name="httpErrorIO" msgid="3860318696166314490">"Nu s-a putut efectua comunicarea cu serverul. Încearcă din nou mai târziu."</string>
<string name="httpErrorTimeout" msgid="7446272815190334204">"Conexiunea la server a expirat."</string>
<string name="httpErrorRedirectLoop" msgid="8455757777509512098">"Pagina conține prea multe redirecționări de server."</string>
<string name="httpErrorUnsupportedScheme" msgid="2664108769858966374">"Protocolul nu este acceptat."</string>
@@ -167,18 +167,18 @@
<string name="httpErrorBadUrl" msgid="754447723314832538">"Pagina nu a putut fi deschisă, deoarece adresa URL nu este validă."</string>
<string name="httpErrorFile" msgid="3400658466057744084">"Fișierul nu a putut fi accesat."</string>
<string name="httpErrorFileNotFound" msgid="5191433324871147386">"Nu s-a putut găsi fișierul solicitat."</string>
- <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"Există prea multe solicitări în curs de procesare. Încercați din nou mai târziu."</string>
+ <string name="httpErrorTooManyRequests" msgid="2149677715552037198">"Există prea multe solicitări în curs de procesare. Încearcă din nou mai târziu."</string>
<string name="notification_title" msgid="5783748077084481121">"Eroare de conectare pentru <xliff:g id="ACCOUNT">%1$s</xliff:g>"</string>
<string name="contentServiceSync" msgid="2341041749565687871">"Sincronizare"</string>
<string name="contentServiceSyncNotificationTitle" msgid="5766411446676388623">"Nu se poate sincroniza"</string>
<string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"Ați încercat să ștergeți prea multe <xliff:g id="CONTENT_TYPE">%s</xliff:g>."</string>
- <string name="low_memory" product="tablet" msgid="5557552311566179924">"Stocarea pe tabletă este plină. Ștergeți câteva fișiere pentru a elibera spațiu."</string>
- <string name="low_memory" product="watch" msgid="3479447988234030194">"Spațiul de stocare de pe ceas este plin! Ștergeți câteva fișiere pentru a elibera spațiu."</string>
- <string name="low_memory" product="tv" msgid="6663680413790323318">"Spațiul de stocare de pe dispozitivul Android TV este plin. Ștergeți câteva fișiere pentru a elibera spațiu."</string>
- <string name="low_memory" product="default" msgid="2539532364144025569">"Stocarea pe telefon este plină. Ștergeți câteva fișiere pentru a elibera spațiu."</string>
+ <string name="low_memory" product="tablet" msgid="5557552311566179924">"Stocarea pe tabletă este plină. Șterge câteva fișiere pentru a elibera spațiu."</string>
+ <string name="low_memory" product="watch" msgid="3479447988234030194">"Spațiul de stocare de pe ceas este plin! Șterge câteva fișiere pentru a elibera spațiu."</string>
+ <string name="low_memory" product="tv" msgid="6663680413790323318">"Spațiul de stocare de pe dispozitivul Android TV este plin. Șterge câteva fișiere pentru a elibera spațiu."</string>
+ <string name="low_memory" product="default" msgid="2539532364144025569">"Stocarea pe telefon este plină. Șterge câteva fișiere pentru a elibera spațiu."</string>
<string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{S-a instalat o autoritate de certificare}few{S-au instalat autorități de certificare}other{S-au instalat autorități de certificare}}"</string>
<string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"De o terță parte necunoscută"</string>
- <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"De administratorul profilului dvs. de serviciu"</string>
+ <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"De administratorul profilului de serviciu"</string>
<string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"De <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string>
<string name="work_profile_deleted" msgid="5891181538182009328">"Profilul de serviciu a fost șters"</string>
<string name="work_profile_deleted_details" msgid="3773706828364418016">"Aplicația de administrare a profilului de serviciu lipsește sau este deteriorată. Prin urmare, profilul de serviciu și datele asociate au fost șterse. Pentru asistență, contactați administratorul."</string>
@@ -188,7 +188,7 @@
<string name="network_logging_notification_title" msgid="554983187553845004">"Dispozitivul este gestionat"</string>
<string name="network_logging_notification_text" msgid="1327373071132562512">"Organizația dvs. gestionează acest dispozitiv și poate monitoriza traficul în rețea. Atingeți pentru mai multe detalii."</string>
<string name="location_changed_notification_title" msgid="3620158742816699316">"Aplicațiile vă pot accesa locația"</string>
- <string name="location_changed_notification_text" msgid="7158423339982706912">"Contactați administratorul IT pentru a afla mai multe"</string>
+ <string name="location_changed_notification_text" msgid="7158423339982706912">"Contactează administratorul IT pentru a afla mai multe"</string>
<string name="geofencing_service" msgid="3826902410740315456">"Serviciul de delimitare geografică"</string>
<string name="country_detector" msgid="7023275114706088854">"Detector de țară"</string>
<string name="location_service" msgid="2439187616018455546">"Servicii de localizare"</string>
@@ -201,19 +201,19 @@
<string name="factory_reset_warning" msgid="6858705527798047809">"Datele de pe dispozitiv vor fi șterse"</string>
<string name="factory_reset_message" msgid="2657049595153992213">"Aplicația de administrare nu poate fi utilizată. Dispozitivul va fi șters.\n\nDacă aveți întrebări, contactați administratorul organizației dvs."</string>
<string name="printing_disabled_by" msgid="3517499806528864633">"Printare dezactivată de <xliff:g id="OWNER_APP">%s</xliff:g>."</string>
- <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Activați profilul de serviciu"</string>
+ <string name="personal_apps_suspension_title" msgid="7561416677884286600">"Activează profilul de serviciu"</string>
<string name="personal_apps_suspension_text" msgid="6115455688932935597">"Aplicațiile personale sunt blocate până când activați profilul de serviciu"</string>
<string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"Aplicațiile personale vor fi blocate pe <xliff:g id="DATE">%1$s</xliff:g>, la <xliff:g id="TIME">%2$s</xliff:g>. Administratorul IT nu permite ca profilul de serviciu să fie dezactivat mai mult de <xliff:g id="NUMBER">%3$d</xliff:g> zile."</string>
- <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Activați"</string>
+ <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"Activează"</string>
<string name="me" msgid="6207584824693813140">"Eu"</string>
<string name="power_dialog" product="tablet" msgid="8333207765671417261">"Opțiuni tablet PC"</string>
<string name="power_dialog" product="tv" msgid="7792839006640933763">"Opțiuni pentru Android TV"</string>
<string name="power_dialog" product="default" msgid="1107775420270203046">"Opțiuni telefon"</string>
<string name="silent_mode" msgid="8796112363642579333">"Mod Silențios"</string>
- <string name="turn_on_radio" msgid="2961717788170634233">"Activați funcția wireless"</string>
- <string name="turn_off_radio" msgid="7222573978109933360">"Dezactivați funcția wireless"</string>
- <string name="screen_lock" msgid="2072642720826409809">"Blocați ecranul"</string>
- <string name="power_off" msgid="4111692782492232778">"Opriți"</string>
+ <string name="turn_on_radio" msgid="2961717788170634233">"Activează funcția wireless"</string>
+ <string name="turn_off_radio" msgid="7222573978109933360">"Dezactivează funcția wireless"</string>
+ <string name="screen_lock" msgid="2072642720826409809">"Blochează ecranul"</string>
+ <string name="power_off" msgid="4111692782492232778">"Oprește"</string>
<string name="silent_mode_silent" msgid="5079789070221150912">"Sonerie dezactivată"</string>
<string name="silent_mode_vibrate" msgid="8821830448369552678">"Vibrare sonerie"</string>
<string name="silent_mode_ring" msgid="6039011004781526678">"Sonerie activată"</string>
@@ -229,27 +229,27 @@
<string name="shutdown_confirm" product="watch" msgid="2977299851200240146">"Ceasul dvs. se va închide."</string>
<string name="shutdown_confirm" product="default" msgid="136816458966692315">"Telefonul dvs. se va închide."</string>
<string name="shutdown_confirm_question" msgid="796151167261608447">"Doriți să închideți?"</string>
- <string name="reboot_safemode_title" msgid="5853949122655346734">"Reporniți în modul sigur"</string>
- <string name="reboot_safemode_confirm" msgid="1658357874737219624">"Doriți să reporniți în modul sigur? Astfel vor fi dezactivate toate aplicațiile terță parte pe care le-ați instalat. Acestea vor fi restabilite când reporniți din nou."</string>
+ <string name="reboot_safemode_title" msgid="5853949122655346734">"Repornește în modul sigur"</string>
+ <string name="reboot_safemode_confirm" msgid="1658357874737219624">"Repornești în modul sigur? Astfel vor fi dezactivate toate aplicațiile terță parte instalate. Acestea vor fi restabilite când repornești dispozitivul."</string>
<string name="recent_tasks_title" msgid="8183172372995396653">"Recente"</string>
<string name="no_recent_tasks" msgid="9063946524312275906">"Nu există aplicații recente."</string>
<string name="global_actions" product="tablet" msgid="4412132498517933867">"Opțiuni tablet PC"</string>
<string name="global_actions" product="tv" msgid="3871763739487450369">"Opțiuni pentru Android TV"</string>
<string name="global_actions" product="default" msgid="6410072189971495460">"Opțiuni telefon"</string>
- <string name="global_action_lock" msgid="6949357274257655383">"Blocați ecranul"</string>
- <string name="global_action_power_off" msgid="4404936470711393203">"Opriți"</string>
+ <string name="global_action_lock" msgid="6949357274257655383">"Blochează ecranul"</string>
+ <string name="global_action_power_off" msgid="4404936470711393203">"Oprește"</string>
<string name="global_action_power_options" msgid="1185286119330160073">"Alimentare"</string>
- <string name="global_action_restart" msgid="4678451019561687074">"Reporniți"</string>
+ <string name="global_action_restart" msgid="4678451019561687074">"Repornește"</string>
<string name="global_action_emergency" msgid="1387617624177105088">"Urgență"</string>
<string name="global_action_bug_report" msgid="5127867163044170003">"Raport despre erori"</string>
- <string name="global_action_logout" msgid="6093581310002476511">"Încheiați sesiunea"</string>
+ <string name="global_action_logout" msgid="6093581310002476511">"Încheie sesiunea"</string>
<string name="global_action_screenshot" msgid="2610053466156478564">"Instantaneu"</string>
<string name="bugreport_title" msgid="8549990811777373050">"Raport de eroare"</string>
<string name="bugreport_message" msgid="5212529146119624326">"Acest raport va colecta informații despre starea actuală a dispozitivului, pentru a le trimite într-un e-mail. Aveți răbdare după pornirea raportului despre erori până când va fi gata de trimis."</string>
<string name="bugreport_option_interactive_title" msgid="7968287837902871289">"Raport interactiv"</string>
- <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Folosiți această opțiune în majoritatea situațiilor. Astfel, puteți să urmăriți progresul raportului, să introduceți mai multe detalii în privința problemei și să creați capturi de ecran. Pot fi omise unele secțiuni mai puțin folosite pentru care raportarea durează prea mult."</string>
+ <string name="bugreport_option_interactive_summary" msgid="8493795476325339542">"Folosește această opțiune în majoritatea situațiilor. Astfel, poți să urmărești progresul raportului, să introduci mai multe detalii în privința problemei și să creezi capturi de ecran. Pot fi omise unele secțiuni mai puțin folosite pentru care raportarea durează prea mult."</string>
<string name="bugreport_option_full_title" msgid="7681035745950045690">"Raport complet"</string>
- <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Folosiți această opțiune pentru a reduce la minimum interferențele cu sistemul când dispozitivul nu răspunde, funcționează prea lent sau când aveți nevoie de toate secțiunile raportului. Nu puteți să introduceți mai multe detalii sau să creați capturi de ecran suplimentare."</string>
+ <string name="bugreport_option_full_summary" msgid="1975130009258435885">"Folosește această opțiune pentru a reduce la minimum interferențele cu sistemul când dispozitivul nu răspunde, funcționează prea lent sau când ai nevoie de toate secțiunile raportului. Nu poți să introduci mai multe detalii sau să creezi capturi de ecran suplimentare."</string>
<string name="bugreport_countdown" msgid="6418620521782120755">"{count,plural, =1{Peste # secundă se va realiza o captură de ecran pentru raportul de eroare.}few{Peste # secunde se va realiza o captură de ecran pentru raportul de eroare.}other{Peste # de secunde se va realiza o captură de ecran pentru raportul de eroare.}}"</string>
<string name="bugreport_screenshot_success_toast" msgid="7986095104151473745">"S-a realizat captura de ecran a raportului de eroare"</string>
<string name="bugreport_screenshot_failure_toast" msgid="6736320861311294294">"Nu s-a realizat captura de ecran a raportului de eroare"</string>
@@ -287,16 +287,16 @@
<string name="notification_channel_accessibility_security_policy" msgid="1727787021725251912">"Folosirea accesibilității"</string>
<string name="foreground_service_app_in_background" msgid="1439289699671273555">"<xliff:g id="APP_NAME">%1$s</xliff:g> folosește bateria"</string>
<string name="foreground_service_apps_in_background" msgid="7340037176412387863">"<xliff:g id="NUMBER">%1$d</xliff:g> aplicații folosesc bateria"</string>
- <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Atingeți pentru mai multe detalii privind bateria și utilizarea datelor"</string>
+ <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Atinge pentru mai multe detalii privind bateria și utilizarea datelor"</string>
<string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string>
<string name="safeMode" msgid="8974401416068943888">"Mod sigur"</string>
<string name="android_system_label" msgid="5974767339591067210">"Sistemul Android"</string>
<string name="user_owner_label" msgid="8628726904184471211">"Comutați la profilul personal"</string>
<string name="managed_profile_label" msgid="7316778766973512382">"Comutați la profilul de serviciu"</string>
<string name="permgrouplab_contacts" msgid="4254143639307316920">"Agendă"</string>
- <string name="permgroupdesc_contacts" msgid="9163927941244182567">"acceseze persoanele de contact"</string>
+ <string name="permgroupdesc_contacts" msgid="9163927941244182567">"să acceseze agenda"</string>
<string name="permgrouplab_location" msgid="1858277002233964394">"Locație"</string>
- <string name="permgroupdesc_location" msgid="1995955142118450685">"acceseze locația acestui dispozitiv"</string>
+ <string name="permgroupdesc_location" msgid="1995955142118450685">"să acceseze locația acestui dispozitiv"</string>
<string name="permgrouplab_calendar" msgid="6426860926123033230">"Calendar"</string>
<string name="permgroupdesc_calendar" msgid="6762751063361489379">"acceseze calendarul"</string>
<string name="permgrouplab_sms" msgid="795737735126084874">"SMS"</string>
@@ -310,7 +310,7 @@
<string name="permgrouplab_microphone" msgid="2480597427667420076">"Microfon"</string>
<string name="permgroupdesc_microphone" msgid="1047786732792487722">"înregistreze sunet"</string>
<string name="permgrouplab_activityRecognition" msgid="3324466667921775766">"Activitate fizică"</string>
- <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"accesați activitatea fizică"</string>
+ <string name="permgroupdesc_activityRecognition" msgid="4725624819457670704">"să acceseze activitatea fizică"</string>
<string name="permgrouplab_camera" msgid="9090413408963547706">"Camera foto"</string>
<string name="permgroupdesc_camera" msgid="7585150538459320326">"fotografieze și să înregistreze videoclipuri"</string>
<string name="permgrouplab_nearby_devices" msgid="5529147543651181991">"Dispozitive din apropiere"</string>
@@ -318,14 +318,14 @@
<string name="permgrouplab_calllog" msgid="7926834372073550288">"Jurnale de apeluri"</string>
<string name="permgroupdesc_calllog" msgid="2026996642917801803">"să citească și să scrie jurnalul de apeluri telefonice"</string>
<string name="permgrouplab_phone" msgid="570318944091926620">"Telefon"</string>
- <string name="permgroupdesc_phone" msgid="270048070781478204">"inițieze și să gestioneze apeluri telefonice"</string>
+ <string name="permgroupdesc_phone" msgid="270048070781478204">"să inițieze și să gestioneze apeluri telefonice"</string>
<string name="permgrouplab_sensors" msgid="9134046949784064495">"Senzori corporali"</string>
- <string name="permgroupdesc_sensors" msgid="2610631290633747752">"acceseze datele de la senzori despre semnele vitale"</string>
+ <string name="permgroupdesc_sensors" msgid="2610631290633747752">"să acceseze datele de la senzori despre semnele vitale"</string>
<string name="permgrouplab_notifications" msgid="5472972361980668884">"Notificări"</string>
<string name="permgroupdesc_notifications" msgid="4608679556801506580">"să afișeze notificări"</string>
<string name="capability_title_canRetrieveWindowContent" msgid="7554282892101587296">"Analizeze conținutul ferestrei"</string>
<string name="capability_desc_canRetrieveWindowContent" msgid="6195610527625237661">"Inspectează conținutul unei ferestre cu care interacționați."</string>
- <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"Activeze funcția Explorați prin atingere"</string>
+ <string name="capability_title_canRequestTouchExploration" msgid="327598364696316213">"să activeze funcția Explorează prin atingere"</string>
<string name="capability_desc_canRequestTouchExploration" msgid="4394677060796752976">"Elementele atinse vor fi rostite cu voce tare, iar ecranul poate fi explorat utilizând gesturi."</string>
<string name="capability_title_canRequestFilterKeyEvents" msgid="2772371671541753254">"Remarce textul pe care îl introduceți"</string>
<string name="capability_desc_canRequestFilterKeyEvents" msgid="2381315802405773092">"Include date personale, cum ar fi numere ale cardurilor de credit sau parole."</string>
@@ -356,17 +356,17 @@
<string name="permlab_receiveSms" msgid="505961632050451881">"primește mesaje text (SMS)"</string>
<string name="permdesc_receiveSms" msgid="1797345626687832285">"Permite aplicației să primească și să proceseze mesaje SMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string>
<string name="permlab_receiveMms" msgid="4000650116674380275">"primește mesaje text (MMS)"</string>
- <string name="permdesc_receiveMms" msgid="958102423732219710">"Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitivul dvs. fără a vi le arăta."</string>
+ <string name="permdesc_receiveMms" msgid="958102423732219710">"Permite aplicației să primească și să proceseze mesaje MMS. Acest lucru înseamnă că aplicația ar putea monitoriza sau șterge mesajele trimise pe dispozitiv fără a ți le arăta."</string>
<string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"Redirecționează mesajele cu transmisie celulară"</string>
<string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Permite aplicației să se conecteze la modulul de transmisie celulară pentru a redirecționa mesajele cu transmisie celulară pe măsură ce le primește. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgență."</string>
<string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Să gestioneze apelurile în desfășurare"</string>
<string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Permite unei aplicații să vadă detalii despre apelurile în desfășurare de pe dispozitiv și să gestioneze apelurile respective."</string>
<string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"citește mesajele cu transmisie celulară"</string>
- <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitivul dvs. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a vă avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului dvs. când este primită o transmisie celulară de urgență."</string>
+ <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Permite aplicației să citească mesajele primite prin transmisie celulară de dispozitiv. Alertele cu transmisie celulară sunt difuzate în unele locații pentru a te avertiza cu privire la situațiile de urgență. Aplicațiile rău intenționate pot afecta performanța sau funcționarea dispozitivului când e primită o transmisie celulară de urgență."</string>
<string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"citire feeduri abonat"</string>
<string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Permite aplicației să obțină detalii despre feedurile sincronizate în prezent."</string>
- <string name="permlab_sendSms" msgid="7757368721742014252">"trimită și să vadă mesajele SMS"</string>
- <string name="permdesc_sendSms" msgid="6757089798435130769">"Permite aplicației să trimită mesaje SMS, ceea ce ar putea determina apariția unor taxe neașteptate. Aplicațiile rău intenționate pot acumula costuri prin trimiterea mesajelor fără confirmarea dvs."</string>
+ <string name="permlab_sendSms" msgid="7757368721742014252">"să trimită și să vadă mesajele SMS"</string>
+ <string name="permdesc_sendSms" msgid="6757089798435130769">"Permite aplicației să trimită mesaje SMS, ceea ce ar putea duce la costuri neașteptate. Aplicațiile rău intenționate pot acumula costuri prin trimiterea mesajelor fără confirmarea ta."</string>
<string name="permlab_readSms" msgid="5164176626258800297">"citește mesajele text (SMS sau MMS)"</string>
<string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"Această aplicație poate citi toate mesajele SMS stocate pe tabletă."</string>
<string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Această aplicație poate să citească toate mesajele SMS (texT) stocate pe dispozitivul Android TV."</string>
@@ -378,10 +378,10 @@
<string name="permlab_manageProfileAndDeviceOwners" msgid="639849495253987493">"să gestioneze profilul și proprietarii dispozitivului"</string>
<string name="permdesc_manageProfileAndDeviceOwners" msgid="7304240671781989283">"Permite aplicațiilor să seteze proprietarii de profiluri și proprietarul dispozitivului."</string>
<string name="permlab_reorderTasks" msgid="7598562301992923804">"reordonare aplicații care rulează"</string>
- <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Permite aplicației să mute activitățile în prim-plan și în fundal. Aplicația poate face acest lucru fără aportul dvs."</string>
+ <string name="permdesc_reorderTasks" msgid="8796089937352344183">"Permite aplicației să mute activitățile în prim-plan și în fundal. Aplicația poate face acest lucru fără intervenția ta."</string>
<string name="permlab_enableCarMode" msgid="893019409519325311">"activare mod Mașină"</string>
<string name="permdesc_enableCarMode" msgid="56419168820473508">"Permite aplicației să activeze modul Mașină."</string>
- <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"închide alte aplicații"</string>
+ <string name="permlab_killBackgroundProcesses" msgid="6559320515561928348">"să închidă alte aplicații"</string>
<string name="permdesc_killBackgroundProcesses" msgid="2357013583055434685">"Permite aplicației să oprească procesele derulate în fundal de alte aplicații. Acest lucru poate face ca respectivele aplicații să nu mai ruleze."</string>
<string name="permlab_systemAlertWindow" msgid="5757218350944719065">"Această aplicație poate apărea deasupra altor aplicații"</string>
<string name="permdesc_systemAlertWindow" msgid="1145660714855738308">"Această aplicație poate apărea deasupra altor aplicații sau a altor părți ale ecranului. Acest lucru poate să afecteze utilizarea normală a aplicației și să schimbe modul în care se afișează alte aplicații."</string>
@@ -418,7 +418,7 @@
<string name="permlab_readCallLog" msgid="1739990210293505948">"citește jurnalul de apeluri"</string>
<string name="permdesc_readCallLog" msgid="8964770895425873433">"Această aplicație poate citi istoricul apelurilor."</string>
<string name="permlab_writeCallLog" msgid="670292975137658895">"scrie jurnalul de apeluri"</string>
- <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Permite aplicației să modifice jurnalul de apeluri al tabletei dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string>
+ <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Permite aplicației să modifice jurnalul de apeluri al tabletei, inclusiv datele despre apelurile primite sau făcute. Aplicațiile rău intenționate pot folosi această permisiune pentru a șterge sau a modifica jurnalul de apeluri."</string>
<string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Permite aplicației să modifice jurnalul de apeluri al dispozitivului Android TV, inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul de apeluri."</string>
<string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Permite aplicației să modifice jurnalul de apeluri al telefonului dvs., inclusiv datele despre apelurile primite sau efectuate. Aplicațiile rău intenționate pot utiliza această permisiune pentru a șterge sau pentru a modifica jurnalul dvs. de apeluri."</string>
<string name="permlab_bodySensors" msgid="662918578601619569">"Să acceseze date de la senzorii corporali, cum ar fi pulsul, în timpul folosirii"</string>
@@ -443,7 +443,7 @@
<string name="permdesc_accessBackgroundLocation" msgid="8264885066095638105">"Aplicația poate accesa locația oricând, chiar dacă nu este folosită."</string>
<string name="permlab_modifyAudioSettings" msgid="6129039778010031815">"modificare setări audio"</string>
<string name="permdesc_modifyAudioSettings" msgid="8687227609663124921">"Permite aplicației să modifice setările audio globale, cum ar fi volumul și difuzorul care este utilizat pentru ieșire."</string>
- <string name="permlab_recordAudio" msgid="1208457423054219147">"înregistreze sunet"</string>
+ <string name="permlab_recordAudio" msgid="1208457423054219147">"să înregistreze sunet"</string>
<string name="permdesc_recordAudio" msgid="5857246765327514062">"Această aplicație poate să înregistreze conținut audio folosind microfonul când este în uz."</string>
<string name="permlab_recordBackgroundAudio" msgid="5891032812308878254">"să înregistreze conținut audio în fundal"</string>
<string name="permdesc_recordBackgroundAudio" msgid="1992623135737407516">"Această aplicație poate înregistra conținut audio folosind microfonul oricând."</string>
@@ -455,14 +455,14 @@
<string name="permdesc_camera" msgid="5240801376168647151">"Această aplicație poate să fotografieze și să înregistreze videoclipuri folosind camera foto când este în uz."</string>
<string name="permlab_backgroundCamera" msgid="7549917926079731681">"să fotografieze și să înregistreze videoclipuri în fundal"</string>
<string name="permdesc_backgroundCamera" msgid="1615291686191138250">"Această aplicație poate să fotografieze și să înregistreze videoclipuri folosind camera foto oricând."</string>
- <string name="permlab_systemCamera" msgid="3642917457796210580">"Permiteți unei aplicații sau unui serviciu accesul la camerele de sistem, ca să fotografieze și să înregistreze videoclipuri"</string>
+ <string name="permlab_systemCamera" msgid="3642917457796210580">"Permite unei aplicații sau unui serviciu accesul la camerele de sistem, ca să fotografieze și să înregistreze videoclipuri"</string>
<string name="permdesc_systemCamera" msgid="5938360914419175986">"Această aplicație de sistem privilegiată poate să fotografieze și să înregistreze videoclipuri folosind o cameră de sistem în orice moment. Necesită și permisiunea android.permission.CAMERA pentru aplicație"</string>
- <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permiteți unei aplicații sau unui serviciu să primească apeluri inverse atunci când sunt deschise sau închise dispozitive cu cameră."</string>
+ <string name="permlab_cameraOpenCloseListener" msgid="5548732769068109315">"Permite unei aplicații sau unui serviciu să primească apeluri inverse atunci când sunt deschise sau închise dispozitive cu cameră."</string>
<string name="permdesc_cameraOpenCloseListener" msgid="2002636131008772908">"Această aplicație poate primi apeluri inverse atunci când este deschis (de aplicație) sau închis orice dispozitiv cu cameră."</string>
<string name="permlab_vibrate" msgid="8596800035791962017">"controlează vibrarea"</string>
<string name="permdesc_vibrate" msgid="8733343234582083721">"Permite aplicației să controleze mecanismul de vibrare."</string>
<string name="permdesc_vibrator_state" msgid="7050024956594170724">"Permite aplicației să acceseze modul de vibrații."</string>
- <string name="permlab_callPhone" msgid="1798582257194643320">"apelare directă numere de telefon"</string>
+ <string name="permlab_callPhone" msgid="1798582257194643320">"să sune direct la numere de telefon"</string>
<string name="permdesc_callPhone" msgid="5439809516131609109">"Permite aplicației să apeleze numere de telefon fără intervenția dvs. Acest lucru poate determina apariția unor taxe sau a unor apeluri neașteptate. Cu această permisiune aplicația nu poate apela numerele de urgență. Aplicațiile rău intenționate pot acumula costuri prin efectuarea unor apeluri fără confirmare."</string>
<string name="permlab_accessImsCallService" msgid="442192920714863782">"accesează serviciul de apelare IMS"</string>
<string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Permite aplicației să folosească serviciul IMS pentru apeluri, fără intervenția dvs."</string>
@@ -471,8 +471,8 @@
<string name="permlab_readBasicPhoneState" msgid="3214853233263871347">"să citească informații de bază, precum activitatea și starea telefonului"</string>
<string name="permdesc_readBasicPhoneState" msgid="828185691675460520">"Permite ca aplicația să acceseze funcțiile de telefonie de bază ale dispozitivului."</string>
<string name="permlab_manageOwnCalls" msgid="9033349060307561370">"să direcționeze apelurile prin intermediul sistemului"</string>
- <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permiteți aplicației să direcționeze apelurile prin intermediul sistemului pentru a îmbunătăți calitatea apelurilor."</string>
- <string name="permlab_callCompanionApp" msgid="3654373653014126884">"Vedeți și controlați apelurile prin intermediul sistemului."</string>
+ <string name="permdesc_manageOwnCalls" msgid="4431178362202142574">"Permite aplicației să direcționeze apelurile prin intermediul sistemului pentru a îmbunătăți calitatea apelurilor."</string>
+ <string name="permlab_callCompanionApp" msgid="3654373653014126884">"Vezi și controlează apelurile prin intermediul sistemului."</string>
<string name="permdesc_callCompanionApp" msgid="8474168926184156261">"Permite aplicației să vadă și să controleze apelurile în desfășurare pe dispozitiv. Aceasta include informații ca numerele pentru apeluri și starea apelurilor."</string>
<string name="permlab_exemptFromAudioRecordRestrictions" msgid="1164725468350759486">"scutită de restricțiile pentru înregistrarea conținutului audio"</string>
<string name="permdesc_exemptFromAudioRecordRestrictions" msgid="2425117015896871976">"Scutiți aplicația de restricțiile pentru înregistrarea conținutului audio."</string>
@@ -501,10 +501,10 @@
<string name="permdesc_setTimeZone" product="tv" msgid="9069045914174455938">"Permite aplicației să schimbe fusul orar al dispozitivului Android TV."</string>
<string name="permdesc_setTimeZone" product="default" msgid="4611828585759488256">"Permite aplicației să schimbe fusul orar al telefonului."</string>
<string name="permlab_getAccounts" msgid="5304317160463582791">"găsește conturi pe dispozitiv"</string>
- <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Permite aplicației să obțină lista de conturi cunoscute de tabletă. Aceasta poate include conturile create de aplicațiile pe care le-ați instalat."</string>
+ <string name="permdesc_getAccounts" product="tablet" msgid="1784452755887604512">"Permite aplicației să obțină lista de conturi cunoscute de tabletă. Aceasta poate include conturile create de aplicațiile pe care le-ai instalat."</string>
<string name="permdesc_getAccounts" product="tv" msgid="437604680436540822">"Permite aplicației să obțină lista conturilor cunoscute de dispozitivul Android TV. Aceasta poate include conturile create de aplicațiile pe care le-ați instalat."</string>
<string name="permdesc_getAccounts" product="default" msgid="2491273043569751867">"Permite aplicației să obțină lista de conturi cunoscute de telefon. Aceasta poate include conturile create de aplicațiile pe care le-ați instalat."</string>
- <string name="permlab_accessNetworkState" msgid="2349126720783633918">"vizualizează conexiunile la rețea"</string>
+ <string name="permlab_accessNetworkState" msgid="2349126720783633918">"să vadă conexiunile la rețea"</string>
<string name="permdesc_accessNetworkState" msgid="4394564702881662849">"Permite aplicației să vadă informațiile despre conexiunile la rețea, cum ar fi rețelele existente și cele care sunt conectate."</string>
<string name="permlab_createNetworkSockets" msgid="3224420491603590541">"să aibă acces deplin la rețea"</string>
<string name="permdesc_createNetworkSockets" msgid="7722020828749535988">"Permite aplicației să creeze socluri de rețea și să utilizeze protocoale de rețea personalizate. Browserul și alte aplicații oferă mijloacele de trimitere a datelor pe internet, astfel încât această permisiune nu este necesară pentru trimiterea datelor pe internet."</string>
@@ -512,7 +512,7 @@
<string name="permdesc_changeNetworkState" msgid="649341947816898736">"Permite aplicației să modifice starea de conectivitate la rețea."</string>
<string name="permlab_changeTetherState" msgid="9079611809931863861">"modificare conectivitate tethering"</string>
<string name="permdesc_changeTetherState" msgid="3025129606422533085">"Permite aplicației să modifice starea de conectivitate prin tethering la rețea."</string>
- <string name="permlab_accessWifiState" msgid="5552488500317911052">"vizualizează conexiunile Wi-Fi"</string>
+ <string name="permlab_accessWifiState" msgid="5552488500317911052">"să vadă conexiunile Wi-Fi"</string>
<string name="permdesc_accessWifiState" msgid="6913641669259483363">"Permite aplicației să vadă informațiile despre rețelele Wi-Fi, de ex. dacă o rețea Wi-Fi este activată, precum și numele dispozitivelor conectate la rețeaua Wi-Fi."</string>
<string name="permlab_changeWifiState" msgid="7947824109713181554">"se conectează și se deconectează de la Wi-Fi"</string>
<string name="permdesc_changeWifiState" msgid="7170350070554505384">"Permite aplicației să se conecteze și să se deconecteze de la punctele de acces Wi-Fi, precum și să efectueze modificări în configurația dispozitivului pentru rețelele Wi-Fi."</string>
@@ -526,12 +526,12 @@
<string name="permdesc_bluetoothAdmin" product="default" msgid="7381341743021234863">"Permite aplicației să configureze telefonul Bluetooth local, să descopere și să se împerecheze cu dispozitive la distanță."</string>
<string name="permlab_accessWimaxState" msgid="7029563339012437434">"se conectează și se deconectează de la WiMAX"</string>
<string name="permdesc_accessWimaxState" msgid="5372734776802067708">"Permite aplicației să stabilească dacă o rețea WiMAX este activată și să vadă informațiile cu privire la toate rețelele WiMAX conectate."</string>
- <string name="permlab_changeWimaxState" msgid="6223305780806267462">"schimbați starea WiMAX"</string>
+ <string name="permlab_changeWimaxState" msgid="6223305780806267462">"schimbă starea WiMAX"</string>
<string name="permdesc_changeWimaxState" product="tablet" msgid="4011097664859480108">"Permite aplicației să conecteze și să deconecteze tableta la și de la rețelele WiMAX."</string>
<string name="permdesc_changeWimaxState" product="tv" msgid="5373274458799425276">"Permite aplicației să conecteze și să deconecteze dispozitivul Android TV de la rețelele WiMAX."</string>
<string name="permdesc_changeWimaxState" product="default" msgid="1551666203780202101">"Permite aplicației să conecteze și să deconecteze telefonul la și de la rețelele WiMAX."</string>
<string name="permlab_bluetooth" msgid="586333280736937209">"conectează dispozitive Bluetooth"</string>
- <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Permite aplicației să vadă configurația tabletei Bluetooth, să efectueze și să accepte conexiuni cu dispozitive împerecheate."</string>
+ <string name="permdesc_bluetooth" product="tablet" msgid="3053222571491402635">"Permite aplicației să vadă configurația tabletei Bluetooth, să facă și să accepte conexiuni cu dispozitive asociate."</string>
<string name="permdesc_bluetooth" product="tv" msgid="8851534496561034998">"Permite aplicației să vadă configurația conexiunii prin Bluetooth a dispozitivului Android TV, să efectueze și să accepte conexiuni cu dispozitive împerecheate."</string>
<string name="permdesc_bluetooth" product="default" msgid="2779606714091276746">"Permite aplicației să vadă configurația telefonului Bluetooth, să efectueze și să accepte conexiuni cu dispozitive împerecheate."</string>
<string name="permlab_bluetooth_scan" msgid="5402587142833124594">"să descopere și să asocieze dispozitive Bluetooth din apropiere"</string>
@@ -541,7 +541,7 @@
<string name="permlab_bluetooth_advertise" msgid="2781147747928853177">"să transmită anunțuri pe dispozitive Bluetooth din apropiere"</string>
<string name="permdesc_bluetooth_advertise" product="default" msgid="6085174451034210183">"Permite aplicației să difuzeze anunțuri pe dispozitive Bluetooth din apropiere"</string>
<string name="permlab_uwb_ranging" msgid="8141915781475770665">"să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string>
- <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permiteți-i aplicației să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string>
+ <string name="permdesc_uwb_ranging" msgid="2519723069604307055">"Permite-i aplicației să stabilească poziția relativă dintre dispozitivele Ultra-Wideband din apropiere"</string>
<string name="permlab_nearby_wifi_devices" msgid="392774237063608500">"să interacționeze cu dispozitive Wi‑Fi din apropiere"</string>
<string name="permdesc_nearby_wifi_devices" msgid="3054307728646332906">"Permite aplicației să se conecteze la dispozitive Wi-Fi din apropiere, să transmită anunțuri și să stabilească poziția relativă a acestora"</string>
<string name="permlab_preferredPaymentInfo" msgid="5274423844767445054">"Informații despre serviciul de plăți NFC preferat"</string>
@@ -563,92 +563,92 @@
<string name="permlab_useFingerprint" msgid="1001421069766751922">"folosește hardware-ul pentru amprentă"</string>
<string name="permdesc_useFingerprint" msgid="412463055059323742">"Permite aplicației să folosească hardware pentru amprentă pentru autentificare"</string>
<string name="permlab_audioWrite" msgid="8501705294265669405">"modificați colecția de muzică"</string>
- <string name="permdesc_audioWrite" msgid="8057399517013412431">"Permite aplicației să vă modifice colecția de muzică."</string>
+ <string name="permdesc_audioWrite" msgid="8057399517013412431">"Permite aplicației să modifice colecția de muzică."</string>
<string name="permlab_videoWrite" msgid="5940738769586451318">"modificați colecția de videoclipuri"</string>
<string name="permdesc_videoWrite" msgid="6124731210613317051">"Permite aplicației să vă modifice colecția de videoclipuri."</string>
<string name="permlab_imagesWrite" msgid="1774555086984985578">"modificați colecția de fotografii"</string>
<string name="permdesc_imagesWrite" msgid="5195054463269193317">"Permite aplicației să vă modifice colecția de fotografii."</string>
<string name="permlab_mediaLocation" msgid="7368098373378598066">"citiți locațiile din colecția media"</string>
<string name="permdesc_mediaLocation" msgid="597912899423578138">"Permite aplicației să citească locațiile din colecția dvs. media."</string>
- <string name="biometric_app_setting_name" msgid="3339209978734534457">"Folosiți sistemele biometrice"</string>
- <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Folosiți sistemele biometrice sau blocarea ecranului"</string>
+ <string name="biometric_app_setting_name" msgid="3339209978734534457">"Folosește sistemele biometrice"</string>
+ <string name="biometric_or_screen_lock_app_setting_name" msgid="5348462421758257752">"Folosește sistemele biometrice sau blocarea ecranului"</string>
<string name="biometric_dialog_default_title" msgid="55026799173208210">"Confirmați-vă identitatea"</string>
- <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Folosiți sistemele biometrice pentru a continua"</string>
- <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Folosiți sistemele biometrice sau blocarea ecranului pentru a continua"</string>
+ <string name="biometric_dialog_default_subtitle" msgid="8457232339298571992">"Folosește sistemele biometrice pentru a continua"</string>
+ <string name="biometric_or_screen_lock_dialog_default_subtitle" msgid="159539678371552009">"Folosește sistemele biometrice sau blocarea ecranului pentru a continua"</string>
<string name="biometric_error_hw_unavailable" msgid="2494077380540615216">"Hardware biometric indisponibil"</string>
<string name="biometric_error_user_canceled" msgid="6732303949695293730">"Autentificarea a fost anulată"</string>
<string name="biometric_not_recognized" msgid="5106687642694635888">"Nu este recunoscut"</string>
<string name="biometric_error_canceled" msgid="8266582404844179778">"Autentificarea a fost anulată"</string>
<string name="biometric_error_device_not_secured" msgid="3129845065043995924">"Nu este setat niciun cod PIN, model sau parolă"</string>
<string name="biometric_error_generic" msgid="6784371929985434439">"Eroare la autentificare"</string>
- <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Folosiți blocarea ecranului"</string>
+ <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Folosește blocarea ecranului"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Introduceți blocarea ecranului ca să continuați"</string>
- <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Apăsați ferm pe senzor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Amprenta nu a putut fi procesată. Încercați din nou."</string>
+ <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Apasă ferm pe senzor"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Amprenta nu a fost recunoscută. Încearcă din nou."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Curățați senzorul de amprentă și încercați din nou"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Curățați senzorul și încercați din nou"</string>
- <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Apăsați ferm pe senzor"</string>
- <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Ați mișcat degetul prea lent. Încercați din nou."</string>
- <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Încercați altă amprentă"</string>
+ <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Apasă ferm pe senzor"</string>
+ <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Ai mișcat degetul prea lent. Încearcă din nou."</string>
+ <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Încearcă altă amprentă"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Prea luminos"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"S-a detectat apăsarea butonului de alimentare"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Încercați să ajustați"</string>
- <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Schimbați ușor poziția degetului de fiecare dată"</string>
+ <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Schimbă ușor poziția degetului de fiecare dată"</string>
<string-array name="fingerprint_acquired_vendor">
</string-array>
<string name="fingerprint_error_not_match" msgid="4599441812893438961">"Amprenta nu a fost recunoscută"</string>
<string name="fingerprint_udfps_error_not_match" msgid="8236930793223158856">"Amprenta nu a fost recunoscută"</string>
<string name="fingerprint_authenticated" msgid="2024862866860283100">"Amprentă autentificată"</string>
<string name="face_authenticated_no_confirmation_required" msgid="8867889115112348167">"Chip autentificat"</string>
- <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Chip autentificat, apăsați Confirmați"</string>
+ <string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Chip autentificat, apasă pe Confirmă"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardware-ul pentru amprentă nu este disponibil."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nu se poate configura amprenta"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Timpul pentru amprentare a expirat. Încercați din nou."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Configurarea amprentei a expirat. Încearcă din nou."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operațiunea privind amprenta a fost anulată."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Operațiunea privind amprenta a fost anulată de utilizator."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Prea multe încercări. Încercați din nou mai târziu."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Prea multe încercări. Folosește blocarea ecranului."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Prea multe încercări. Folosește blocarea ecranului."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Încercați din nou."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Nu putem procesa amprenta. Încearcă din nou."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nu au fost înregistrate amprente."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Dispozitivul nu are senzor de amprentă."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzorul este dezactivat temporar."</string>
<string name="fingerprint_error_bad_calibration" msgid="4385512597740168120">"Nu se poate folosi senzorul de amprentă. Vizitați un furnizor de servicii de reparații."</string>
<string name="fingerprint_error_power_pressed" msgid="5479524500542129414">"A fost apăsat butonul de pornire"</string>
<string name="fingerprint_name_template" msgid="8941662088160289778">"Degetul <xliff:g id="FINGERID">%d</xliff:g>"</string>
- <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosiți amprenta"</string>
- <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Folosiți amprenta sau blocarea ecranului"</string>
- <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Folosiți amprenta pentru a continua"</string>
- <string name="fingerprint_or_screen_lock_dialog_default_subtitle" msgid="5195808203117992200">"Folosiți amprenta sau blocarea ecranului pentru a continua"</string>
+ <string name="fingerprint_app_setting_name" msgid="4253767877095495844">"Folosește amprenta"</string>
+ <string name="fingerprint_or_screen_lock_app_setting_name" msgid="3501743523487644907">"Folosește amprenta sau blocarea ecranului"</string>
+ <string name="fingerprint_dialog_default_subtitle" msgid="3879832845486835905">"Folosește amprenta pentru a continua"</string>
+ <string name="fingerprint_or_screen_lock_dialog_default_subtitle" msgid="5195808203117992200">"Folosește amprenta sau blocarea ecranului pentru a continua"</string>
<string-array name="fingerprint_error_vendor">
</string-array>
- <string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"A apărut o eroare. Încercați din nou."</string>
+ <string name="fingerprint_error_vendor_unknown" msgid="4170002184907291065">"A apărut o eroare. Încearcă din nou."</string>
<string name="fingerprint_icon_content_description" msgid="4741068463175388817">"Pictograma amprentă"</string>
<string name="face_recalibrate_notification_name" msgid="7311163114750748686">"Deblocare facială"</string>
<string name="face_recalibrate_notification_title" msgid="2524791952735579082">"Problemă cu Deblocarea facială"</string>
<string name="face_recalibrate_notification_content" msgid="3064513770251355594">"Atingeți pentru a șterge modelul facial, apoi adăugați din nou fața"</string>
- <string name="face_setup_notification_title" msgid="8843461561970741790">"Configurați Deblocarea facială"</string>
+ <string name="face_setup_notification_title" msgid="8843461561970741790">"Configurează Deblocarea facială"</string>
<string name="face_setup_notification_content" msgid="5463999831057751676">"Deblocați-vă telefonul uitându-vă la acesta"</string>
<string name="face_sensor_privacy_enabled" msgid="7407126963510598508">"Pentru a folosi Deblocarea facială, activați "<b>"Accesul la cameră"</b>" în Setări și confidențialitate"</string>
- <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurați mai multe moduri de deblocare"</string>
+ <string name="fingerprint_setup_notification_title" msgid="2002630611398849495">"Configurează mai multe moduri de deblocare"</string>
<string name="fingerprint_setup_notification_content" msgid="205578121848324852">"Atingeți ca să adăugați o amprentă"</string>
<string name="fingerprint_recalibrate_notification_name" msgid="1414578431898579354">"Deblocare cu amprenta"</string>
<string name="fingerprint_recalibrate_notification_title" msgid="2406561052064558497">"Nu se poate folosi senzorul de amprentă"</string>
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vizitați un furnizor de servicii de reparații."</string>
- <string name="face_acquired_insufficient" msgid="6889245852748492218">"Nu se poate crea modelul facial. Reîncercați."</string>
- <string name="face_acquired_too_bright" msgid="8070756048978079164">"Prea luminos. Încercați o lumină mai slabă."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
- <string name="face_acquired_too_close" msgid="4453646176196302462">"Mutați telefonul mai departe"</string>
- <string name="face_acquired_too_far" msgid="2922278214231064859">"Mutați telefonul mai aproape"</string>
- <string name="face_acquired_too_high" msgid="8278815780046368576">"Mutați telefonul mai sus"</string>
- <string name="face_acquired_too_low" msgid="4075391872960840081">"Mutați telefonul mai jos"</string>
- <string name="face_acquired_too_right" msgid="6245286514593540859">"Mutați telefonul spre stânga"</string>
- <string name="face_acquired_too_left" msgid="9201762240918405486">"Mutați telefonul spre dreapta"</string>
+ <string name="face_acquired_insufficient" msgid="6889245852748492218">"Nu se poate crea modelul facial. Reîncearcă."</string>
+ <string name="face_acquired_too_bright" msgid="8070756048978079164">"Prea luminos. Încearcă o lumină mai slabă."</string>
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Lumină insuficientă"</string>
+ <string name="face_acquired_too_close" msgid="4453646176196302462">"Mută telefonul mai departe"</string>
+ <string name="face_acquired_too_far" msgid="2922278214231064859">"Mută telefonul mai aproape"</string>
+ <string name="face_acquired_too_high" msgid="8278815780046368576">"Mută telefonul mai sus"</string>
+ <string name="face_acquired_too_low" msgid="4075391872960840081">"Mută telefonul mai jos"</string>
+ <string name="face_acquired_too_right" msgid="6245286514593540859">"Mută telefonul spre stânga"</string>
+ <string name="face_acquired_too_left" msgid="9201762240918405486">"Mută telefonul spre dreapta"</string>
<string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Priviți mai direct spre dispozitiv."</string>
<string name="face_acquired_not_detected" msgid="1057966913397548150">"Nu vi se vede fața. Țineți telefonul la nivelul ochilor."</string>
<string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Prea multă mișcare. Țineți telefonul nemișcat."</string>
- <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Reînregistrați-vă chipul."</string>
- <string name="face_acquired_too_different" msgid="2520389515612972889">"Chipul nu a fost recunoscut. Reîncercați."</string>
- <string name="face_acquired_too_similar" msgid="8882920552674125694">"Schimbați ușor poziția capului"</string>
+ <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Reînregistrează-ți chipul."</string>
+ <string name="face_acquired_too_different" msgid="2520389515612972889">"Chipul nu a fost recunoscut. Reîncearcă."</string>
+ <string name="face_acquired_too_similar" msgid="8882920552674125694">"Schimbă ușor poziția capului"</string>
<string name="face_acquired_pan_too_extreme" msgid="5417928604710621088">"Priviți direct spre telefon"</string>
<string name="face_acquired_tilt_too_extreme" msgid="5715715666540716620">"Priviți direct spre telefon"</string>
<string name="face_acquired_roll_too_extreme" msgid="8261939882838881194">"Priviți direct spre telefon"</string>
@@ -658,33 +658,33 @@
<skip />
<!-- no translation found for face_acquired_mouth_covering_detected (8219428572168642593) -->
<skip />
- <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Nu se poate crea modelul facial. Reîncercați."</string>
+ <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Nu se poate crea modelul facial. Reîncearcă."</string>
<string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"S-au detectat ochelari de culoare închisă. Chipul trebuie să fie vizibil în totalitate."</string>
<string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"S-a detectat un articol care acoperă chipul. Chipul trebuie să fie vizibil în totalitate."</string>
<string-array name="face_acquired_vendor">
</string-array>
<string name="face_error_hw_not_available" msgid="5085202213036026288">"Nu se poate confirma fața. Hardware-ul nu este disponibil."</string>
- <string name="face_error_timeout" msgid="2598544068593889762">"Încercați din nou Deblocarea facială"</string>
- <string name="face_error_no_space" msgid="5649264057026021723">"Nu se pot stoca date faciale noi. Ștergeți întâi unele vechi."</string>
+ <string name="face_error_timeout" msgid="2598544068593889762">"Încearcă din nou Deblocarea facială"</string>
+ <string name="face_error_no_space" msgid="5649264057026021723">"Nu se pot stoca date faciale noi. Șterge întâi unele vechi."</string>
<string name="face_error_canceled" msgid="2164434737103802131">"Operațiunea privind chipul a fost anulată."</string>
<string name="face_error_user_canceled" msgid="5766472033202928373">"Deblocarea facială a fost anulată de utilizator"</string>
- <string name="face_error_lockout" msgid="7864408714994529437">"Prea multe încercări. Reîncercați mai târziu."</string>
+ <string name="face_error_lockout" msgid="7864408714994529437">"Prea multe încercări. Reîncearcă mai târziu."</string>
<string name="face_error_lockout_permanent" msgid="3277134834042995260">"Prea multe încercări. Deblocarea facială este dezactivată."</string>
- <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Prea multe încercări. Folosiți blocarea ecranului."</string>
- <string name="face_error_unable_to_process" msgid="5723292697366130070">"Nu se poate confirma fața. Încercați din nou."</string>
+ <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"Prea multe încercări. Folosește blocarea ecranului."</string>
+ <string name="face_error_unable_to_process" msgid="5723292697366130070">"Nu se poate confirma fața. Încearcă din nou."</string>
<string name="face_error_not_enrolled" msgid="1134739108536328412">"Nu ați configurat Deblocarea facială"</string>
<string name="face_error_hw_not_present" msgid="7940978724978763011">"Deblocarea facială nu este acceptată pe acest dispozitiv"</string>
<string name="face_error_security_update_required" msgid="5076017208528750161">"Senzorul este dezactivat temporar."</string>
<string name="face_name_template" msgid="3877037340223318119">"Chip <xliff:g id="FACEID">%d</xliff:g>"</string>
- <string name="face_app_setting_name" msgid="5854024256907828015">"Folosiți Deblocarea facială"</string>
- <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Folosiți deblocarea facială sau ecranul de blocare"</string>
+ <string name="face_app_setting_name" msgid="5854024256907828015">"Folosește Deblocarea facială"</string>
+ <string name="face_or_screen_lock_app_setting_name" msgid="1603149075605709106">"Folosește deblocarea facială sau ecranul de blocare"</string>
<string name="face_dialog_default_subtitle" msgid="6620492813371195429">"Folosiți-vă chipul ca să continuați"</string>
<string name="face_or_screen_lock_dialog_default_subtitle" msgid="5006381531158341844">"Folosiți-vă chipul sau blocarea ecranului pentru a continua"</string>
<string-array name="face_error_vendor">
</string-array>
- <string name="face_error_vendor_unknown" msgid="7387005932083302070">"A apărut o eroare. Încercați din nou."</string>
+ <string name="face_error_vendor_unknown" msgid="7387005932083302070">"A apărut o eroare. Încearcă din nou."</string>
<string name="face_icon_content_description" msgid="465030547475916280">"Pictograma chip"</string>
- <string name="permlab_readSyncSettings" msgid="6250532864893156277">"citire setări sincronizare"</string>
+ <string name="permlab_readSyncSettings" msgid="6250532864893156277">"să citească setări sincronizare"</string>
<string name="permdesc_readSyncSettings" msgid="1325658466358779298">"Permite aplicației să citească setările de sincronizare ale unui cont. De exemplu, cu această permisiune aplicația poate determina dacă aplicația Persoane este sincronizată cu un anumit cont."</string>
<string name="permlab_writeSyncSettings" msgid="6583154300780427399">"activează/dezactivează sincronizarea"</string>
<string name="permdesc_writeSyncSettings" msgid="6029151549667182687">"Permite unei aplicații să modifice setările de sincronizare ale unui cont. De exemplu, cu această permisiune aplicația poate activa sincronizarea aplicației Persoane cu un anumit cont."</string>
@@ -711,7 +711,7 @@
<string name="permlab_bind_incall_service" msgid="5990625112603493016">"interacțiune cu ecranul în timpul unui apel"</string>
<string name="permdesc_bind_incall_service" msgid="4124917526967765162">"Permite aplicației să controleze când și cum vede utilizatorul ecranul în timpul unui apel."</string>
<string name="permlab_bind_connection_service" msgid="5409268245525024736">"să interacționeze cu servicii de telefonie"</string>
- <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Permite aplicației să interacționeze cu servicii de telefonie pentru a da / a primi apeluri."</string>
+ <string name="permdesc_bind_connection_service" msgid="6261796725253264518">"Permite aplicației să interacționeze cu servicii de telefonie pentru a face / a primi apeluri."</string>
<string name="permlab_control_incall_experience" msgid="6436863486094352987">"oferă o experiență de utilizare în timpul unui apel"</string>
<string name="permdesc_control_incall_experience" msgid="5896723643771737534">"Permite aplicației să ofere o experiență de utilizare în timpul unui apel."</string>
<string name="permlab_readNetworkUsageHistory" msgid="8470402862501573795">"citește utilizarea statistică a rețelei"</string>
@@ -746,7 +746,7 @@
<string name="permdesc_bindCarrierServices" msgid="9185614481967262900">"Permite aplicației să se conecteze la serviciile operatorului. Nu ar trebui să fie niciodată necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_access_notification_policy" msgid="5524112842876975537">"accesează Nu deranja"</string>
<string name="permdesc_access_notification_policy" msgid="8538374112403845013">"Permite aplicației să citească și să scrie configurația Nu deranja."</string>
- <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"porniți folosirea permisiunii de vizualizare"</string>
+ <string name="permlab_startViewPermissionUsage" msgid="1504564328641112341">"să înceapă folosirea permisiunii de vizualizare"</string>
<string name="permdesc_startViewPermissionUsage" msgid="2820325605959586538">"Permite proprietarului să pornească folosirea permisiunii pentru o aplicație. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
<string name="permlab_startReviewPermissionDecisions" msgid="8690578688476599284">"să înceapă să examineze deciziile privind permisiunile"</string>
<string name="permdesc_startReviewPermissionDecisions" msgid="2775556853503004236">"Permite proprietarului să deschidă ecranul pentru a examina deciziile privind permisiunile. Nu ar trebui să fie necesară pentru aplicațiile obișnuite."</string>
@@ -760,36 +760,36 @@
<string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați tableta sau ștergeți datele acesteia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați dispozitivul Android TV sau ștergeți toate datele de pe acesta dacă se introduc prea multe parole incorecte."</string>
<string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați sistemul de infotainment sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
- <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați telefonul sau ștergeți toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
+ <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"Monitorizează numărul de parole incorecte introduse la deblocarea ecranului și blochează telefonul sau șterge toate datele acestuia dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați tableta sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați dispozitivul Android TV sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați sistemul de infotainment sau ștergeți toate datele acestui profil dacă sunt introduse prea multe parole incorecte."</string>
<string name="policydesc_watchLogin_secondaryUser" product="default" msgid="9177645136475155924">"Monitorizați numărul de parole incorecte introduse la deblocarea ecranului și blocați telefonul sau ștergeți toate datele acestui utilizator dacă se introduc prea multe parole incorecte."</string>
<string name="policylab_resetPassword" msgid="214556238645096520">"Să schimbe blocarea ecranului"</string>
- <string name="policydesc_resetPassword" msgid="4626419138439341851">"Modificați blocarea ecranului."</string>
+ <string name="policydesc_resetPassword" msgid="4626419138439341851">"Modifică blocarea ecranului."</string>
<string name="policylab_forceLock" msgid="7360335502968476434">"Să blocheze ecranul"</string>
- <string name="policydesc_forceLock" msgid="1008844760853899693">"Stabiliți modul și timpul în care se blochează ecranul."</string>
+ <string name="policydesc_forceLock" msgid="1008844760853899693">"Stabilește cum și când se blochează ecranul."</string>
<string name="policylab_wipeData" msgid="1359485247727537311">"Să șteargă toate datele"</string>
- <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Ștergeți datele de pe tabletă fără avertisment, efectuând resetarea configurării din fabrică."</string>
- <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Ștergeți datele de pe dispozitivul Android TV fără avertisment, efectuând o revenire la setările din fabrică."</string>
- <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Ștergeți datele din sistemul de infotainment fără avertisment, prin revenirea la setările din fabrică."</string>
- <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Ștergeți datele din telefon fără avertisment, efectuând resetarea configurării din fabrică."</string>
- <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Ștergeți datele de profil"</string>
- <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Ștergeți datele utilizatorului"</string>
- <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Ștergeți datele acestui utilizator de pe această tabletă fără avertisment."</string>
- <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Ștergeți datele acestui utilizator de pe acest dispozitiv Android TV fără avertisment"</string>
- <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Ștergeți datele profilului din acest sistem de infotainment fără avertisment."</string>
- <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Ștergeți datele acestui utilizator de pe acest telefon fără avertisment."</string>
- <string name="policylab_setGlobalProxy" msgid="215332221188670221">"Setați serverul proxy global pentru dispozitiv"</string>
- <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Setați serverul proxy global pentru dispozitiv, care să fie utilizat cât timp politica este activă. Numai proprietarul dispozitivului poate seta serverul proxy global."</string>
- <string name="policylab_expirePassword" msgid="6015404400532459169">"Setați expirarea parolei pentru blocarea ecranului"</string>
- <string name="policydesc_expirePassword" msgid="9136524319325960675">"Modificați frecvența cu care trebuie să se schimbe parola, codul PIN sau modelul pentru blocarea ecranului."</string>
+ <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"Șterge datele de pe tabletă fără avertisment, efectuând resetarea configurării din fabrică."</string>
+ <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"Șterge datele de pe dispozitivul Android TV fără avertisment, efectuând o revenire la setările din fabrică."</string>
+ <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"Șterge datele din sistemul de infotainment fără avertisment, prin revenirea la setările din fabrică."</string>
+ <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"Șterge datele din telefon fără avertisment, revenind la setările din fabrică."</string>
+ <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"Șterge datele de profil"</string>
+ <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"Șterge datele utilizatorului"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"Șterge datele acestui utilizator de pe această tabletă fără avertisment."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="tv" msgid="2293713284515865200">"Șterge datele acestui utilizator de pe acest dispozitiv Android TV fără avertisment"</string>
+ <string name="policydesc_wipeData_secondaryUser" product="automotive" msgid="4658832487305780879">"Șterge datele profilului din acest sistem de infotainment fără avertisment."</string>
+ <string name="policydesc_wipeData_secondaryUser" product="default" msgid="2788325512167208654">"Șterge datele acestui utilizator de pe acest telefon fără avertisment."</string>
+ <string name="policylab_setGlobalProxy" msgid="215332221188670221">"Setează serverul proxy global pentru dispozitiv"</string>
+ <string name="policydesc_setGlobalProxy" msgid="7149665222705519604">"Setează serverul proxy global pentru dispozitiv, care să fie utilizat cât timp politica este activă. Numai proprietarul dispozitivului poate seta serverul proxy global."</string>
+ <string name="policylab_expirePassword" msgid="6015404400532459169">"Setează expirarea parolei pentru blocarea ecranului"</string>
+ <string name="policydesc_expirePassword" msgid="9136524319325960675">"Modifică frecvența cu care trebuie să se schimbe parola, codul PIN sau modelul pentru blocarea ecranului."</string>
<string name="policylab_encryptedStorage" msgid="9012936958126670110">"Să seteze criptarea stocării"</string>
<string name="policydesc_encryptedStorage" msgid="1102516950740375617">"Necesită ca datele aplicației stocate să fie criptate."</string>
<string name="policylab_disableCamera" msgid="5749486347810162018">"Să dezactiveze camerele foto"</string>
- <string name="policydesc_disableCamera" msgid="3204405908799676104">"Împiedicați utilizarea camerelor foto de pe dispozitiv."</string>
+ <string name="policydesc_disableCamera" msgid="3204405908799676104">"Împiedică folosirea camerelor foto de pe dispozitiv."</string>
<string name="policylab_disableKeyguardFeatures" msgid="5071855750149949741">"Să oprească funcții de blocare ecran"</string>
- <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"Împiedicați folosirea unor funcții de blocare a ecranului."</string>
+ <string name="policydesc_disableKeyguardFeatures" msgid="6641673177041195957">"Împiedică folosirea unor funcții de blocare a ecranului."</string>
<string-array name="phoneTypes">
<item msgid="8996339953292723951">"Domiciliu"</item>
<item msgid="7740243458912727194">"Mobil"</item>
@@ -904,43 +904,43 @@
<string name="sipAddressTypeWork" msgid="7873967986701216770">"Serviciu"</string>
<string name="sipAddressTypeOther" msgid="6317012577345187275">"Altul"</string>
<string name="quick_contacts_not_available" msgid="1262709196045052223">"Nu s-a găsit nicio aplicație pentru a afișa această persoană de contact."</string>
- <string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"Introduceți codul PIN"</string>
- <string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"Introduceți codul PUK și noul cod PIN"</string>
+ <string name="keyguard_password_enter_pin_code" msgid="6401406801060956153">"Introdu codul PIN"</string>
+ <string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"Introdu codul PUK și noul cod PIN"</string>
<string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"Codul PUK"</string>
<string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"Noul cod PIN"</string>
- <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Atingeți ca să introduceți parola"</font></string>
- <string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"Introduceți parola pentru a debloca"</string>
- <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Introduceți codul PIN pentru a debloca"</string>
+ <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"Atinge ca să introduci parola"</font></string>
+ <string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"Introdu parola pentru a debloca"</string>
+ <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"Introdu codul PIN pentru a debloca"</string>
<string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"Cod PIN incorect."</string>
- <string name="keyguard_label_text" msgid="3841953694564168384">"Pentru a debloca, apăsați Meniu, apoi 0."</string>
+ <string name="keyguard_label_text" msgid="3841953694564168384">"Pentru a debloca, apasă Meniu, apoi 0."</string>
<string name="emergency_call_dialog_number_for_display" msgid="2978165477085612673">"Număr de urgență"</string>
<string name="lockscreen_carrier_default" msgid="6192313772955399160">"Fără semnal"</string>
<string name="lockscreen_screen_locked" msgid="7364905540516041817">"Ecranul este blocat."</string>
- <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Apăsați Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string>
- <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Apăsați Meniu pentru deblocare."</string>
- <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenați modelul pentru a debloca"</string>
+ <string name="lockscreen_instructions_when_pattern_enabled" msgid="7982445492532123308">"Apasă Meniu pentru a debloca sau pentru a efectua apeluri de urgență."</string>
+ <string name="lockscreen_instructions_when_pattern_disabled" msgid="7434061749374801753">"Apasă Meniu pentru deblocare."</string>
+ <string name="lockscreen_pattern_instructions" msgid="3169991838169244941">"Desenează modelul pentru a debloca"</string>
<string name="lockscreen_emergency_call" msgid="7500692654885445299">"Urgență"</string>
<string name="lockscreen_return_to_call" msgid="3156883574692006382">"Reveniți la apel"</string>
<string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Corect!"</string>
- <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Încercați din nou"</string>
- <string name="lockscreen_password_wrong" msgid="8605355913868947490">"Încercați din nou"</string>
- <string name="lockscreen_storage_locked" msgid="634993789186443380">"Deblocați pentru toate funcțiile și datele"</string>
+ <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Încearcă din nou"</string>
+ <string name="lockscreen_password_wrong" msgid="8605355913868947490">"Încearcă din nou"</string>
+ <string name="lockscreen_storage_locked" msgid="634993789186443380">"Deblochează pentru toate funcțiile și datele"</string>
<string name="faceunlock_multiple_failures" msgid="681991538434031708">"S-a depășit numărul maxim de încercări pentru Deblocare facială"</string>
<string name="lockscreen_missing_sim_message_short" msgid="1248431165144893792">"Fără SIM"</string>
<string name="lockscreen_missing_sim_message" product="tablet" msgid="8596805728510570760">"Nu există card SIM în computerul tablet PC."</string>
<string name="lockscreen_missing_sim_message" product="tv" msgid="2582768023352171073">"Nu există un card SIM în dispozitivul Android TV."</string>
<string name="lockscreen_missing_sim_message" product="default" msgid="1408695081255172556">"Telefonul nu are card SIM."</string>
- <string name="lockscreen_missing_sim_instructions" msgid="8473601862688263903">"Introduceți un card SIM."</string>
- <string name="lockscreen_missing_sim_instructions_long" msgid="3664999892038416334">"Cardul SIM lipsește sau nu poate fi citit. Introduceți un card SIM."</string>
+ <string name="lockscreen_missing_sim_instructions" msgid="8473601862688263903">"Introdu un card SIM."</string>
+ <string name="lockscreen_missing_sim_instructions_long" msgid="3664999892038416334">"Cardul SIM lipsește sau nu poate fi citit. Introdu un card SIM."</string>
<string name="lockscreen_permanent_disabled_sim_message_short" msgid="3812893366715730539">"Card SIM inutilizabil."</string>
<string name="lockscreen_permanent_disabled_sim_instructions" msgid="4358929052509450807">"Cardul dvs. SIM este dezactivat definitiv.\n Contactați furnizorul de servicii wireless pentru a obține un alt card SIM."</string>
<string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"Melodia anterioară"</string>
<string name="lockscreen_transport_next_description" msgid="2931509904881099919">"Melodia următoare"</string>
<string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"Pauză"</string>
- <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Redați"</string>
- <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Opriți"</string>
- <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Derulați"</string>
- <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Derulați rapid înainte"</string>
+ <string name="lockscreen_transport_play_description" msgid="106868788691652733">"Redă"</string>
+ <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"Oprește"</string>
+ <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"Derulează"</string>
+ <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"Derulează rapid înainte"</string>
<string name="emergency_calls_only" msgid="3057351206678279851">"Numai apeluri de urgență"</string>
<string name="lockscreen_network_locked_message" msgid="2814046965899249635">"Rețea blocată"</string>
<string name="lockscreen_sim_puk_locked_message" msgid="6618356415831082174">"Cardul SIM este blocat cu codul PUK."</string>
@@ -949,18 +949,18 @@
<string name="lockscreen_sim_unlock_progress_dialog_message" msgid="2286497117428409709">"Se deblochează cardul SIM..."</string>
<string name="lockscreen_too_many_failed_attempts_dialog_message" msgid="6458790975898594240">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
<string name="lockscreen_too_many_failed_password_attempts_dialog_message" msgid="3118353451602377380">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
- <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="lockscreen_too_many_failed_pin_attempts_dialog_message" msgid="2874278239714821984">"Ai introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
<string name="lockscreen_failed_attempts_almost_glogin" product="tablet" msgid="3069635524964070596">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați dispozitivul Android TV prin conectarea la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
- <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul datelor de conectare la Google.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string>
- <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator vor fi pierdute."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="tv" msgid="6399092175942158529">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi dispozitivul Android TV prin conectarea la Google.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
+ <string name="lockscreen_failed_attempts_almost_glogin" product="default" msgid="5691623136957148335">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi telefonul cu ajutorul datelor de conectare la Google.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tablet" msgid="7914445759242151426">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, aceasta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="tv" msgid="4275591249631864248">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string>
+ <string name="lockscreen_failed_attempts_almost_at_wipe" product="default" msgid="1166532464798446579">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va fi acum resetată la setările prestabilite din fabrică."</string>
- <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. Acesta va reveni la setările din fabrică."</string>
+ <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. Acesta va reveni la setările din fabrică."</string>
<string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acesta va fi acum resetat la setările prestabilite din fabrică."</string>
- <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Încercați din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string>
- <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Ați uitat modelul?"</string>
+ <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Încearcă din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string>
+ <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Ai uitat modelul?"</string>
<string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Deblocare cont"</string>
<string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Prea multe încercări de desenare a modelului"</string>
<string name="lockscreen_glogin_instructions" msgid="4695162942525531700">"Pentru a debloca, conectați-vă folosind Contul Google."</string>
@@ -970,7 +970,7 @@
<string name="lockscreen_glogin_invalid_input" msgid="4369219936865697679">"Nume de utilizator sau parolă nevalide."</string>
<string name="lockscreen_glogin_account_recovery_hint" msgid="1683405808525090649">"Ați uitat numele de utilizator sau parola?\nAccesați "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="lockscreen_glogin_checking_password" msgid="2607271802803381645">"Se verifică..."</string>
- <string name="lockscreen_unlock_label" msgid="4648257878373307582">"Deblocați"</string>
+ <string name="lockscreen_unlock_label" msgid="4648257878373307582">"Deblochează"</string>
<string name="lockscreen_sound_on_label" msgid="1660281470535492430">"Sunet activat"</string>
<string name="lockscreen_sound_off_label" msgid="2331496559245450053">"Sunet dezactivat"</string>
<string name="lockscreen_access_pattern_start" msgid="3778502525702613399">"Desenarea modelului a început"</string>
@@ -980,7 +980,7 @@
<string name="lockscreen_access_pattern_detected" msgid="3931150554035194012">"Modelul a fost desenat"</string>
<string name="lockscreen_access_pattern_area" msgid="1288780416685002841">"Zonă model."</string>
<string name="keyguard_accessibility_widget_changed" msgid="7298011259508200234">"%1$s. Widget %2$d din %3$d."</string>
- <string name="keyguard_accessibility_add_widget" msgid="8245795023551343672">"Adăugați un widget."</string>
+ <string name="keyguard_accessibility_add_widget" msgid="8245795023551343672">"Adaugă un widget."</string>
<string name="keyguard_accessibility_widget_empty_slot" msgid="544239307077644480">"Gol"</string>
<string name="keyguard_accessibility_unlock_area_expanded" msgid="7768634718706488951">"Zona de deblocare a fost extinsă."</string>
<string name="keyguard_accessibility_unlock_area_collapsed" msgid="4729922043778400434">"Zona de deblocare a fost restrânsă."</string>
@@ -992,7 +992,7 @@
<string name="keyguard_accessibility_widget_reorder_start" msgid="7066213328912939191">"A început reordonarea widgeturilor."</string>
<string name="keyguard_accessibility_widget_reorder_end" msgid="1083806817600593490">"Reordonarea widgeturilor s-a încheiat."</string>
<string name="keyguard_accessibility_widget_deleted" msgid="1509738950119878705">"Widgetul <xliff:g id="WIDGET_INDEX">%1$s</xliff:g> a fost eliminat."</string>
- <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Extindeți zona de deblocare."</string>
+ <string name="keyguard_accessibility_expand_lock_area" msgid="4215280881346033434">"Extinde zona de deblocare."</string>
<string name="keyguard_accessibility_slide_unlock" msgid="2968195219692413046">"Deblocare prin glisare."</string>
<string name="keyguard_accessibility_pattern_unlock" msgid="8669128146589233293">"Deblocare cu model."</string>
<string name="keyguard_accessibility_face_unlock" msgid="4533832120787386728">"Deblocare facială."</string>
@@ -1012,18 +1012,18 @@
<string name="factorytest_failed" msgid="3190979160945298006">"Testarea de fabrică nu a reușit"</string>
<string name="factorytest_not_system" msgid="5658160199925519869">"Acțiunea FACTORY_TEST este acceptată doar pentru pachetele instalate în /system/app."</string>
<string name="factorytest_no_action" msgid="339252838115675515">"Nu s-a găsit niciun pachet care să ofere acțiunea FACTORY_TEST."</string>
- <string name="factorytest_reboot" msgid="2050147445567257365">"Reporniți"</string>
+ <string name="factorytest_reboot" msgid="2050147445567257365">"Repornește"</string>
<string name="js_dialog_title" msgid="7464775045615023241">"La pagina de la „<xliff:g id="TITLE">%s</xliff:g>” apare:"</string>
<string name="js_dialog_title_default" msgid="3769524569903332476">"JavaScript"</string>
- <string name="js_dialog_before_unload_title" msgid="7012587995876771246">"Confirmați părăsirea paginii"</string>
- <string name="js_dialog_before_unload_positive_button" msgid="4274257182303565509">"Părăsiți această pagină"</string>
+ <string name="js_dialog_before_unload_title" msgid="7012587995876771246">"Confirmă părăsirea paginii"</string>
+ <string name="js_dialog_before_unload_positive_button" msgid="4274257182303565509">"Părăsește această pagină"</string>
<string name="js_dialog_before_unload_negative_button" msgid="3873765747622415310">"Rămâneți în această pagină"</string>
- <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nSigur doriți să părăsiți această pagină?"</string>
- <string name="save_password_label" msgid="9161712335355510035">"Confirmați"</string>
- <string name="double_tap_toast" msgid="7065519579174882778">"Sfat: măriți și micșorați prin dublă atingere."</string>
+ <string name="js_dialog_before_unload" msgid="7213364985774778744">"<xliff:g id="MESSAGE">%s</xliff:g>\n\nSigur părăsești această pagină?"</string>
+ <string name="save_password_label" msgid="9161712335355510035">"Confirmă"</string>
+ <string name="double_tap_toast" msgid="7065519579174882778">"Sfat: mărește și micșorează prin dublă atingere."</string>
<string name="autofill_this_form" msgid="3187132440451621492">"Automat"</string>
<string name="setup_autofill" msgid="5431369130866618567">"Conf.Compl.auto."</string>
- <string name="autofill_window_title" msgid="4379134104008111961">"Completați automat cu <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
+ <string name="autofill_window_title" msgid="4379134104008111961">"Completează automat cu <xliff:g id="SERVICENAME">%1$s</xliff:g>"</string>
<string name="autofill_address_name_separator" msgid="8190155636149596125">" "</string>
<string name="autofill_address_summary_name_format" msgid="3402882515222673691">"$1$2$3"</string>
<string name="autofill_address_summary_separator" msgid="760522655085707045">", "</string>
@@ -1054,9 +1054,9 @@
<string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Permite aplicației să modifice permisiunile privind locația geografică a browserului. Aplicațiile rău intenționate pot utiliza această permisiune pentru a permite trimiterea informațiilor privind locația către site-uri web arbitrare."</string>
<string name="save_password_message" msgid="2146409467245462965">"Doriți ca browserul să rețină această parolă?"</string>
<string name="save_password_notnow" msgid="2878327088951240061">"Nu acum"</string>
- <string name="save_password_remember" msgid="6490888932657708341">"Rețineți"</string>
+ <string name="save_password_remember" msgid="6490888932657708341">"Reține"</string>
<string name="save_password_never" msgid="6776808375903410659">"Niciodată"</string>
- <string name="open_permission_deny" msgid="5136793905306987251">"Nu aveți permisiunea de a deschide această pagină."</string>
+ <string name="open_permission_deny" msgid="5136793905306987251">"Nu ai permisiunea de a deschide această pagină."</string>
<string name="text_copied" msgid="2531420577879738860">"Text copiat în clipboard."</string>
<string name="pasted_from_app" msgid="5627698450808256545">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat date din <xliff:g id="SOURCE_APP_NAME">%2$s</xliff:g>"</string>
<string name="pasted_from_clipboard" msgid="7355790625710831847">"<xliff:g id="PASTING_APP_NAME">%1$s</xliff:g> a inserat din clipboard"</string>
@@ -1074,16 +1074,16 @@
<string name="menu_space_shortcut_label" msgid="5949311515646872071">"spațiu"</string>
<string name="menu_enter_shortcut_label" msgid="6709499510082897320">"enter"</string>
<string name="menu_delete_shortcut_label" msgid="4365787714477739080">"delete"</string>
- <string name="search_go" msgid="2141477624421347086">"Căutați"</string>
- <string name="search_hint" msgid="455364685740251925">"Căutați…"</string>
- <string name="searchview_description_search" msgid="1045552007537359343">"Căutați"</string>
+ <string name="search_go" msgid="2141477624421347086">"Caută"</string>
+ <string name="search_hint" msgid="455364685740251925">"Caută…"</string>
+ <string name="searchview_description_search" msgid="1045552007537359343">"Caută"</string>
<string name="searchview_description_query" msgid="7430242366971716338">"Interogare de căutare"</string>
- <string name="searchview_description_clear" msgid="1989371719192982900">"Ștergeți interogarea"</string>
- <string name="searchview_description_submit" msgid="6771060386117334686">"Trimiteți interogarea"</string>
+ <string name="searchview_description_clear" msgid="1989371719192982900">"Șterge interogarea"</string>
+ <string name="searchview_description_submit" msgid="6771060386117334686">"Trimite interogarea"</string>
<string name="searchview_description_voice" msgid="42360159504884679">"Căutare vocală"</string>
<string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Activați Explorați prin atingere?"</string>
- <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> dorește să activeze funcția Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacționa cu tableta."</string>
- <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> dorește să activeze funcția Explorați prin atingere. Când această funcție este activată, puteți auzi sau vedea descrieri pentru ceea ce se află sub degetul dvs. sau puteți efectua gesturi pentru a interacționa cu telefonul."</string>
+ <string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> vrea să activeze funcția Explorează prin atingere. Când e activată, poți auzi sau vedea descrieri pentru ceea ce se află sub degetul tău sau poți face gesturi pentru a interacționa cu tableta."</string>
+ <string name="enable_explore_by_touch_warning_message" product="default" msgid="4312979647356179250">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> dorește să activeze funcția Explorează prin atingere. Când aceasta e activată, poți auzi sau vedea descrieri pentru ceea ce se află sub degetul tău sau poți face gesturi pentru a interacționa cu telefonul."</string>
<string name="oneMonthDurationPast" msgid="4538030857114635777">"cu 1 lună în urmă"</string>
<string name="beforeOneMonthDurationPast" msgid="8315149541372065392">"Cu mai mult de 1 lună în urmă"</string>
<string name="last_num_days" msgid="2393660431490280537">"{count,plural, =1{Ultima zi}few{Ultimele # zile}other{Ultimele # de zile}}"</string>
@@ -1132,22 +1132,22 @@
<string name="Midnight" msgid="8176019203622191377">"Miezul nopții"</string>
<string name="elapsed_time_short_format_mm_ss" msgid="8689459651807876423">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
<string name="elapsed_time_short_format_h_mm_ss" msgid="2302144714803345056">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
- <string name="selectAll" msgid="1532369154488982046">"Selectați-le pe toate"</string>
- <string name="cut" msgid="2561199725874745819">"Decupați"</string>
- <string name="copy" msgid="5472512047143665218">"Copiați"</string>
+ <string name="selectAll" msgid="1532369154488982046">"Selectează-le pe toate"</string>
+ <string name="cut" msgid="2561199725874745819">"Decupează"</string>
+ <string name="copy" msgid="5472512047143665218">"Copiază"</string>
<string name="failed_to_copy_to_clipboard" msgid="725919885138539875">"Eroare la copierea în clipboard"</string>
- <string name="paste" msgid="461843306215520225">"Inserați"</string>
- <string name="paste_as_plain_text" msgid="7664800665823182587">"Inserați ca text simplu"</string>
+ <string name="paste" msgid="461843306215520225">"Inserează"</string>
+ <string name="paste_as_plain_text" msgid="7664800665823182587">"Inserează ca text simplu"</string>
<string name="replace" msgid="7842675434546657444">"Înlocuiți..."</string>
- <string name="delete" msgid="1514113991712129054">"Ștergeți"</string>
- <string name="copyUrl" msgid="6229645005987260230">"Copiați adresa URL"</string>
- <string name="selectTextMode" msgid="3225108910999318778">"Selectați text"</string>
- <string name="undo" msgid="3175318090002654673">"Anulați"</string>
- <string name="redo" msgid="7231448494008532233">"Repetați"</string>
+ <string name="delete" msgid="1514113991712129054">"Șterge"</string>
+ <string name="copyUrl" msgid="6229645005987260230">"Copiază adresa URL"</string>
+ <string name="selectTextMode" msgid="3225108910999318778">"Selectează text"</string>
+ <string name="undo" msgid="3175318090002654673">"Anulează"</string>
+ <string name="redo" msgid="7231448494008532233">"Repetă"</string>
<string name="autofill" msgid="511224882647795296">"Completare automată"</string>
<string name="textSelectionCABTitle" msgid="5151441579532476940">"Selectare text"</string>
- <string name="addToDictionary" msgid="8041821113480950096">"Adăugați în dicționar"</string>
- <string name="deleteText" msgid="4200807474529938112">"Ștergeți"</string>
+ <string name="addToDictionary" msgid="8041821113480950096">"Adaugă în dicționar"</string>
+ <string name="deleteText" msgid="4200807474529938112">"Șterge"</string>
<string name="inputMethod" msgid="1784759500516314751">"Metodă de intrare"</string>
<string name="editTextMenuTitle" msgid="857666911134482176">"Acțiuni pentru text"</string>
<string name="input_method_nav_back_button_desc" msgid="3655838793765691787">"Înapoi"</string>
@@ -1156,11 +1156,11 @@
<string name="low_internal_storage_view_text" msgid="8172166728369697835">"Este posibil ca unele funcții de sistem să nu funcționeze"</string>
<string name="low_internal_storage_view_text_no_boot" msgid="7368968163411251788">"Spațiu de stocare insuficient pentru sistem. Asigurați-vă că aveți 250 MB de spațiu liber și reporniți."</string>
<string name="app_running_notification_title" msgid="8985999749231486569">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează acum"</string>
- <string name="app_running_notification_text" msgid="5120815883400228566">"Atingeți pentru mai multe informații sau pentru a opri aplicația."</string>
+ <string name="app_running_notification_text" msgid="5120815883400228566">"Atinge pentru mai multe informații sau pentru a opri aplicația."</string>
<string name="ok" msgid="2646370155170753815">"OK"</string>
- <string name="cancel" msgid="6908697720451760115">"Anulați"</string>
+ <string name="cancel" msgid="6908697720451760115">"Anulează"</string>
<string name="yes" msgid="9069828999585032361">"OK"</string>
- <string name="no" msgid="5122037903299899715">"Anulați"</string>
+ <string name="no" msgid="5122037903299899715">"Anulează"</string>
<string name="dialog_alert_title" msgid="651856561974090712">"Atenție"</string>
<string name="loading" msgid="3138021523725055037">"Se încarcă…"</string>
<string name="capital_on" msgid="2770685323900821829">"DA"</string>
@@ -1174,45 +1174,45 @@
<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>
- <string name="whichViewApplication" msgid="5733194231473132945">"Deschideți cu"</string>
- <string name="whichViewApplicationNamed" msgid="415164730629690105">"Deschideți cu %1$s"</string>
- <string name="whichViewApplicationLabel" msgid="7367556735684742409">"Deschideți"</string>
- <string name="whichOpenHostLinksWith" msgid="7645631470199397485">"Deschideți linkurile <xliff:g id="HOST">%1$s</xliff:g> cu"</string>
- <string name="whichOpenLinksWith" msgid="1120936181362907258">"Deschideți linkurile cu"</string>
- <string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Deschideți linkurile cu <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
- <string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Deschideți linkurile <xliff:g id="HOST">%1$s</xliff:g> cu <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
- <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Permiteți accesul"</string>
- <string name="whichEditApplication" msgid="6191568491456092812">"Editați cu"</string>
- <string name="whichEditApplicationNamed" msgid="8096494987978521514">"Editați cu %1$s"</string>
- <string name="whichEditApplicationLabel" msgid="1463288652070140285">"Editați"</string>
- <string name="whichSendApplication" msgid="4143847974460792029">"Trimiteți"</string>
- <string name="whichSendApplicationNamed" msgid="4470386782693183461">"Distribuiți cu %1$s"</string>
- <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Trimiteți"</string>
- <string name="whichSendToApplication" msgid="77101541959464018">"Trimiteți folosind"</string>
- <string name="whichSendToApplicationNamed" msgid="3385686512014670003">"Trimiteți folosind %1$s"</string>
- <string name="whichSendToApplicationLabel" msgid="3543240188816513303">"Trimiteți"</string>
- <string name="whichHomeApplication" msgid="8276350727038396616">"Selectați o aplicație de pe ecranul de pornire"</string>
+ <string name="whichViewApplication" msgid="5733194231473132945">"Deschide cu"</string>
+ <string name="whichViewApplicationNamed" msgid="415164730629690105">"Deschide cu %1$s"</string>
+ <string name="whichViewApplicationLabel" msgid="7367556735684742409">"Deschide"</string>
+ <string name="whichOpenHostLinksWith" msgid="7645631470199397485">"Deschide linkurile <xliff:g id="HOST">%1$s</xliff:g> cu"</string>
+ <string name="whichOpenLinksWith" msgid="1120936181362907258">"Deschide linkurile cu"</string>
+ <string name="whichOpenLinksWithApp" msgid="6917864367861910086">"Deschide linkurile cu <xliff:g id="APPLICATION">%1$s</xliff:g>"</string>
+ <string name="whichOpenHostLinksWithApp" msgid="2401668560768463004">"Deschide linkurile <xliff:g id="HOST">%1$s</xliff:g> cu <xliff:g id="APPLICATION">%2$s</xliff:g>"</string>
+ <string name="whichGiveAccessToApplicationLabel" msgid="7805857277166106236">"Permite accesul"</string>
+ <string name="whichEditApplication" msgid="6191568491456092812">"Editează cu"</string>
+ <string name="whichEditApplicationNamed" msgid="8096494987978521514">"Editează cu %1$s"</string>
+ <string name="whichEditApplicationLabel" msgid="1463288652070140285">"Editează"</string>
+ <string name="whichSendApplication" msgid="4143847974460792029">"Trimite"</string>
+ <string name="whichSendApplicationNamed" msgid="4470386782693183461">"Distribuie cu %1$s"</string>
+ <string name="whichSendApplicationLabel" msgid="7467813004769188515">"Trimite"</string>
+ <string name="whichSendToApplication" msgid="77101541959464018">"Trimite folosind"</string>
+ <string name="whichSendToApplicationNamed" msgid="3385686512014670003">"Trimite folosind %1$s"</string>
+ <string name="whichSendToApplicationLabel" msgid="3543240188816513303">"Trimite"</string>
+ <string name="whichHomeApplication" msgid="8276350727038396616">"Selectează o aplicație de pe ecranul de pornire"</string>
<string name="whichHomeApplicationNamed" msgid="5855990024847433794">"Utilizați %1$s ca ecran de pornire"</string>
- <string name="whichHomeApplicationLabel" msgid="8907334282202933959">"Fotografiați"</string>
- <string name="whichImageCaptureApplication" msgid="2737413019463215284">"Fotografiați cu"</string>
- <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Fotografiați cu %1$s"</string>
- <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Fotografiați"</string>
+ <string name="whichHomeApplicationLabel" msgid="8907334282202933959">"Fotografiază"</string>
+ <string name="whichImageCaptureApplication" msgid="2737413019463215284">"Fotografiază cu"</string>
+ <string name="whichImageCaptureApplicationNamed" msgid="8820702441847612202">"Fotografiază cu %1$s"</string>
+ <string name="whichImageCaptureApplicationLabel" msgid="6505433734824988277">"Fotografiază"</string>
<string name="alwaysUse" msgid="3153558199076112903">"Se utilizează în mod prestabilit pentru această acțiune."</string>
<string name="use_a_different_app" msgid="4987790276170972776">"Utilizați altă aplicație"</string>
- <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Ștergeți setările prestabilite din Setări de sistem &gt; Aplicații &gt; Descărcate."</string>
- <string name="chooseActivity" msgid="8563390197659779956">"Alegeți o acțiune"</string>
- <string name="chooseUsbActivity" msgid="2096269989990986612">"Alegeți o aplicație pentru dispozitivul USB"</string>
+ <string name="clearDefaultHintMsg" msgid="1325866337702524936">"Șterge setările prestabilite din Setări de sistem &gt; Aplicații &gt; Descărcate."</string>
+ <string name="chooseActivity" msgid="8563390197659779956">"Alege o acțiune"</string>
+ <string name="chooseUsbActivity" msgid="2096269989990986612">"Alege o aplicație pentru dispozitivul USB"</string>
<string name="noApplications" msgid="1186909265235544019">"Această acțiune nu poate fi efectuată de nicio aplicație."</string>
<string name="aerr_application" msgid="4090916809370389109">"<xliff:g id="APPLICATION">%1$s</xliff:g> s-a oprit"</string>
<string name="aerr_process" msgid="4268018696970966407">"<xliff:g id="PROCESS">%1$s</xliff:g> s-a oprit"</string>
<string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> se oprește încontinuu"</string>
<string name="aerr_process_repeated" msgid="1153152413537954974">"<xliff:g id="PROCESS">%1$s</xliff:g> se oprește încontinuu"</string>
- <string name="aerr_restart" msgid="2789618625210505419">"Deschideți din nou aplicația"</string>
- <string name="aerr_report" msgid="3095644466849299308">"Trimiteți feedback"</string>
- <string name="aerr_close" msgid="3398336821267021852">"Închideți"</string>
- <string name="aerr_mute" msgid="2304972923480211376">"Dezactivați până la repornirea dispozitivului"</string>
- <string name="aerr_wait" msgid="3198677780474548217">"Așteptați"</string>
- <string name="aerr_close_app" msgid="8318883106083050970">"Închideți aplicația"</string>
+ <string name="aerr_restart" msgid="2789618625210505419">"Deschide din nou aplicația"</string>
+ <string name="aerr_report" msgid="3095644466849299308">"Trimite feedback"</string>
+ <string name="aerr_close" msgid="3398336821267021852">"Închide"</string>
+ <string name="aerr_mute" msgid="2304972923480211376">"Dezactivează până la repornirea dispozitivului"</string>
+ <string name="aerr_wait" msgid="3198677780474548217">"Așteaptă"</string>
+ <string name="aerr_close_app" msgid="8318883106083050970">"Închide aplicația"</string>
<string name="anr_title" msgid="7290329487067300120"></string>
<string name="anr_activity_application" msgid="8121716632960340680">"<xliff:g id="APPLICATION">%2$s</xliff:g> nu răspunde"</string>
<string name="anr_activity_process" msgid="3477362583767128667">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nu răspunde"</string>
@@ -1220,19 +1220,19 @@
<string name="anr_process" msgid="1664277165911816067">"Procesul <xliff:g id="PROCESS">%1$s</xliff:g> nu răspunde"</string>
<string name="force_close" msgid="9035203496368973803">"OK"</string>
<string name="report" msgid="2149194372340349521">"Raportați"</string>
- <string name="wait" msgid="7765985809494033348">"Așteptați"</string>
- <string name="webpage_unresponsive" msgid="7850879412195273433">"Pagina a devenit inactivă.\n\nDoriți să o închideți?"</string>
+ <string name="wait" msgid="7765985809494033348">"Așteaptă"</string>
+ <string name="webpage_unresponsive" msgid="7850879412195273433">"Pagina a devenit inactivă.\n\nO închizi?"</string>
<string name="launch_warning_title" msgid="6725456009564953595">"Aplicație redirecționată"</string>
<string name="launch_warning_replace" msgid="3073392976283203402">"<xliff:g id="APP_NAME">%1$s</xliff:g> funcționează acum."</string>
<string name="launch_warning_original" msgid="3332206576800169626">"<xliff:g id="APP_NAME">%1$s</xliff:g> a fost lansată inițial."</string>
<string name="screen_compat_mode_scale" msgid="8627359598437527726">"Scară"</string>
- <string name="screen_compat_mode_show" msgid="5080361367584709857">"Afișați întotdeauna"</string>
- <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reactivați acest mod din Setări de sistem &gt; Aplicații &gt; Descărcate."</string>
+ <string name="screen_compat_mode_show" msgid="5080361367584709857">"Afișează întotdeauna"</string>
+ <string name="screen_compat_mode_hint" msgid="4032272159093750908">"Reactivează acest mod din Setări de sistem &gt; Aplicații &gt; Descărcate."</string>
<string name="unsupported_display_size_message" msgid="7265211375269394699">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu acceptă setarea actuală pentru Dimensiunea afișării și este posibil să aibă un comportament neașteptat."</string>
<string name="unsupported_display_size_show" msgid="980129850974919375">"Afișează întotdeauna"</string>
<string name="unsupported_compile_sdk_message" msgid="7326293500707890537">"<xliff:g id="APP_NAME">%1$s</xliff:g> a fost concepută pentru o versiune incompatibilă de sistem de operare Android și este posibil să se comporte în mod neprevăzut. Poate fi disponibilă o versiune actualizată a aplicației."</string>
- <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Afișați întotdeauna"</string>
- <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Căutați actualizări"</string>
+ <string name="unsupported_compile_sdk_show" msgid="1601210057960312248">"Afișează întotdeauna"</string>
+ <string name="unsupported_compile_sdk_check_update" msgid="1103639989147664456">"Caută actualizări"</string>
<string name="smv_application" msgid="3775183542777792638">"Aplicația <xliff:g id="APPLICATION">%1$s</xliff:g> (procesul <xliff:g id="PROCESS">%2$s</xliff:g>) a încălcat propria politică StrictMode."</string>
<string name="smv_process" msgid="1398801497130695446">"Procesul <xliff:g id="PROCESS">%1$s</xliff:g> a încălcat propria politică StrictMode."</string>
<string name="android_upgrading_title" product="default" msgid="7279077384220829683">"Se actualizează telefonul.…"</string>
@@ -1250,27 +1250,27 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Se pornesc aplicațiile."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Se finalizează pornirea."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ați apăsat butonul de pornire. De obicei, această acțiune dezactivează ecranul.\n\nAtingeți ușor când vă configurați amprenta."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Atinge pentru a dezactiva ecranul"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Dezactivează ecranul"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Ca să termini configurarea, dezactivează ecranul"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Dezactivează"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuați cu verificarea amprentei?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ați apăsat butonul de pornire. De obicei, această acțiune dezactivează ecranul.\n\nAtingeți ușor pentru verificarea amprentei."</string>
- <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Dezactivați ecranul"</string>
- <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuați"</string>
+ <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Dezactivează ecranul"</string>
+ <string name="fp_power_button_bp_negative_button" msgid="3971364246496775178">"Continuă"</string>
<string name="heavy_weight_notification" msgid="8382784283600329576">"Rulează <xliff:g id="APP">%1$s</xliff:g>"</string>
- <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Atingeți pentru a reveni la joc"</string>
- <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Alegeți jocul"</string>
+ <string name="heavy_weight_notification_detail" msgid="6802247239468404078">"Atinge pentru a reveni la joc"</string>
+ <string name="heavy_weight_switcher_title" msgid="3861984210040100886">"Alege jocul"</string>
<string name="heavy_weight_switcher_text" msgid="6814316627367160126">"Pentru o performanță mai bună, se poate deschide un singur joc odată."</string>
- <string name="old_app_action" msgid="725331621042848590">"Reveniți la <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
- <string name="new_app_action" msgid="547772182913269801">"Deschideți <xliff:g id="NEW_APP">%1$s</xliff:g>"</string>
+ <string name="old_app_action" msgid="725331621042848590">"Revino la <xliff:g id="OLD_APP">%1$s</xliff:g>"</string>
+ <string name="new_app_action" msgid="547772182913269801">"Deschide <xliff:g id="NEW_APP">%1$s</xliff:g>"</string>
<string name="new_app_description" msgid="1958903080400806644">"<xliff:g id="OLD_APP">%1$s</xliff:g> se va închide fără să salveze"</string>
<string name="dump_heap_notification" msgid="5316644945404825032">"<xliff:g id="PROC">%1$s</xliff:g> a depășit limita de memorie"</string>
<string name="dump_heap_ready_notification" msgid="2302452262927390268">"Datele privind memoria heap <xliff:g id="PROC">%1$s</xliff:g> sunt gata"</string>
- <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Datele privind memoria au fost culese. Atingeți pentru a trimite."</string>
- <string name="dump_heap_title" msgid="4367128917229233901">"Trimiteți datele privind memoria?"</string>
- <string name="dump_heap_text" msgid="1692649033835719336">"Procesul <xliff:g id="PROC">%1$s</xliff:g> și-a depășit limita de memorie de <xliff:g id="SIZE">%2$s</xliff:g>. Sunt disponibile datele privind memoria heap, pe care le puteți trimite dezvoltatorului. Atenție: aceste date privind memoria heap pot conține informații cu caracter personal la care aplicația are acces."</string>
+ <string name="dump_heap_notification_detail" msgid="8431586843001054050">"Datele privind memoria au fost culese. Atinge pentru a trimite."</string>
+ <string name="dump_heap_title" msgid="4367128917229233901">"Trimiți datele privind memoria?"</string>
+ <string name="dump_heap_text" msgid="1692649033835719336">"Procesul <xliff:g id="PROC">%1$s</xliff:g> și-a depășit limita de memorie de <xliff:g id="SIZE">%2$s</xliff:g>. Sunt disponibile datele privind memoria heap, pe care le poți trimite dezvoltatorului. Atenție: aceste date privind memoria heap pot conține informații cu caracter personal la care aplicația are acces."</string>
<string name="dump_heap_system_text" msgid="6805155514925350849">"Procesul <xliff:g id="PROC">%1$s</xliff:g> a depășit limita de memorie de <xliff:g id="SIZE">%2$s</xliff:g>. Sunt disponibile datele privind memoria heap, pe care le puteți distribui. Atenție: aceste date privind memoria heap pot conține informații cu caracter personal sensibile la care procesul are acces și care pot include ceea ce tastați."</string>
<string name="dump_heap_ready_text" msgid="5849618132123045516">"Sunt disponibile datele privind memoria heap a procesului <xliff:g id="PROC">%1$s</xliff:g>, pe care le puteți distribui. Atenție: aceste date privind memoria heap pot conține informații cu caracter personal sensibile la care procesul are acces și care pot include ceea ce tastați."</string>
- <string name="sendText" msgid="493003724401350724">"Alegeți o acțiune pentru text"</string>
+ <string name="sendText" msgid="493003724401350724">"Alege o acțiune pentru text"</string>
<string name="volume_ringtone" msgid="134784084629229029">"Volum sonerie"</string>
<string name="volume_music" msgid="7727274216734955095">"Volum media"</string>
<string name="volume_music_hint_playing_through_bluetooth" msgid="2614142915948898228">"Redare prin Bluetooth"</string>
@@ -1292,12 +1292,12 @@
<string name="ringtone_picker_title_alarm" msgid="7438934548339024767">"Sunete de alarmă"</string>
<string name="ringtone_picker_title_notification" msgid="6387191794719608122">"Sunete pentru notificare"</string>
<string name="ringtone_unknown" msgid="5059495249862816475">"Necunoscut"</string>
- <string name="wifi_available_sign_in" msgid="381054692557675237">"Conectați-vă la rețeaua Wi-Fi"</string>
- <string name="network_available_sign_in" msgid="1520342291829283114">"Conectați-vă la rețea"</string>
+ <string name="wifi_available_sign_in" msgid="381054692557675237">"Conectează-te la rețeaua Wi-Fi"</string>
+ <string name="network_available_sign_in" msgid="1520342291829283114">"Conectează-te la rețea"</string>
<!-- no translation found for network_available_sign_in_detailed (7520423801613396556) -->
<skip />
<string name="wifi_no_internet" msgid="1386911698276448061">"<xliff:g id="NETWORK_SSID">%1$s</xliff:g> nu are acces la internet"</string>
- <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Atingeți pentru opțiuni"</string>
+ <string name="wifi_no_internet_detailed" msgid="634938444133558942">"Atinge pentru opțiuni"</string>
<string name="mobile_no_internet" msgid="4014455157529909781">"Rețeaua mobilă nu are acces la internet"</string>
<string name="other_networks_no_internet" msgid="6698711684200067033">"Rețeaua nu are acces la internet"</string>
<string name="private_dns_broken_detailed" msgid="3709388271074611847">"Serverul DNS privat nu poate fi accesat"</string>
@@ -1315,36 +1315,36 @@
</string-array>
<string name="network_switch_type_name_unknown" msgid="3665696841646851068">"un tip de rețea necunoscut"</string>
<string name="accept" msgid="5447154347815825107">"Acceptați"</string>
- <string name="decline" msgid="6490507610282145874">"Refuzați"</string>
- <string name="select_character" msgid="3352797107930786979">"Introduceți caracterul"</string>
+ <string name="decline" msgid="6490507610282145874">"Refuz"</string>
+ <string name="select_character" msgid="3352797107930786979">"Introdu caracterul"</string>
<string name="sms_control_title" msgid="4748684259903148341">"Se trimit mesaje SMS"</string>
- <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; trimite un număr mare de mesaje SMS. Permiteți acestei aplicații să trimită în continuare mesaje?"</string>
- <string name="sms_control_yes" msgid="4858845109269524622">"Permiteți"</string>
- <string name="sms_control_no" msgid="4845717880040355570">"Refuzați"</string>
+ <string name="sms_control_message" msgid="6574313876316388239">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; trimite un număr mare de mesaje SMS. Permiți acestei aplicații să trimită în continuare mesaje?"</string>
+ <string name="sms_control_yes" msgid="4858845109269524622">"Permite"</string>
+ <string name="sms_control_no" msgid="4845717880040355570">"Refuz"</string>
<string name="sms_short_code_confirm_message" msgid="1385416688897538724">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; intenționează să trimită un mesaj la &lt;b&gt;<xliff:g id="DEST_ADDRESS">%2$s</xliff:g>&lt;/b&gt;."</string>
- <string name="sms_short_code_details" msgid="2723725738333388351">"Acest lucru "<b>"poate genera costuri"</b>" în contul dvs. mobil."</string>
- <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Acest lucru va genera costuri în contul dvs. mobil."</b></string>
- <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Trimiteți"</string>
- <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Anulați"</string>
+ <string name="sms_short_code_details" msgid="2723725738333388351">"Acest lucru "<b>"poate genera costuri"</b>" în contul tău mobil."</string>
+ <string name="sms_premium_short_code_details" msgid="1400296309866638111"><b>"Acest lucru va genera costuri în contul tău mobil."</b></string>
+ <string name="sms_short_code_confirm_allow" msgid="920477594325526691">"Trimite"</string>
+ <string name="sms_short_code_confirm_deny" msgid="1356917469323768230">"Anulează"</string>
<string name="sms_short_code_remember_choice" msgid="1374526438647744862">"Doresc să se rețină opțiunea"</string>
- <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Puteți modifica ulterior în Setări &gt; Aplicații"</string>
- <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Permiteți întotdeauna"</string>
- <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nu permiteți niciodată"</string>
+ <string name="sms_short_code_remember_undo_instruction" msgid="2620984439143080410">"Poți modifica ulterior în Setări &gt; Aplicații"</string>
+ <string name="sms_short_code_confirm_always_allow" msgid="2223014893129755950">"Permite întotdeauna"</string>
+ <string name="sms_short_code_confirm_never_allow" msgid="2688828813521652079">"Nu permite niciodată"</string>
<string name="sim_removed_title" msgid="5387212933992546283">"Card SIM eliminat"</string>
<string name="sim_removed_message" msgid="9051174064474904617">"Rețeaua mobilă va fi indisponibilă până când reporniți cu un card SIM valid introdus."</string>
<string name="sim_done_button" msgid="6464250841528410598">"Terminat"</string>
<string name="sim_added_title" msgid="7930779986759414595">"Card SIM adăugat"</string>
- <string name="sim_added_message" msgid="6602906609509958680">"Reporniți dispozitivul pentru a accesa rețeaua mobilă."</string>
- <string name="sim_restart_button" msgid="8481803851341190038">"Reporniți"</string>
- <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Activați serviciul mobil"</string>
+ <string name="sim_added_message" msgid="6602906609509958680">"Repornește dispozitivul pentru a accesa rețeaua mobilă."</string>
+ <string name="sim_restart_button" msgid="8481803851341190038">"Repornește"</string>
+ <string name="install_carrier_app_notification_title" msgid="5712723402213090102">"Activează serviciul mobil"</string>
<string name="install_carrier_app_notification_text" msgid="2781317581274192728">"Descărcați aplicația operatorului pentru a vă activa noul card SIM"</string>
- <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Descărcați aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> pentru a vă activa noul card SIM"</string>
- <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Descărcați aplicația"</string>
+ <string name="install_carrier_app_notification_text_app_name" msgid="4086877327264106484">"Descarcă aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> pentru a-ți activa noul card SIM"</string>
+ <string name="install_carrier_app_notification_button" msgid="6257740533102594290">"Descarcă aplicația"</string>
<string name="carrier_app_notification_title" msgid="5815477368072060250">"S-a introdus un card SIM nou"</string>
- <string name="carrier_app_notification_text" msgid="6567057546341958637">"Atingeți pentru a-l configura"</string>
- <string name="time_picker_dialog_title" msgid="9053376764985220821">"Setați ora"</string>
- <string name="date_picker_dialog_title" msgid="5030520449243071926">"Setați data"</string>
- <string name="date_time_set" msgid="4603445265164486816">"Setați"</string>
+ <string name="carrier_app_notification_text" msgid="6567057546341958637">"Atinge pentru a-l configura"</string>
+ <string name="time_picker_dialog_title" msgid="9053376764985220821">"Setează ora"</string>
+ <string name="date_picker_dialog_title" msgid="5030520449243071926">"Setează data"</string>
+ <string name="date_time_set" msgid="4603445265164486816">"Setează"</string>
<string name="date_time_done" msgid="8363155889402873463">"Terminat"</string>
<string name="perms_new_perm_prefix" msgid="6984556020395757087"><font size="12" fgcolor="#ff33b5e5">"NOU: "</font></string>
<string name="perms_description_app" msgid="2747752389870161996">"Furnizată de <xliff:g id="APP_NAME">%1$s</xliff:g>."</string>
@@ -1358,18 +1358,18 @@
<string name="usb_tether_notification_title" msgid="8828527870612663771">"Tetheringul prin USB este activat"</string>
<string name="usb_midi_notification_title" msgid="7404506788950595557">"MIDI prin USB este activat"</string>
<string name="usb_accessory_notification_title" msgid="1385394660861956980">"Accesoriu USB conectat"</string>
- <string name="usb_notification_message" msgid="4715163067192110676">"Atingeți pentru mai multe opțiuni."</string>
- <string name="usb_power_notification_message" msgid="7284765627437897702">"Se încarcă dispozitivul conectat. Atingeți pentru mai multe opțiuni."</string>
+ <string name="usb_notification_message" msgid="4715163067192110676">"Atinge pentru mai multe opțiuni."</string>
+ <string name="usb_power_notification_message" msgid="7284765627437897702">"Se încarcă dispozitivul conectat. Atinge pentru mai multe opțiuni."</string>
<string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"S-a detectat un accesoriu audio analogic"</string>
- <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Dispozitivul atașat nu este compatibil cu acest telefon. Atingeți pentru a afla mai multe."</string>
+ <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Dispozitivul atașat nu este compatibil cu acest telefon. Atinge pentru a afla mai multe."</string>
<string name="adb_active_notification_title" msgid="408390247354560331">"Remedierea erorilor prin USB conectată"</string>
- <string name="adb_active_notification_message" msgid="5617264033476778211">"Atingeți pentru a dezactiva."</string>
- <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Selectați pentru a dezactiva remedierea erorilor prin USB."</string>
+ <string name="adb_active_notification_message" msgid="5617264033476778211">"Atinge pentru a dezactiva."</string>
+ <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Selectează pentru a dezactiva remedierea erorilor prin USB."</string>
<string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Remedierea erorilor wireless este activă"</string>
<string name="adbwifi_active_notification_message" msgid="930987922852867972">"Atinge pentru a dezactiva remedierea erorilor wireless"</string>
- <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selectați pentru a dezactiva remedierea erorilor wireless."</string>
+ <string name="adbwifi_active_notification_message" product="tv" msgid="8633421848366915478">"Selectează pentru a dezactiva remedierea erorilor wireless."</string>
<string name="test_harness_mode_notification_title" msgid="2282785860014142511">"Modul Set de testare este activat"</string>
- <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Reveniți la setările din fabrică pentru a dezactiva modul Set de testare."</string>
+ <string name="test_harness_mode_notification_message" msgid="3039123743127958420">"Revino la setările din fabrică pentru a dezactiva modul Set de testare."</string>
<string name="console_running_notification_title" msgid="6087888939261635904">"Consola din serie este activată"</string>
<string name="console_running_notification_message" msgid="7892751888125174039">"Performanța este afectată. Pentru a dezactiva, verificați programul bootloader."</string>
<string name="mte_override_notification_title" msgid="4731115381962792944">"MTE experimentală activată"</string>
@@ -1379,61 +1379,61 @@
<string name="usb_contaminant_not_detected_title" msgid="2651167729563264053">"Portul USB poate fi folosit"</string>
<string name="usb_contaminant_not_detected_message" msgid="892863190942660462">"Telefonul nu mai detectează lichide sau reziduuri."</string>
<string name="taking_remote_bugreport_notification_title" msgid="1582531382166919850">"Se creează un raport de eroare…"</string>
- <string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"Trimiteți raportul de eroare?"</string>
+ <string name="share_remote_bugreport_notification_title" msgid="6708897723753334999">"Trimiți raportul de eroare?"</string>
<string name="sharing_remote_bugreport_notification_title" msgid="3077385149217638550">"Se trimite raportul de eroare…"</string>
<string name="share_remote_bugreport_notification_message_finished" msgid="7325635795739260135">"Administratorul dvs. a solicitat un raport de eroare pentru a remedia problemele acestui dispozitiv. Este posibil să se permită accesul la date și aplicații."</string>
<string name="share_remote_bugreport_action" msgid="7630880678785123682">"TRIMITEȚI"</string>
<string name="decline_remote_bugreport_action" msgid="4040894777519784346">"REFUZAȚI"</string>
- <string name="select_input_method" msgid="3971267998568587025">"Alegeți metoda de introducere de text"</string>
+ <string name="select_input_method" msgid="3971267998568587025">"Alege metoda de introducere de text"</string>
<string name="show_ime" msgid="6406112007347443383">"Se păstrează pe ecran cât timp este activată tastatura fizică"</string>
- <string name="hardware" msgid="1800597768237606953">"Afișați tastatura virtuală"</string>
- <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Configurați tastatura fizică"</string>
- <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Atingeți pentru a selecta limba și aspectul"</string>
+ <string name="hardware" msgid="1800597768237606953">"Afișează tastatura virtuală"</string>
+ <string name="select_keyboard_layout_notification_title" msgid="4427643867639774118">"Configurează tastatura fizică"</string>
+ <string name="select_keyboard_layout_notification_message" msgid="8835158247369158154">"Atinge pentru a selecta limba și aspectul"</string>
<string name="fast_scroll_alphabet" msgid="8854435958703888376">" ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="fast_scroll_numeric_alphabet" msgid="2529539945421557329">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string>
<string name="alert_windows_notification_channel_group_name" msgid="6063891141815714246">"Afișare peste alte aplicații"</string>
<string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"<xliff:g id="NAME">%s</xliff:g> se afișează peste alte aplicații"</string>
<string name="alert_windows_notification_title" msgid="6331662751095228536">"<xliff:g id="NAME">%s</xliff:g> se afișează peste aplicații"</string>
<string name="alert_windows_notification_message" msgid="6538171456970725333">"Dacă nu doriți ca <xliff:g id="NAME">%s</xliff:g> să utilizeze această funcție, atingeți pentru a deschide setările și dezactivați-o."</string>
- <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Dezactivați"</string>
+ <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Dezactivează"</string>
<string name="ext_media_checking_notification_title" msgid="8299199995416510094">"Se verifică <xliff:g id="NAME">%s</xliff:g>…"</string>
<string name="ext_media_checking_notification_message" msgid="2231566971425375542">"Se examinează conținutul curent"</string>
<string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"Se analizează spațiul de stocare media"</string>
<string name="ext_media_new_notification_title" msgid="3517407571407687677">"<xliff:g id="NAME">%s</xliff:g> nou"</string>
<string name="ext_media_new_notification_title" product="automotive" msgid="9085349544984742727">"<xliff:g id="NAME">%s</xliff:g> nu funcționează"</string>
- <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Atingeți pentru a configura"</string>
- <string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"Selectați pentru a configura"</string>
+ <string name="ext_media_new_notification_message" msgid="6095403121990786986">"Atinge pentru a configura"</string>
+ <string name="ext_media_new_notification_message" product="tv" msgid="216863352100263668">"Selectează pentru a configura"</string>
<string name="ext_media_new_notification_message" product="automotive" msgid="5140127881613227162">"Poate fi nevoie să reformatați dispozitivul. Atingeți pentru a-l scoate."</string>
<string name="ext_media_ready_notification_message" msgid="7509496364380197369">"Pentru stocarea de fotografii, videoclipuri, muzică și altele"</string>
<string name="ext_media_ready_notification_message" product="tv" msgid="8847134811163165935">"Răsfoiți fișierele media"</string>
<string name="ext_media_unmountable_notification_title" msgid="4895444667278979910">"Problemă cu <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_unmountable_notification_title" product="automotive" msgid="3142723758949023280">"<xliff:g id="NAME">%s</xliff:g> nu funcționează"</string>
- <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"Atingeți pentru a remedia"</string>
- <string name="ext_media_unmountable_notification_message" product="tv" msgid="3003611129979934633">"<xliff:g id="NAME">%s</xliff:g> este corupt. Selectați pentru a remedia."</string>
+ <string name="ext_media_unmountable_notification_message" msgid="3256290114063126205">"Atinge pentru a remedia"</string>
+ <string name="ext_media_unmountable_notification_message" product="tv" msgid="3003611129979934633">"<xliff:g id="NAME">%s</xliff:g> este corupt. Selectează pentru a remedia."</string>
<string name="ext_media_unmountable_notification_message" product="automotive" msgid="2274596120715020680">"Poate fi nevoie să reformatați dispozitivul. Atingeți pentru a-l scoate."</string>
<string name="ext_media_unsupported_notification_title" msgid="3487534182861251401">"S-a detectat <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_unsupported_notification_title" product="automotive" msgid="6004193172658722381">"<xliff:g id="NAME">%s</xliff:g> nu funcționează"</string>
<string name="ext_media_unsupported_notification_message" msgid="8463636521459807981">"Atinge pentru a configura"</string>
- <string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"Selectați pentru a configura <xliff:g id="NAME">%s</xliff:g> într-un format acceptat."</string>
+ <string name="ext_media_unsupported_notification_message" product="tv" msgid="1595482802187036532">"Selectează pentru a configura <xliff:g id="NAME">%s</xliff:g> într-un format acceptat."</string>
<string name="ext_media_unsupported_notification_message" product="automotive" msgid="3412494732736336330">"Poate fi nevoie să reformatați dispozitivul"</string>
<string name="ext_media_badremoval_notification_title" msgid="4114625551266196872">"<xliff:g id="NAME">%s</xliff:g> scos pe neașteptate"</string>
- <string name="ext_media_badremoval_notification_message" msgid="1986514704499809244">"Deconectați din setări dispozitivele media înainte de a le îndepărta, pentru a evita pierderea conținutului"</string>
+ <string name="ext_media_badremoval_notification_message" msgid="1986514704499809244">"Deconectează din setări dispozitivele media înainte de a le îndepărta, pentru a evita pierderea conținutului"</string>
<string name="ext_media_nomedia_notification_title" msgid="742671636376975890">"S-a eliminat <xliff:g id="NAME">%s</xliff:g>"</string>
- <string name="ext_media_nomedia_notification_message" msgid="2832724384636625852">"Funcționarea ar putea fi necorespunzătoare. Introduceți un dispozitiv de stocare nou."</string>
+ <string name="ext_media_nomedia_notification_message" msgid="2832724384636625852">"Funcționarea ar putea fi necorespunzătoare. Introdu un dispozitiv de stocare nou."</string>
<string name="ext_media_unmounting_notification_title" msgid="4147986383917892162">"Se deconectează <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_unmounting_notification_message" msgid="5717036261538754203">"Nu scoateți"</string>
- <string name="ext_media_init_action" msgid="2312974060585056709">"Configurați"</string>
- <string name="ext_media_unmount_action" msgid="966992232088442745">"Scoateți"</string>
+ <string name="ext_media_init_action" msgid="2312974060585056709">"Configurează"</string>
+ <string name="ext_media_unmount_action" msgid="966992232088442745">"Scoate"</string>
<string name="ext_media_browse_action" msgid="344865351947079139">"Explorați"</string>
- <string name="ext_media_seamless_action" msgid="8837030226009268080">"Schimbați ieșirea"</string>
+ <string name="ext_media_seamless_action" msgid="8837030226009268080">"Schimbă ieșirea"</string>
<string name="ext_media_missing_title" msgid="3209472091220515046">"<xliff:g id="NAME">%s</xliff:g> lipsește"</string>
- <string name="ext_media_missing_message" msgid="4408988706227922909">"Reintroduceți dispozitivul"</string>
+ <string name="ext_media_missing_message" msgid="4408988706227922909">"Reintrodu dispozitivul"</string>
<string name="ext_media_move_specific_title" msgid="8492118544775964250">"Se mută <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_title" msgid="2682741525619033637">"Se mută datele"</string>
<string name="ext_media_move_success_title" msgid="4901763082647316767">"Transfer de conținut încheiat"</string>
<string name="ext_media_move_success_message" msgid="9159542002276982979">"Conținut mutat pe <xliff:g id="NAME">%s</xliff:g>"</string>
<string name="ext_media_move_failure_title" msgid="3184577479181333665">"Nu s-a putut muta conținutul"</string>
- <string name="ext_media_move_failure_message" msgid="4197306718121869335">"Încercați să mutați din nou conținutul"</string>
+ <string name="ext_media_move_failure_message" msgid="4197306718121869335">"Încearcă să muți din nou conținutul"</string>
<string name="ext_media_status_removed" msgid="241223931135751691">"Eliminat"</string>
<string name="ext_media_status_unmounted" msgid="8145812017295835941">"Scos"</string>
<string name="ext_media_status_checking" msgid="159013362442090347">"Se verifică..."</string>
@@ -1458,11 +1458,11 @@
<string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Permite unei aplicații să solicite permisiunea de a ignora optimizările bateriei pentru aplicația respectivă."</string>
<string name="permlab_queryAllPackages" msgid="2928450604653281650">"să interogheze toate pachetele"</string>
<string name="permdesc_queryAllPackages" msgid="5339069855520996010">"Permite unei aplicații să vadă toate pachetele instalate."</string>
- <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Apăsați de două ori pentru a controla mărirea/micșorarea"</string>
+ <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Apasă de două ori pentru a controla mărirea/micșorarea"</string>
<string name="gadget_host_error_inflating" msgid="2449961590495198720">"Nu s-a putut adăuga widgetul."</string>
- <string name="ime_action_go" msgid="5536744546326495436">"Accesați"</string>
- <string name="ime_action_search" msgid="4501435960587287668">"Căutați"</string>
- <string name="ime_action_send" msgid="8456843745664334138">"Trimiteți"</string>
+ <string name="ime_action_go" msgid="5536744546326495436">"Accesează"</string>
+ <string name="ime_action_search" msgid="4501435960587287668">"Caută"</string>
+ <string name="ime_action_send" msgid="8456843745664334138">"Trimite"</string>
<string name="ime_action_next" msgid="4169702997635728543">"Înainte"</string>
<string name="ime_action_done" msgid="6299921014822891569">"Terminat"</string>
<string name="ime_action_previous" msgid="6548799326860401611">"Înapoi"</string>
@@ -1470,48 +1470,48 @@
<string name="dial_number_using" msgid="6060769078933953531">"Formați numărul\nutilizând <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="create_contact_using" msgid="6200708808003692594">"Creați contactul\nutilizând <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="grant_credentials_permission_message_header" msgid="5365733888842570481">"Următoarele aplicații solicită permisiunea de a accesa contul dvs. acum și în viitor."</string>
- <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Permiteți această solicitare?"</string>
+ <string name="grant_credentials_permission_message_footer" msgid="1886710210516246461">"Permiți această solicitare?"</string>
<string name="grant_permissions_header_text" msgid="3420736827804657201">"Solicitare de acces"</string>
- <string name="allow" msgid="6195617008611933762">"Permiteți"</string>
- <string name="deny" msgid="6632259981847676572">"Refuzați"</string>
+ <string name="allow" msgid="6195617008611933762">"Permite"</string>
+ <string name="deny" msgid="6632259981847676572">"Refuz"</string>
<string name="permission_request_notification_title" msgid="1810025922441048273">"Permisiune solicitată"</string>
<string name="permission_request_notification_with_subtitle" msgid="3743417870360129298">"Permisiune solicitată\npentru contul <xliff:g id="ACCOUNT">%s</xliff:g>."</string>
<string name="permission_request_notification_for_app_with_subtitle" msgid="1298704005732851350">"Permisiune solicitată de <xliff:g id="APP">%1$s</xliff:g>\npentru contul <xliff:g id="ACCOUNT">%2$s</xliff:g>."</string>
- <string name="forward_intent_to_owner" msgid="4620359037192871015">"Utilizați această aplicație în afara profilului de serviciu"</string>
- <string name="forward_intent_to_work" msgid="3620262405636021151">"Utilizați această aplicație în profilul de serviciu"</string>
+ <string name="forward_intent_to_owner" msgid="4620359037192871015">"Folosești această aplicație în afara profilului de serviciu"</string>
+ <string name="forward_intent_to_work" msgid="3620262405636021151">"Folosești această aplicație în profilul de serviciu"</string>
<string name="input_method_binding_label" msgid="1166731601721983656">"Metodă de intrare"</string>
<string name="sync_binding_label" msgid="469249309424662147">"Sincronizare"</string>
<string name="accessibility_binding_label" msgid="1974602776545801715">"Accesibilitate"</string>
<string name="wallpaper_binding_label" msgid="1197440498000786738">"Imagine de fundal"</string>
- <string name="chooser_wallpaper" msgid="3082405680079923708">"Schimbați imaginea de fundal"</string>
+ <string name="chooser_wallpaper" msgid="3082405680079923708">"Schimbă imaginea de fundal"</string>
<string name="notification_listener_binding_label" msgid="2702165274471499713">"Serviciu de citire a notificărilor"</string>
<string name="vr_listener_binding_label" msgid="8013112996671206429">"Instrument de ascultare pentru RV"</string>
<string name="condition_provider_service_binding_label" msgid="8490641013951857673">"Furnizor de condiții"</string>
<string name="notification_ranker_binding_label" msgid="432708245635563763">"Serviciul de clasificare a notificărilor"</string>
<string name="vpn_title" msgid="5906991595291514182">"VPN activat"</string>
<string name="vpn_title_long" msgid="6834144390504619998">"VPN este activată de <xliff:g id="APP">%s</xliff:g>"</string>
- <string name="vpn_text" msgid="2275388920267251078">"Apăsați pentru a gestiona rețeaua."</string>
- <string name="vpn_text_long" msgid="278540576806169831">"Conectat la <xliff:g id="SESSION">%s</xliff:g>. Apăsați pentru a gestiona rețeaua."</string>
+ <string name="vpn_text" msgid="2275388920267251078">"Apasă pentru a gestiona rețeaua."</string>
+ <string name="vpn_text_long" msgid="278540576806169831">"Conectat la <xliff:g id="SESSION">%s</xliff:g>. Apasă pentru a gestiona rețeaua."</string>
<string name="vpn_lockdown_connecting" msgid="6096725311950342607">"Se efectuează conectarea la rețeaua VPN activată permanent…"</string>
<string name="vpn_lockdown_connected" msgid="2853127976590658469">"Conectat(ă) la rețeaua VPN activată permanent"</string>
<string name="vpn_lockdown_disconnected" msgid="5573611651300764955">"Deconectat de la rețeaua VPN activată permanent"</string>
<string name="vpn_lockdown_error" msgid="4453048646854247947">"Nu s-a putut conecta la rețeaua VPN activată permanent"</string>
- <string name="vpn_lockdown_config" msgid="8331697329868252169">"Modificați setările de rețea sau VPN"</string>
- <string name="upload_file" msgid="8651942222301634271">"Alegeți un fișier"</string>
+ <string name="vpn_lockdown_config" msgid="8331697329868252169">"Modifică setările de rețea sau VPN"</string>
+ <string name="upload_file" msgid="8651942222301634271">"Alege un fișier"</string>
<string name="no_file_chosen" msgid="4146295695162318057">"Nu au fost găsite fișiere"</string>
- <string name="reset" msgid="3865826612628171429">"Resetați"</string>
- <string name="submit" msgid="862795280643405865">"Trimiteți"</string>
+ <string name="reset" msgid="3865826612628171429">"Resetează"</string>
+ <string name="submit" msgid="862795280643405865">"Trimite"</string>
<string name="car_mode_disable_notification_title" msgid="8450693275833142896">"Aplicația pentru condus rulează"</string>
- <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Atingeți ca să ieșiți din aplicația pentru condus."</string>
+ <string name="car_mode_disable_notification_message" msgid="8954550232288567515">"Atinge ca să ieși din aplicația pentru condus."</string>
<string name="back_button_label" msgid="4078224038025043387">"Înapoi"</string>
<string name="next_button_label" msgid="6040209156399907780">"Înainte"</string>
- <string name="skip_button_label" msgid="3566599811326688389">"Omiteți"</string>
+ <string name="skip_button_label" msgid="3566599811326688389">"Omite"</string>
<string name="no_matches" msgid="6472699895759164599">"Nicio potrivire"</string>
<string name="find_on_page" msgid="5400537367077438198">"Găsiți pe pagină"</string>
<string name="matches_found" msgid="2296462299979507689">"{count,plural, =1{# potrivire}few{# din {total}}other{# din {total}}}"</string>
<string name="action_mode_done" msgid="2536182504764803222">"Terminat"</string>
<string name="progress_erasing" msgid="6891435992721028004">"Se șterge spațiul de stocare distribuit..."</string>
- <string name="share" msgid="4157615043345227321">"Distribuiți"</string>
+ <string name="share" msgid="4157615043345227321">"Distribuie"</string>
<string name="find" msgid="5015737188624767706">"Găsiți"</string>
<string name="websearch" msgid="5624340204512793290">"Căutare pe web"</string>
<string name="find_next" msgid="5341217051549648153">"Următorul rezultat"</string>
@@ -1523,45 +1523,45 @@
<string name="gpsVerifNo" msgid="1671201856091564741">"Nu"</string>
<string name="sync_too_many_deletes" msgid="6999440774578705300">"Limita pentru ștergere a fost depășită"</string>
<string name="sync_too_many_deletes_desc" msgid="7409327940303504440">"Există <xliff:g id="NUMBER_OF_DELETED_ITEMS">%1$d</xliff:g> elemente șterse pentru <xliff:g id="TYPE_OF_SYNC">%2$s</xliff:g>, contul <xliff:g id="ACCOUNT_NAME">%3$s</xliff:g>. Ce doriți să faceți?"</string>
- <string name="sync_really_delete" msgid="5657871730315579051">"Ștergeți elementele"</string>
- <string name="sync_undo_deletes" msgid="5786033331266418896">"Anulați aceste ștergeri"</string>
+ <string name="sync_really_delete" msgid="5657871730315579051">"Șterge elementele"</string>
+ <string name="sync_undo_deletes" msgid="5786033331266418896">"Anulează aceste ștergeri"</string>
<string name="sync_do_nothing" msgid="4528734662446469646">"Nu trebuie să luați nicio măsură deocamdată"</string>
- <string name="choose_account_label" msgid="5557833752759831548">"Alegeți un cont"</string>
- <string name="add_account_label" msgid="4067610644298737417">"Adăugați un cont"</string>
- <string name="add_account_button_label" msgid="322390749416414097">"Adăugați un cont"</string>
+ <string name="choose_account_label" msgid="5557833752759831548">"Alege un cont"</string>
+ <string name="add_account_label" msgid="4067610644298737417">"Adaugă un cont"</string>
+ <string name="add_account_button_label" msgid="322390749416414097">"Adaugă un cont"</string>
<string name="number_picker_increment_button" msgid="7621013714795186298">"Creșteți"</string>
<string name="number_picker_decrement_button" msgid="5116948444762708204">"Reduceți"</string>
<string name="number_picker_increment_scroll_mode" msgid="8403893549806805985">"<xliff:g id="VALUE">%s</xliff:g> atingeți lung."</string>
- <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Glisați în sus pentru a crește și în jos pentru a reduce."</string>
+ <string name="number_picker_increment_scroll_action" msgid="8310191318914268271">"Glisează în sus pentru a crește și în jos pentru a reduce."</string>
<string name="time_picker_increment_minute_button" msgid="7195870222945784300">"Creșteți valoarea pentru minute"</string>
- <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Reduceți valoarea pentru minute"</string>
+ <string name="time_picker_decrement_minute_button" msgid="230925389943411490">"Redu valoarea pentru minute"</string>
<string name="time_picker_increment_hour_button" msgid="3063572723197178242">"Creșteți valoarea pentru oră"</string>
- <string name="time_picker_decrement_hour_button" msgid="584101766855054412">"Reduceți valoarea pentru oră"</string>
- <string name="time_picker_increment_set_pm_button" msgid="5889149366900376419">"Setați valoarea PM"</string>
- <string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"Setați valoarea AM"</string>
- <string name="date_picker_increment_month_button" msgid="3447263316096060309">"Creșteți valoarea pentru lună"</string>
+ <string name="time_picker_decrement_hour_button" msgid="584101766855054412">"Redu valoarea pentru oră"</string>
+ <string name="time_picker_increment_set_pm_button" msgid="5889149366900376419">"Setează valoarea PM"</string>
+ <string name="time_picker_decrement_set_am_button" msgid="1422608001541064087">"Setează valoarea AM"</string>
+ <string name="date_picker_increment_month_button" msgid="3447263316096060309">"Mărește valoarea pentru lună"</string>
<string name="date_picker_decrement_month_button" msgid="6531888937036883014">"Reduceți valoarea pentru lună"</string>
- <string name="date_picker_increment_day_button" msgid="4349336637188534259">"Creșteți valoarea pentru zi"</string>
+ <string name="date_picker_increment_day_button" msgid="4349336637188534259">"Mărește valoarea pentru zi"</string>
<string name="date_picker_decrement_day_button" msgid="6840253837656637248">"Reduceți valoarea pentru zi"</string>
<string name="date_picker_increment_year_button" msgid="7608128783435372594">"Creșteți valoarea pentru an"</string>
<string name="date_picker_decrement_year_button" msgid="4102586521754172684">"Reduceți valoarea pentru an"</string>
<string name="date_picker_prev_month_button" msgid="3418694374017868369">"Luna trecută"</string>
<string name="date_picker_next_month_button" msgid="4858207337779144840">"Luna viitoare"</string>
<string name="keyboardview_keycode_alt" msgid="8997420058584292385">"Alt"</string>
- <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"Anulați"</string>
- <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Ștergeți"</string>
+ <string name="keyboardview_keycode_cancel" msgid="2134624484115716975">"Anulează"</string>
+ <string name="keyboardview_keycode_delete" msgid="2661117313730098650">"Șterge"</string>
<string name="keyboardview_keycode_done" msgid="2524518019001653851">"Terminat"</string>
<string name="keyboardview_keycode_mode_change" msgid="2743735349997999020">"Schimbarea modului"</string>
<string name="keyboardview_keycode_shift" msgid="3026509237043975573">"Shift"</string>
<string name="keyboardview_keycode_enter" msgid="168054869339091055">"Enter"</string>
- <string name="activitychooserview_choose_application" msgid="3500574466367891463">"Alegeți o aplicație"</string>
+ <string name="activitychooserview_choose_application" msgid="3500574466367891463">"Alege o aplicație"</string>
<string name="activitychooserview_choose_application_error" msgid="6937782107559241734">"Nu s-a putut lansa <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
- <string name="shareactionprovider_share_with" msgid="2753089758467748982">"Permiteți accesul pentru"</string>
- <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"Permiteți accesul pentru <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
+ <string name="shareactionprovider_share_with" msgid="2753089758467748982">"Permite accesul pentru"</string>
+ <string name="shareactionprovider_share_with_application" msgid="4902832247173666973">"Permite accesul pentru <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
<string name="content_description_sliding_handle" msgid="982510275422590757">"Mâner glisant. Atingeți și țineți apăsat."</string>
- <string name="description_target_unlock_tablet" msgid="7431571180065859551">"Glisați pentru a debloca."</string>
- <string name="action_bar_home_description" msgid="1501655419158631974">"Navigați la ecranul de pornire"</string>
- <string name="action_bar_up_description" msgid="6611579697195026932">"Navigați în sus"</string>
+ <string name="description_target_unlock_tablet" msgid="7431571180065859551">"Glisează pentru a debloca."</string>
+ <string name="action_bar_home_description" msgid="1501655419158631974">"Navighează la ecranul de pornire"</string>
+ <string name="action_bar_up_description" msgid="6611579697195026932">"Navighează în sus"</string>
<string name="action_menu_overflow_description" msgid="4579536843510088170">"Mai multe opțiuni"</string>
<string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s, %2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s, %2$s, %3$s"</string>
@@ -1571,19 +1571,19 @@
<string name="storage_usb_drive" msgid="448030813201444573">"Unitate USB"</string>
<string name="storage_usb_drive_label" msgid="6631740655876540521">"Unitate USB <xliff:g id="MANUFACTURER">%s</xliff:g>"</string>
<string name="storage_usb" msgid="2391213347883616886">"Dsipozitiv de stocare USB"</string>
- <string name="extract_edit_menu_button" msgid="63954536535863040">"Editați"</string>
+ <string name="extract_edit_menu_button" msgid="63954536535863040">"Editează"</string>
<string name="data_usage_warning_title" msgid="9034893717078325845">"Avertisment pentru date"</string>
- <string name="data_usage_warning_body" msgid="1669325367188029454">"Ați folosit <xliff:g id="APP">%s</xliff:g> din date"</string>
+ <string name="data_usage_warning_body" msgid="1669325367188029454">"Ai folosit <xliff:g id="APP">%s</xliff:g> din date"</string>
<string name="data_usage_mobile_limit_title" msgid="3911447354393775241">"S-a atins limita de date mobile"</string>
- <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Ați atins limita de date Wi-Fi"</string>
+ <string name="data_usage_wifi_limit_title" msgid="2069698056520812232">"Ai atins limita de date Wi-Fi"</string>
<string name="data_usage_limit_body" msgid="3567699582000085710">"Datele au fost întrerupte pentru restul ciclului"</string>
<string name="data_usage_mobile_limit_snoozed_title" msgid="101888478915677895">"Peste limita de date mobile"</string>
<string name="data_usage_wifi_limit_snoozed_title" msgid="1622359254521960508">"Peste limita de date Wi-Fi"</string>
- <string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Ați depășit limita stabilită cu <xliff:g id="SIZE">%s</xliff:g>"</string>
+ <string name="data_usage_limit_snoozed_body" msgid="545146591766765678">"Ai depășit limita stabilită cu <xliff:g id="SIZE">%s</xliff:g>"</string>
<string name="data_usage_restricted_title" msgid="126711424380051268">"Datele de fundal restricționate"</string>
- <string name="data_usage_restricted_body" msgid="5338694433686077733">"Atingeți ca să eliminați restricția."</string>
+ <string name="data_usage_restricted_body" msgid="5338694433686077733">"Atinge ca să elimini restricția."</string>
<string name="data_usage_rapid_title" msgid="2950192123248740375">"Utilizare mare de date mobile"</string>
- <string name="data_usage_rapid_body" msgid="3886676853263693432">"Aplicațiile dvs. au utilizat mai multe date decât de obicei"</string>
+ <string name="data_usage_rapid_body" msgid="3886676853263693432">"Aplicațiile au folosit mai multe date decât de obicei"</string>
<string name="data_usage_rapid_app_body" msgid="5425779218506513861">"<xliff:g id="APP">%s</xliff:g> a utilizat mai multe date decât de obicei"</string>
<string name="ssl_certificate" msgid="5690020361307261997">"Certificat de securitate"</string>
<string name="ssl_certificate_is_valid" msgid="7293675884598527081">"Certificatul este valid."</string>
@@ -1599,12 +1599,12 @@
<string name="fingerprints" msgid="148690767172613723">"Amprente:"</string>
<string name="sha256_fingerprint" msgid="7103976380961964600">"Amprentă SHA-256:"</string>
<string name="sha1_fingerprint" msgid="2339915142825390774">"Amprentă SHA-1:"</string>
- <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Afișați-le pe toate"</string>
- <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Alegeți activitatea"</string>
- <string name="share_action_provider_share_with" msgid="1904096863622941880">"Distribuiți pentru"</string>
+ <string name="activity_chooser_view_see_all" msgid="3917045206812726099">"Afișează-le pe toate"</string>
+ <string name="activity_chooser_view_dialog_title_default" msgid="8880731437191978314">"Alege activitatea"</string>
+ <string name="share_action_provider_share_with" msgid="1904096863622941880">"Distribuie pentru"</string>
<string name="sending" msgid="206925243621664438">"Se trimite..."</string>
- <string name="launchBrowserDefault" msgid="6328349989932924119">"Lansați browserul?"</string>
- <string name="SetupCallDefault" msgid="5581740063237175247">"Acceptați apelul?"</string>
+ <string name="launchBrowserDefault" msgid="6328349989932924119">"Lansezi browserul?"</string>
+ <string name="SetupCallDefault" msgid="5581740063237175247">"Accepți apelul?"</string>
<string name="activity_resolver_use_always" msgid="5575222334666843269">"Întotdeauna"</string>
<string name="activity_resolver_use_once" msgid="948462794469672658">"Numai o dată"</string>
<string name="activity_resolver_work_profiles_support" msgid="4071345609235361269">"%1$s nu acceptă profilul de serviciu"</string>
@@ -1612,18 +1612,18 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Difuz. dispozit. andocare"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Dispozitiv extern"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Căști"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistem"</string>
<string name="bluetooth_a2dp_audio_route_name" msgid="4214648773120426288">"Audio Bluetooth"</string>
<string name="wireless_display_route_description" msgid="8297563323032966831">"Ecran wireless"</string>
- <string name="media_route_button_content_description" msgid="2299223698196869956">"Trimiteți"</string>
- <string name="media_route_chooser_title" msgid="6646594924991269208">"Conectați-vă la dispozitiv"</string>
- <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Proiectați ecranul pe dispozitiv"</string>
+ <string name="media_route_button_content_description" msgid="2299223698196869956">"Trimite"</string>
+ <string name="media_route_chooser_title" msgid="6646594924991269208">"Conectează-te la dispozitiv"</string>
+ <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"Proiectează ecranul pe dispozitiv"</string>
<string name="media_route_chooser_searching" msgid="6119673534251329535">"Se caută dispozitive..."</string>
<string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"Setări"</string>
- <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Deconectați-vă"</string>
+ <string name="media_route_controller_disconnect" msgid="7362617572732576959">"Deconectează-te"</string>
<string name="media_route_status_scanning" msgid="8045156315309594482">"Se scanează..."</string>
<string name="media_route_status_connecting" msgid="5845597961412010540">"Se conectează..."</string>
<string name="media_route_status_available" msgid="1477537663492007608">"Disponibilă"</string>
@@ -1638,16 +1638,16 @@
<string name="kg_wrong_pattern" msgid="1342812634464179931">"Model greșit"</string>
<string name="kg_wrong_password" msgid="2384677900494439426">"Parolă greșită"</string>
<string name="kg_wrong_pin" msgid="3680925703673166482">"Cod PIN greșit"</string>
- <string name="kg_pattern_instructions" msgid="8366024510502517748">"Desenați modelul"</string>
- <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Introduceți codul PIN al cardului SIM"</string>
- <string name="kg_pin_instructions" msgid="7355933174673539021">"Introduceți codul PIN"</string>
- <string name="kg_password_instructions" msgid="7179782578809398050">"Introduceți parola"</string>
+ <string name="kg_pattern_instructions" msgid="8366024510502517748">"Desenează modelul"</string>
+ <string name="kg_sim_pin_instructions" msgid="6479401489471690359">"Introdu codul PIN al cardului SIM"</string>
+ <string name="kg_pin_instructions" msgid="7355933174673539021">"Introdu codul PIN"</string>
+ <string name="kg_password_instructions" msgid="7179782578809398050">"Introdu parola"</string>
<string name="kg_puk_enter_puk_hint" msgid="6696187482616360994">"Cardul SIM este acum dezactivat. Introduceți codul PUK pentru a continua. Contactați operatorul pentru mai multe detalii."</string>
- <string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Introduceți codul PIN dorit"</string>
- <string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Confirmați codul PIN dorit"</string>
+ <string name="kg_puk_enter_pin_hint" msgid="8190982314659429770">"Introdu codul PIN dorit"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="6372557107414074580">"Confirmă codul PIN dorit"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="8871937892678885545">"Se deblochează cardul SIM..."</string>
<string name="kg_password_wrong_pin_code" msgid="9013856346870572451">"Cod PIN incorect."</string>
- <string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Introduceți un cod PIN format din 4 până la 8 cifre."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="4821601451222564077">"Introdu un cod PIN format din 4 până la 8 cifre."</string>
<string name="kg_invalid_sim_puk_hint" msgid="2539364558870734339">"Codul PUK trebuie să conțină 8 numere."</string>
<string name="kg_invalid_puk" msgid="4809502818518963344">"Reintroduceți codul PUK corect. Încercările repetate vor dezactiva definitiv cardul SIM."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="4705368340409816254">"Codurile PIN nu coincid"</string>
@@ -1660,28 +1660,28 @@
<string name="kg_login_account_recovery_hint" msgid="4892466171043541248">"Ați uitat numele de utilizator sau parola?\nAccesați "<b>"google.com/accounts/recovery"</b>"."</string>
<string name="kg_login_checking_password" msgid="4676010303243317253">"Se verifică contul…"</string>
<string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="23741434207544038">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="3328686432962224215">"Ai introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
<string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="7357404233979139075">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, aceasta va fi resetată la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="3479940221343361587">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, aceasta va reveni la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="tv" msgid="9064457748587850217">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va reveni la setările din fabrică, iar toate datele de utilizator se vor pierde."</string>
<string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="5955398963754432548">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acesta va fi resetat la setările prestabilite din fabrică, iar toate datele de utilizator se vor pierde."</string>
- <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va fi acum resetată la setările prestabilite din fabrică."</string>
- <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. Acesta va reveni la setările din fabrică."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tablet" msgid="2299099385175083308">"Ați făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Tableta va reveni acum la setările din fabrică."</string>
+ <string name="kg_failed_attempts_now_wiping" product="tv" msgid="5045460916106267585">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a dispozitivului Android TV. Acesta va reveni la setările din fabrică."</string>
<string name="kg_failed_attempts_now_wiping" product="default" msgid="5043730590446071189">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Telefonul va fi acum resetat la setările prestabilite din fabrică."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="7086799295109717623">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
<string name="kg_failed_attempts_almost_at_login" product="tv" msgid="4670840383567106114">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați dispozitivul Android TV cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
- <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
+ <string name="kg_failed_attempts_almost_at_login" product="default" msgid="5270861875006378092">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, ți se va solicita să deblochezi telefonul cu ajutorul unui cont de e-mail.\n\n Încearcă din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
<string name="kg_text_message_separator" product="default" msgid="4503708889934976866">" — "</string>
- <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Eliminați"</string>
+ <string name="kg_reordering_delete_drop_target_text" msgid="2034358143731750914">"Elimină"</string>
<string name="safe_media_volume_warning" product="default" msgid="3751676824423049994">"Ridicați volumul mai sus de nivelul recomandat?\n\nAscultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul."</string>
<string name="accessibility_shortcut_warning_dialog_title" msgid="4017995837692622933">"Utilizați comanda rapidă pentru accesibilitate?"</string>
<string name="accessibility_shortcut_toogle_warning" msgid="4161716521310929544">"Atunci când comanda rapidă este activată, dacă apăsați ambele butoane de volum timp de trei secunde, veți lansa o funcție de accesibilitate."</string>
- <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Activați comanda rapidă pentru funcțiile de accesibilitate?"</string>
+ <string name="accessibility_shortcut_multiple_service_warning_title" msgid="3135860819356676426">"Activezi comanda rapidă pentru funcțiile de accesibilitate?"</string>
<string name="accessibility_shortcut_multiple_service_warning" msgid="3740723309483706911">"Dacă apăsați ambele taste de volum câteva secunde, activați funcțiile de accesibilitate. Acest lucru poate schimba funcționarea dispozitivului.\n\nFuncțiile actuale:\n<xliff:g id="SERVICE">%1$s</xliff:g>\nPuteți schimba funcțiile selectate din Setări &gt; Accesibilitate."</string>
<string name="accessibility_shortcut_multiple_service_list" msgid="2128323171922023762">" • <xliff:g id="SERVICE">%1$s</xliff:g>\n"</string>
- <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Activați comanda rapidă <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="accessibility_shortcut_single_service_warning_title" msgid="1909518473488345266">"Activezi comanda rapidă <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="accessibility_shortcut_single_service_warning" msgid="6363127705112844257">"Dacă apăsați ambele taste de volum câteva secunde, activați funcția de accesibilitate <xliff:g id="SERVICE">%1$s</xliff:g>. Acest lucru poate schimba funcționarea dispozitivului.\n\nPuteți alege altă funcție pentru această comandă în Setări &gt; Accesibilitate."</string>
- <string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activați"</string>
+ <string name="accessibility_shortcut_on" msgid="5463618449556111344">"Activează"</string>
<string name="accessibility_shortcut_off" msgid="3651336255403648739">"Nu activați"</string>
<string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"ACTIVAT"</string>
<string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"DEZACTIVAT"</string>
@@ -1691,23 +1691,23 @@
<string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Poate citi tot conținutul de pe ecran și poate afișa conținut peste alte aplicații."</string>
<string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Vă vede interacțiunile și le realizează"</string>
<string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Poate urmări interacțiunile dvs. cu o aplicație sau cu un senzor hardware și poate interacționa cu aplicații în numele dvs."</string>
- <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permiteți"</string>
- <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Refuzați"</string>
+ <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Permite"</string>
+ <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"Refuz"</string>
<string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"Atingeți o funcție ca să începeți să o folosiți:"</string>
<string name="accessibility_edit_shortcut_menu_button_title" msgid="239446795930436325">"Alegeți funcțiile pe care să le folosiți cu butonul de accesibilitate"</string>
- <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Alegeți funcțiile pentru comanda rapidă a butonului de volum"</string>
+ <string name="accessibility_edit_shortcut_menu_volume_title" msgid="1077294237378645981">"Alege funcțiile pentru comanda rapidă a butonului de volum"</string>
<string name="accessibility_uncheck_legacy_item_warning" msgid="8047830891064817447">"<xliff:g id="SERVICE_NAME">%s</xliff:g> a fost dezactivat"</string>
- <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editați comenzile rapide"</string>
+ <string name="edit_accessibility_shortcut_menu_button" msgid="8885752738733772935">"Editează comenzile rapide"</string>
<string name="done_accessibility_shortcut_menu_button" msgid="3668407723770815708">"Gata"</string>
- <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Dezactivați comanda rapidă"</string>
- <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Utilizați comanda rapidă"</string>
+ <string name="disable_accessibility_shortcut" msgid="5806091378745232383">"Dezactivează comanda rapidă"</string>
+ <string name="leave_accessibility_shortcut_on" msgid="6543362062336990814">"Folosește comanda rapidă"</string>
<string name="color_inversion_feature_name" msgid="326050048927789012">"Inversarea culorilor"</string>
<string name="color_correction_feature_name" msgid="3655077237805422597">"Corecția culorii"</string>
<string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modul cu o mână"</string>
<string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminozitate redusă suplimentar"</string>
<string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
<string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
- <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Apăsați ambele butoane de volum timp de trei secunde pentru a folosi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
+ <string name="accessibility_shortcut_spoken_feedback" msgid="4228997042855695090">"Apasă ambele butoane de volum timp de trei secunde pentru a folosi <xliff:g id="SERVICE_NAME">%1$s</xliff:g>"</string>
<string name="accessibility_button_prompt_text" msgid="8343213623338605305">"Alegeți o funcție pe care să o folosiți când atingeți butonul de accesibilitate:"</string>
<string name="accessibility_gesture_prompt_text" msgid="8742535972130563952">"Alegeți o funcție pe care să o folosiți cu gestul de accesibilitate (glisați în sus cu două degete din partea de jos a ecranului):"</string>
<string name="accessibility_gesture_3finger_prompt_text" msgid="5211827854510660203">"Alegeți o funcție pe care să o folosiți cu gestul de accesibilitate (glisați în sus cu trei degete din partea de jos a ecranului):"</string>
@@ -1721,7 +1721,7 @@
<string name="owner_name" msgid="8713560351570795743">"Proprietar"</string>
<string name="guest_name" msgid="8502103277839834324">"Invitat"</string>
<string name="error_message_title" msgid="4082495589294631966">"Eroare"</string>
- <string name="error_message_change_not_allowed" msgid="843159705042381454">"Această modificare nu este permisă de administratorul dvs."</string>
+ <string name="error_message_change_not_allowed" msgid="843159705042381454">"Această modificare nu este permisă de administrator"</string>
<string name="app_not_found" msgid="3429506115332341800">"Nicio aplicație pentru gestionarea acestei acțiuni"</string>
<string name="revoke" msgid="5526857743819590458">"Revocați"</string>
<string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string>
@@ -1824,27 +1824,27 @@
<string name="reason_unknown" msgid="5599739807581133337">"necunoscut"</string>
<string name="reason_service_unavailable" msgid="5288405248063804713">"Serviciul de printare nu este activat"</string>
<string name="print_service_installed_title" msgid="6134880817336942482">"Serviciul <xliff:g id="NAME">%s</xliff:g> a fost instalat"</string>
- <string name="print_service_installed_message" msgid="7005672469916968131">"Atingeți pentru a activa"</string>
- <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Introduceți codul PIN de administrator"</string>
- <string name="restr_pin_enter_pin" msgid="373139384161304555">"Introduceți codul PIN"</string>
+ <string name="print_service_installed_message" msgid="7005672469916968131">"Atinge pentru a activa"</string>
+ <string name="restr_pin_enter_admin_pin" msgid="1199419462726962697">"Introdu codul PIN de administrator"</string>
+ <string name="restr_pin_enter_pin" msgid="373139384161304555">"Introdu codul PIN"</string>
<string name="restr_pin_incorrect" msgid="3861383632940852496">"Incorect"</string>
<string name="restr_pin_enter_old_pin" msgid="7537079094090650967">"Codul PIN actual"</string>
<string name="restr_pin_enter_new_pin" msgid="3267614461844565431">"Codul PIN nou"</string>
- <string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Confirmați noul cod PIN"</string>
+ <string name="restr_pin_confirm_pin" msgid="7143161971614944989">"Confirmă noul cod PIN"</string>
<string name="restr_pin_create_pin" msgid="917067613896366033">"Creați un cod PIN pentru modificarea restricțiilor"</string>
<string name="restr_pin_error_doesnt_match" msgid="7063392698489280556">"Codurile PIN nu se potrivesc. Încercați din nou."</string>
<string name="restr_pin_error_too_short" msgid="1547007808237941065">"Codul PIN este prea scurt. Trebuie să aibă cel puțin 4 cifre."</string>
- <string name="restr_pin_try_later" msgid="5897719962541636727">"Reîncercați mai târziu"</string>
+ <string name="restr_pin_try_later" msgid="5897719962541636727">"Reîncearcă mai târziu"</string>
<string name="immersive_cling_title" msgid="2307034298721541791">"Vizualizare pe ecran complet"</string>
<string name="immersive_cling_description" msgid="7092737175345204832">"Pentru a ieși, glisați de sus în jos."</string>
<string name="immersive_cling_positive" msgid="7047498036346489883">"Am înțeles"</string>
<string name="done_label" msgid="7283767013231718521">"Terminat"</string>
<string name="hour_picker_description" msgid="5153757582093524635">"Selector circular pentru ore"</string>
<string name="minute_picker_description" msgid="9029797023621927294">"Selector circular pentru minute"</string>
- <string name="select_hours" msgid="5982889657313147347">"Selectați orele"</string>
- <string name="select_minutes" msgid="9157401137441014032">"Selectați minutele"</string>
- <string name="select_day" msgid="2060371240117403147">"Selectați luna și ziua"</string>
- <string name="select_year" msgid="1868350712095595393">"Selectați anul"</string>
+ <string name="select_hours" msgid="5982889657313147347">"Selectează orele"</string>
+ <string name="select_minutes" msgid="9157401137441014032">"Selectează minutele"</string>
+ <string name="select_day" msgid="2060371240117403147">"Selectează luna și ziua"</string>
+ <string name="select_year" msgid="1868350712095595393">"Selectează anul"</string>
<string name="deleted_key" msgid="9130083334943364001">"<xliff:g id="KEY">%1$s</xliff:g> a fost șters"</string>
<string name="managed_profile_label_badge" msgid="6762559569999499495">"<xliff:g id="LABEL">%1$s</xliff:g> de serviciu"</string>
<string name="managed_profile_label_badge_2" msgid="5673187309555352550">"<xliff:g id="LABEL">%1$s</xliff:g> pentru serviciu (2)"</string>
@@ -1852,15 +1852,15 @@
<string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Solicită codul PIN înainte de a anula fixarea"</string>
<string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Solicită mai întâi modelul pentru deblocare"</string>
<string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Solicită parola înainte de a anula fixarea"</string>
- <string name="package_installed_device_owner" msgid="7035926868974878525">"Instalat de administratorul dvs."</string>
- <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizat de administratorul dvs."</string>
+ <string name="package_installed_device_owner" msgid="7035926868974878525">"Instalat de administrator"</string>
+ <string name="package_updated_device_owner" msgid="7560272363805506941">"Actualizat de administrator"</string>
<string name="package_deleted_device_owner" msgid="2292335928930293023">"Șters de administratorul dvs."</string>
<string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
<string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"Economisirea bateriei activează tema întunecată și restricționează sau dezactivează activitatea în fundal, unele efecte vizuale, alte funcții și câteva conexiuni la rețea."</string>
<string name="battery_saver_description" msgid="8518809702138617167">"Economisirea bateriei activează tema întunecată și restricționează sau dezactivează activitatea în fundal, unele efecte vizuale, alte funcții și câteva conexiuni la rețea."</string>
- <string name="data_saver_description" msgid="4995164271550590517">"Pentru a contribui la reducerea utilizării de date, Economizorul de date împiedică unele aplicații să trimită sau să primească date în fundal. O aplicație pe care o folosiți poate accesa datele, însă mai rar. Aceasta poate însemna, de exemplu, că imaginile se afișează numai după ce le atingeți."</string>
- <string name="data_saver_enable_title" msgid="7080620065745260137">"Activați Economizorul de date?"</string>
- <string name="data_saver_enable_button" msgid="4399405762586419726">"Activați"</string>
+ <string name="data_saver_description" msgid="4995164271550590517">"Pentru a contribui la reducerea utilizării de date, Economizorul de date împiedică unele aplicații să trimită sau să primească date în fundal. O aplicație pe care o folosești poate accesa datele, însă mai rar. Aceasta poate însemna, de exemplu, că imaginile se afișează numai după ce le atingi."</string>
+ <string name="data_saver_enable_title" msgid="7080620065745260137">"Activezi Economizorul de date?"</string>
+ <string name="data_saver_enable_button" msgid="4399405762586419726">"Activează"</string>
<string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{Timp de un minut (până la {formattedTime})}few{Timp de # minute (până la {formattedTime})}other{Timp de # de minute (până la {formattedTime})}}"</string>
<string name="zen_mode_duration_minutes_summary_short" msgid="1187553788355486950">"{count,plural, =1{Timp de un min. (până la {formattedTime})}few{Timp de # min. (până la {formattedTime})}other{Timp de # min. (până la {formattedTime})}}"</string>
<string name="zen_mode_duration_hours_summary" msgid="3866333100793277211">"{count,plural, =1{Timp de o oră (până la {formattedTime})}few{Timp de # ore (până la {formattedTime})}other{Timp de # de ore (până la {formattedTime})}}"</string>
@@ -1875,7 +1875,7 @@
<string name="zen_mode_forever" msgid="740585666364912448">"Până când dezactivați"</string>
<string name="zen_mode_forever_dnd" msgid="3423201955704180067">"Până când dezactivați „Nu deranja”"</string>
<string name="zen_mode_rule_name_combination" msgid="7174598364351313725">"<xliff:g id="FIRST">%1$s</xliff:g>/<xliff:g id="REST">%2$s</xliff:g>"</string>
- <string name="toolbar_collapse_description" msgid="8009920446193610996">"Restrângeți"</string>
+ <string name="toolbar_collapse_description" msgid="8009920446193610996">"Restrânge"</string>
<string name="zen_mode_feature_name" msgid="3785547207263754500">"Nu deranja"</string>
<string name="zen_mode_downtime_feature_name" msgid="5886005761431427128">"Inactivitate"</string>
<string name="zen_mode_default_weeknights_name" msgid="7902108149994062847">"Nopțile din zilele lucrătoare"</string>
@@ -1884,7 +1884,7 @@
<string name="zen_mode_default_every_night_name" msgid="1467765312174275823">"Somn"</string>
<string name="muted_by" msgid="91464083490094950">"<xliff:g id="THIRD_PARTY">%1$s</xliff:g> dezactivează anumite sunete"</string>
<string name="system_error_wipe_data" msgid="5910572292172208493">"A apărut o problemă internă pe dispozitiv, iar acesta poate fi instabil până la revenirea la setările din fabrică."</string>
- <string name="system_error_manufacturer" msgid="703545241070116315">"A apărut o problemă internă pe dispozitiv. Pentru detalii, contactați producătorul."</string>
+ <string name="system_error_manufacturer" msgid="703545241070116315">"A apărut o problemă internă pe dispozitiv. Pentru detalii, contactează producătorul."</string>
<string name="stk_cc_ussd_to_dial" msgid="3139884150741157610">"Solicitarea USSD a fost schimbată cu un apel obișnuit"</string>
<string name="stk_cc_ussd_to_ss" msgid="4826846653052609738">"Solicitarea USSD a fost schimbată cu o solicitare SS"</string>
<string name="stk_cc_ussd_to_ussd" msgid="8343001461299302472">"Schimbat cu o solicitare USSD nouă"</string>
@@ -1897,18 +1897,18 @@
<string name="notification_work_profile_content_description" msgid="5296477955677725799">"Profil de serviciu"</string>
<string name="notification_alerted_content_description" msgid="6139691253611265992">"Notificat"</string>
<string name="notification_verified_content_description" msgid="6401483602782359391">"Confirmat"</string>
- <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Extindeți"</string>
- <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Restrângeți"</string>
- <string name="expand_action_accessibility" msgid="1947657036871746627">"extindeți/restrângeți"</string>
+ <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"Extinde"</string>
+ <string name="expand_button_content_description_expanded" msgid="7484217944948667489">"Restrânge"</string>
+ <string name="expand_action_accessibility" msgid="1947657036871746627">"extinde/restrânge"</string>
<string name="usb_midi_peripheral_name" msgid="490523464968655741">"Port USB Android periferic"</string>
<string name="usb_midi_peripheral_manufacturer_name" msgid="7557148557088787741">"Android"</string>
<string name="usb_midi_peripheral_product_name" msgid="2836276258480904434">"Port USB periferic"</string>
<string name="floating_toolbar_open_overflow_description" msgid="2260297653578167367">"Mai multe opțiuni"</string>
- <string name="floating_toolbar_close_overflow_description" msgid="3949818077708138098">"Închideți meniul suplimentar"</string>
+ <string name="floating_toolbar_close_overflow_description" msgid="3949818077708138098">"Închide meniul suplimentar"</string>
<string name="maximize_button_text" msgid="4258922519914732645">"Maximizați"</string>
- <string name="close_button_text" msgid="10603510034455258">"Închideți"</string>
+ <string name="close_button_text" msgid="10603510034455258">"Închide"</string>
<string name="notification_messaging_title_template" msgid="772857526770251989">"<xliff:g id="CONVERSATION_TITLE">%1$s</xliff:g>: <xliff:g id="SENDER_NAME">%2$s</xliff:g>"</string>
- <string name="call_notification_answer_action" msgid="5999246836247132937">"Răspundeți"</string>
+ <string name="call_notification_answer_action" msgid="5999246836247132937">"Răspunde"</string>
<string name="call_notification_answer_video_action" msgid="2086030940195382249">"Video"</string>
<string name="call_notification_decline_action" msgid="3700345945214000726">"Respingeți"</string>
<string name="call_notification_hang_up_action" msgid="9130720590159188131">"Încheiați"</string>
@@ -1916,13 +1916,13 @@
<string name="call_notification_ongoing_text" msgid="3880832933933020875">"Apel în desfășurare"</string>
<string name="call_notification_screening_text" msgid="8396931408268940208">"Se filtrează un apel primit"</string>
<string name="default_notification_channel_label" msgid="3697928973567217330">"Neclasificate"</string>
- <string name="importance_from_user" msgid="2782756722448800447">"Dvs. setați importanța acestor notificări."</string>
+ <string name="importance_from_user" msgid="2782756722448800447">"Tu setezi importanța acestor notificări."</string>
<string name="importance_from_person" msgid="4235804979664465383">"Notificarea este importantă având în vedere persoanele implicate."</string>
<string name="notification_history_title_placeholder" msgid="7748630986182249599">"Notificare de aplicație personalizată"</string>
- <string name="user_creation_account_exists" msgid="2239146360099708035">"Permiteți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>? (există deja un utilizator cu acest cont)"</string>
- <string name="user_creation_adding" msgid="7305185499667958364">"Permiteți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
- <string name="supervised_user_creation_label" msgid="6884904353827427515">"Adăugați un utilizator monitorizat"</string>
- <string name="language_selection_title" msgid="52674936078683285">"Adăugați o limbă"</string>
+ <string name="user_creation_account_exists" msgid="2239146360099708035">"Permiți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>? (există deja un utilizator cu acest cont)"</string>
+ <string name="user_creation_adding" msgid="7305185499667958364">"Permiți ca <xliff:g id="APP">%1$s</xliff:g> să creeze un nou utilizator folosind <xliff:g id="ACCOUNT">%2$s</xliff:g>?"</string>
+ <string name="supervised_user_creation_label" msgid="6884904353827427515">"Adaugă un utilizator monitorizat"</string>
+ <string name="language_selection_title" msgid="52674936078683285">"Adaugă o limbă"</string>
<string name="country_selection_title" msgid="5221495687299014379">"Regiunea preferată"</string>
<string name="search_language_hint" msgid="7004225294308793583">"Numele limbii"</string>
<string name="language_picker_section_suggested" msgid="6556199184638990447">"Sugerate"</string>
@@ -1931,47 +1931,47 @@
<string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"Regiuni sugerate"</string>
<string name="language_picker_section_all" msgid="1985809075777564284">"Toate limbile"</string>
<string name="region_picker_section_all" msgid="756441309928774155">"Toate regiunile"</string>
- <string name="locale_search_menu" msgid="6258090710176422934">"Căutați"</string>
+ <string name="locale_search_menu" msgid="6258090710176422934">"Caută"</string>
<string name="app_suspended_title" msgid="888873445010322650">"Aplicația nu este disponibilă"</string>
<string name="app_suspended_default_message" msgid="6451215678552004172">"Momentan, aplicația <xliff:g id="APP_NAME_0">%1$s</xliff:g> nu este disponibilă. Aceasta este gestionată de <xliff:g id="APP_NAME_1">%2$s</xliff:g>."</string>
- <string name="app_suspended_more_details" msgid="211260942831587014">"Aflați mai multe"</string>
- <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Anulați întreruperea aplicației"</string>
- <string name="work_mode_off_title" msgid="961171256005852058">"Activați aplicațiile pentru lucru?"</string>
+ <string name="app_suspended_more_details" msgid="211260942831587014">"Află mai multe"</string>
+ <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"Anulează întreruperea aplicației"</string>
+ <string name="work_mode_off_title" msgid="961171256005852058">"Activezi aplicațiile pentru lucru?"</string>
<string name="work_mode_off_message" msgid="7319580997683623309">"Obțineți acces la aplicațiile pentru lucru și notificări"</string>
- <string name="work_mode_turn_on" msgid="3662561662475962285">"Activați"</string>
+ <string name="work_mode_turn_on" msgid="3662561662475962285">"Activează"</string>
<string name="app_blocked_title" msgid="7353262160455028160">"Aplicația nu este disponibilă"</string>
<string name="app_blocked_message" msgid="542972921087873023">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu este disponibilă momentan."</string>
<string name="app_streaming_blocked_title" msgid="6090945835898766139">"<xliff:g id="ACTIVITY">%1$s</xliff:g> nu este disponibilă"</string>
<string name="app_streaming_blocked_title_for_permission_dialog" msgid="4483161748582966785">"Necesită permisiune"</string>
<string name="app_streaming_blocked_title_for_camera_dialog" msgid="3935701653713853065">"Camera video nu este disponibilă"</string>
- <string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Continuați pe telefon"</string>
+ <string name="app_streaming_blocked_title_for_fingerprint_dialog" msgid="3516853717714141951">"Continuă pe telefon"</string>
<string name="app_streaming_blocked_title_for_microphone_dialog" msgid="544822455127171206">"Microfon indisponibil"</string>
<string name="app_streaming_blocked_title_for_playstore_dialog" msgid="8149823099822897538">"Aplicația Magazin Play nu este disponibilă"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="tv" msgid="196994247017450357">"Setările pentru Android TV sunt indisponibile"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="tablet" msgid="8222710146267948647">"Setările pentru tabletă sunt indisponibile"</string>
<string name="app_streaming_blocked_title_for_settings_dialog" product="default" msgid="6895719984375299791">"Setările pentru telefon sunt indisponibile"</string>
- <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încercați pe dispozitivul Android TV."</string>
- <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încercați pe tabletă."</string>
- <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încercați pe telefon."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Aplicația necesită securitate suplimentară. Încercați pe dispozitivul Android TV."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Aplicația necesită securitate suplimentară. Încercați pe tabletă."</string>
- <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Aplicația necesită securitate suplimentară. Încercați pe telefon."</string>
+ <string name="app_streaming_blocked_message" product="tv" msgid="4003011766528814377">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încearcă pe dispozitivul Android TV."</string>
+ <string name="app_streaming_blocked_message" product="tablet" msgid="4242053045964946062">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încearcă pe tabletă."</string>
+ <string name="app_streaming_blocked_message" product="default" msgid="6159168735030739398">"Aplicația nu poate fi accesată pe <xliff:g id="DEVICE">%1$s</xliff:g> momentan. Încearcă pe telefon."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tv" msgid="3470977315395784567">"Aplicația necesită securitate suplimentară. Încearcă pe dispozitivul Android TV."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="tablet" msgid="698460091901465092">"Aplicația necesită securitate suplimentară. Încearcă pe tabletă."</string>
+ <string name="app_streaming_blocked_message_for_fingerprint_dialog" product="default" msgid="8552691971910603907">"Aplicația necesită securitate suplimentară. Încearcă pe telefon."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tv" msgid="820334666354451145">"Nu se poate accesa pe <xliff:g id="DEVICE">%1$s</xliff:g>. Încearcă pe dispozitivul Android TV."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="tablet" msgid="3286849551133045896">"Nu se poate accesa pe <xliff:g id="DEVICE">%1$s</xliff:g>. Încearcă pe tabletă."</string>
<string name="app_streaming_blocked_message_for_settings_dialog" product="default" msgid="6264287556598916295">"Nu se poate accesa pe <xliff:g id="DEVICE">%1$s</xliff:g>. Încearcă pe telefon."</string>
<string name="deprecated_target_sdk_message" msgid="5203207875657579953">"Această aplicație a fost creată pentru o versiune Android mai veche și este posibil să nu funcționeze corect. Încercați să căutați actualizări sau contactați dezvoltatorul."</string>
- <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Căutați actualizări"</string>
+ <string name="deprecated_target_sdk_app_store" msgid="8456784048558808909">"Caută actualizări"</string>
<string name="new_sms_notification_title" msgid="6528758221319927107">"Aveți mesaje noi"</string>
- <string name="new_sms_notification_content" msgid="3197949934153460639">"Deschideți aplicația pentru SMS-uri ca să vizualizați"</string>
+ <string name="new_sms_notification_content" msgid="3197949934153460639">"Deschide aplicația pentru SMS-uri ca să vezi"</string>
<string name="profile_encrypted_title" msgid="9001208667521266472">"Unele funcții ar putea fi limitate"</string>
<string name="profile_encrypted_detail" msgid="5279730442756849055">"Profil de serviciu blocat"</string>
- <string name="profile_encrypted_message" msgid="1128512616293157802">"Atingeți ca să deblocați"</string>
+ <string name="profile_encrypted_message" msgid="1128512616293157802">"Atinge ca să deblochezi"</string>
<string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"Conectat la <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string>
- <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Atingeți pentru a vedea fișierele"</string>
+ <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"Atinge pentru a vedea fișierele"</string>
<string name="pin_target" msgid="8036028973110156895">"Fixați"</string>
<string name="pin_specific_target" msgid="7824671240625957415">"Fixați <xliff:g id="LABEL">%1$s</xliff:g>"</string>
- <string name="unpin_target" msgid="3963318576590204447">"Anulați fixarea"</string>
- <string name="unpin_specific_target" msgid="3859828252160908146">"Anulați fixarea pentru <xliff:g id="LABEL">%1$s</xliff:g>"</string>
+ <string name="unpin_target" msgid="3963318576590204447">"Anulează fixarea"</string>
+ <string name="unpin_specific_target" msgid="3859828252160908146">"Anulează fixarea pentru <xliff:g id="LABEL">%1$s</xliff:g>"</string>
<string name="app_info" msgid="6113278084877079851">"Informații despre aplicație"</string>
<string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string>
<string name="demo_starting_message" msgid="6577581216125805905">"Se pornește demonstrația…"</string>
@@ -1992,30 +1992,30 @@
<string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Remedierea erorilor prin USB"</string>
<string name="time_picker_hour_label" msgid="4208590187662336864">"oră"</string>
<string name="time_picker_minute_label" msgid="8307452311269824553">"minut"</string>
- <string name="time_picker_header_text" msgid="9073802285051516688">"Setați ora"</string>
- <string name="time_picker_input_error" msgid="8386271930742451034">"Introduceți o oră validă"</string>
- <string name="time_picker_prompt_label" msgid="303588544656363889">"Introduceți ora"</string>
+ <string name="time_picker_header_text" msgid="9073802285051516688">"Setează ora"</string>
+ <string name="time_picker_input_error" msgid="8386271930742451034">"Introdu o oră validă"</string>
+ <string name="time_picker_prompt_label" msgid="303588544656363889">"Introdu ora"</string>
<string name="time_picker_text_input_mode_description" msgid="4761160667516611576">"Pentru a introduce ora, comutați la modul de introducere a textului."</string>
- <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Pentru a introduce ora, comutați la modul ceas."</string>
+ <string name="time_picker_radial_mode_description" msgid="1222342577115016953">"Pentru a introduce ora, comută la modul ceas."</string>
<string name="autofill_picker_accessibility_title" msgid="4425806874792196599">"Opțiuni de completare automată"</string>
- <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Salvați pentru completare automată"</string>
+ <string name="autofill_save_accessibility_title" msgid="1523225776218450005">"Salvează pentru completare automată"</string>
<string name="autofill_error_cannot_autofill" msgid="6528827648643138596">"Conținutul nu poate fi completat automat"</string>
<string name="autofill_picker_no_suggestions" msgid="1076022650427481509">"Nicio sugestie de completare automată"</string>
<string name="autofill_picker_some_suggestions" msgid="5560549696296202701">"{count,plural, =1{O sugestie de completare automată}few{# sugestii de completare automată}other{# de sugestii de completare automată}}"</string>
- <string name="autofill_save_title" msgid="7719802414283739775">"Salvați în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
- <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Salvați <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
- <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Salvați <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_save_title_with_3types" msgid="6598228952100102578">"Salvați <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g> în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title" msgid="3630695947047069136">"Actualizați în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_type" msgid="5264152633488495704">"Actualizați <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Actualizați <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
- <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Actualizați aceste articole în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
- <string name="autofill_save_yes" msgid="8035743017382012850">"Salvați"</string>
+ <string name="autofill_save_title" msgid="7719802414283739775">"Salvezi în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Salvezi <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Salvezi <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_save_title_with_3types" msgid="6598228952100102578">"Salvezi <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g> în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_update_title" msgid="3630695947047069136">"Actualizezi în "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_update_title_with_type" msgid="5264152633488495704">"Actualizezi <xliff:g id="TYPE">%1$s</xliff:g> în "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_update_title_with_2types" msgid="1797514386321086273">"Actualizezi <xliff:g id="TYPE_0">%1$s</xliff:g> și <xliff:g id="TYPE_1">%2$s</xliff:g> în "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>"?"</string>
+ <string name="autofill_update_title_with_3types" msgid="1312232153076212291">"Actualizezi aceste articole în "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>": <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> și <xliff:g id="TYPE_2">%3$s</xliff:g>?"</string>
+ <string name="autofill_save_yes" msgid="8035743017382012850">"Salvează"</string>
<string name="autofill_save_no" msgid="9212826374207023544">"Nu, mulțumesc"</string>
<string name="autofill_save_notnow" msgid="2853932672029024195">"Nu acum"</string>
<string name="autofill_save_never" msgid="6821841919831402526">"Niciodată"</string>
- <string name="autofill_update_yes" msgid="4608662968996874445">"Actualizați"</string>
- <string name="autofill_continue_yes" msgid="7914985605534510385">"Continuați"</string>
+ <string name="autofill_update_yes" msgid="4608662968996874445">"Actualizează"</string>
+ <string name="autofill_continue_yes" msgid="7914985605534510385">"Continuă"</string>
<string name="autofill_save_type_password" msgid="5624528786144539944">"parolă"</string>
<string name="autofill_save_type_address" msgid="3111006395818252885">"adresă"</string>
<string name="autofill_save_type_credit_card" msgid="3583795235862046693">"card de credit"</string>
@@ -2028,7 +2028,7 @@
<string name="etws_primary_default_message_tsunami" msgid="5828171463387976279">"Părăsiți imediat zonele de coastă și din apropierea râurilor și îndreptați-vă spre un loc mai sigur, cum ar fi o zonă aflată la înălțime."</string>
<string name="etws_primary_default_message_earthquake_and_tsunami" msgid="4888224011071875068">"Păstrați-vă calmul și căutați un adăpost în apropiere."</string>
<string name="etws_primary_default_message_test" msgid="4583367373909549421">"Testarea mesajelor de urgență"</string>
- <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"Răspundeți"</string>
+ <string name="notification_reply_button_accessibility" msgid="5235776156579456126">"Răspunde"</string>
<string name="etws_primary_default_message_others" msgid="7958161706019130739"></string>
<string name="mmcc_authentication_reject" msgid="4891965994643876369">"Cardul SIM nu este permis pentru voce"</string>
<string name="mmcc_imsi_unknown_in_hlr" msgid="227760698553988751">"Cardul SIM nu este activat pentru voce"</string>
@@ -2046,15 +2046,15 @@
<string name="shortcut_restore_unknown_issue" msgid="2478146134395982154">"Nu s-a putut restabili comanda rapidă"</string>
<string name="shortcut_disabled_reason_unknown" msgid="753074793553599166">"Comanda rapidă este dezactivată"</string>
<string name="harmful_app_warning_uninstall" msgid="6472912975664191772">"DEZINSTALAȚI"</string>
- <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"Deschideți oricum"</string>
+ <string name="harmful_app_warning_open_anyway" msgid="5963657791740211807">"Deschide oricum"</string>
<string name="harmful_app_warning_title" msgid="8794823880881113856">"Aplicație dăunătoare detectată"</string>
- <string name="log_access_confirmation_title" msgid="2343578467290592708">"Permiteți ca <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> să acceseze toate jurnalele dispozitivului?"</string>
- <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permiteți accesul o dată"</string>
- <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nu permiteți"</string>
+ <string name="log_access_confirmation_title" msgid="2343578467290592708">"Permiți ca <xliff:g id="LOG_ACCESS_APP_NAME">%s</xliff:g> să acceseze toate jurnalele dispozitivului?"</string>
+ <string name="log_access_confirmation_allow" msgid="5302517782599389507">"Permite accesul o dată"</string>
+ <string name="log_access_confirmation_deny" msgid="7685790957455099845">"Nu permite"</string>
<string name="log_access_confirmation_body" msgid="1806692062668620735">"Jurnalele dispozitivului înregistrează activitatea de pe dispozitivul tău. Aplicațiile pot folosi aceste jurnale pentru a identifica și a remedia probleme.\n\nUnele jurnale pot să conțină informații sensibile, prin urmare permite accesul la toate jurnalele dispozitivului doar aplicațiilor în care ai încredere. \n\nDacă nu permiți accesul aplicației la toate jurnalele dispozitivului, aceasta poate în continuare să acceseze propriile jurnale. Este posibil ca producătorul dispozitivului să acceseze în continuare unele jurnale sau informații de pe dispozitiv."</string>
<string name="log_access_do_not_show_again" msgid="1058690599083091552">"Nu mai afișa"</string>
<string name="slices_permission_request" msgid="3677129866636153406">"<xliff:g id="APP_0">%1$s</xliff:g> vrea să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>"</string>
- <string name="screenshot_edit" msgid="7408934887203689207">"Editați"</string>
+ <string name="screenshot_edit" msgid="7408934887203689207">"Editează"</string>
<string name="volume_dialog_ringer_guidance_vibrate" msgid="2055927873175228519">"Apelurile și notificările vor vibra"</string>
<string name="volume_dialog_ringer_guidance_silent" msgid="1011246774949993783">"Apelurile și notificările vor avea sunetul dezactivat"</string>
<string name="notification_channel_system_changes" msgid="2462010596920209678">"Modificări de sistem"</string>
@@ -2062,26 +2062,26 @@
<string name="zen_upgrade_notification_visd_title" msgid="2001148984371968620">"Funcția nouă Nu deranja ascunde notificările"</string>
<string name="zen_upgrade_notification_visd_content" msgid="3683314609114134946">"Atingeți ca să aflați mai multe și să modificați"</string>
<string name="zen_upgrade_notification_title" msgid="8198167698095298717">"Funcția Nu deranja s-a schimbat"</string>
- <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"Atingeți pentru a verifica ce este blocat."</string>
+ <string name="zen_upgrade_notification_content" msgid="5228458567180124005">"Atinge pentru a verifica ce este blocat."</string>
<string name="review_notification_settings_title" msgid="5102557424459810820">"Examinați setările pentru notificări"</string>
<string name="review_notification_settings_text" msgid="5916244866751849279">"Începând cu Android 13, aplicațiile pe care le instalați necesită permisiunea de a trimite notificări. Atingeți ca să modificați permisiunea pentru aplicațiile existente."</string>
<string name="review_notification_settings_remind_me_action" msgid="1081081018678480907">"Mai târziu"</string>
- <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Închideți"</string>
+ <string name="review_notification_settings_dismiss" msgid="4160916504616428294">"Închide"</string>
<string name="notification_app_name_system" msgid="3045196791746735601">"Sistem"</string>
<string name="notification_app_name_settings" msgid="9088548800899952531">"Setări"</string>
<string name="notification_appops_camera_active" msgid="8177643089272352083">"Cameră foto"</string>
<string name="notification_appops_microphone_active" msgid="581333393214739332">"Microfon"</string>
<string name="notification_appops_overlay_active" msgid="5571732753262836481">"se afișează peste alte aplicații de pe ecran"</string>
<string name="notification_feedback_indicator" msgid="663476517711323016">"Oferiți feedback"</string>
- <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Notificarea a fost promovată la Prestabilită. Atingeți pentru a oferi feedback."</string>
- <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Notificarea a fost mutată în jos la Silențioasă. Atingeți pentru a oferi feedback."</string>
- <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Notificarea a fost mutată la un nivel superior. Atingeți pentru a oferi feedback."</string>
- <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Notificarea a fost mutată la un nivel inferior. Atingeți pentru a oferi feedback."</string>
+ <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Notificarea a fost promovată la Prestabilită. Atinge pentru a oferi feedback."</string>
+ <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Notificarea a fost mutată în jos la Silențioasă. Atinge pentru a oferi feedback."</string>
+ <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Notificarea a fost mutată la un nivel superior. Atinge pentru a oferi feedback."</string>
+ <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Notificarea a fost mutată la un nivel inferior. Atinge pentru a oferi feedback."</string>
<string name="nas_upgrade_notification_title" msgid="8436359459300146555">"Notificări optimizate"</string>
<string name="nas_upgrade_notification_content" msgid="5157550369837103337">"Acțiunile și răspunsurile sugerate sunt acum trimise prin notificări optimizate. Notificările adaptive Android nu mai sunt acceptate."</string>
<string name="nas_upgrade_notification_enable_action" msgid="3046406808378726874">"OK"</string>
- <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Dezactivați"</string>
- <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Aflați mai multe"</string>
+ <string name="nas_upgrade_notification_disable_action" msgid="3794833210043497982">"Dezactivează"</string>
+ <string name="nas_upgrade_notification_learn_more_action" msgid="7011130656195423947">"Află mai multe"</string>
<string name="nas_upgrade_notification_learn_more_content" msgid="3735480566983530650">"Notificările optimizate au înlocuit Notificările adaptive Android de pe Android 12. Această funcție afișează acțiuni și răspunsuri sugerate și vă organizează notificările.\n\nNotificările optimizate pot accesa conținutul notificărilor, inclusiv informații cu caracter personal, precum mesajele și numele persoanelor de contact. În plus, funcția poate să închidă sau să răspundă la notificări, de exemplu, să răspundă la apeluri telefonice și să gestioneze opțiunea Nu deranja."</string>
<string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificare pentru informații despre modul Rutină"</string>
<string name="dynamic_mode_notification_title" msgid="1388718452788985481">"Economisirea bateriei este activată"</string>
@@ -2127,7 +2127,7 @@
<string name="accessibility_system_action_on_screen_a11y_shortcut_label" msgid="8488701469459210309">"Comandă rapidă de accesibilitate de pe ecran"</string>
<string name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" msgid="1057878690209817886">"Selector de comenzi rapide de accesibilitate de pe ecran"</string>
<string name="accessibility_system_action_hardware_a11y_shortcut_label" msgid="5764644187715255107">"Comandă rapidă de accesibilitate"</string>
- <string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Închideți fereastra de notificări"</string>
+ <string name="accessibility_system_action_dismiss_notification_shade" msgid="8931637495533770352">"Închide fereastra de notificări"</string>
<string name="accessibility_system_action_dpad_up_label" msgid="1029042950229333782">"Dpad sus"</string>
<string name="accessibility_system_action_dpad_down_label" msgid="3441918448624921461">"Dpad jos"</string>
<string name="accessibility_system_action_dpad_left_label" msgid="6557647179116479152">"Dpad stânga"</string>
@@ -2150,35 +2150,35 @@
<string name="resolver_cant_share_with_personal_apps_explanation" msgid="6349766201904601544">"Acest conținut nu poate fi trimis cu aplicații personale"</string>
<string name="resolver_cant_access_personal_apps_explanation" msgid="1679399548862724359">"Acest conținut nu poate fi deschis cu aplicații personale"</string>
<string name="resolver_turn_on_work_apps" msgid="884910835250037247">"Profilul de serviciu este întrerupt"</string>
- <string name="resolver_switch_on_work" msgid="463709043650610420">"Atingeți pentru a activa"</string>
+ <string name="resolver_switch_on_work" msgid="463709043650610420">"Atinge pentru a activa"</string>
<string name="resolver_no_work_apps_available" msgid="3298291360133337270">"Nicio aplicație pentru lucru"</string>
<string name="resolver_no_personal_apps_available" msgid="6284837227019594881">"Nicio aplicație personală"</string>
- <string name="miniresolver_open_in_personal" msgid="3874522693661065566">"Deschideți <xliff:g id="APP">%s</xliff:g> în profilul personal?"</string>
- <string name="miniresolver_open_in_work" msgid="4415223793669536559">"Deschideți <xliff:g id="APP">%s</xliff:g> în profilul de serviciu?"</string>
- <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Folosiți browserul personal"</string>
- <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Folosiți browserul de serviciu"</string>
+ <string name="miniresolver_open_in_personal" msgid="3874522693661065566">"Deschizi <xliff:g id="APP">%s</xliff:g> în profilul personal?"</string>
+ <string name="miniresolver_open_in_work" msgid="4415223793669536559">"Deschizi <xliff:g id="APP">%s</xliff:g> în profilul de serviciu?"</string>
+ <string name="miniresolver_use_personal_browser" msgid="776072682871133308">"Folosește browserul personal"</string>
+ <string name="miniresolver_use_work_browser" msgid="543575306251952994">"Folosește browserul de serviciu"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_ENTRY" msgid="8050953231914637819">"Codul PIN de deblocare SIM privind rețeaua"</string>
<string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_ENTRY" msgid="7164399703751688214">"Codul PIN de deblocare SIM privind subsetul de rețea"</string>
<string name="PERSOSUBSTATE_SIM_CORPORATE_ENTRY" msgid="4447629474818217364">"Codul PIN de deblocare SIM corporativă"</string>
<string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_ENTRY" msgid="973059024670737358">"Codul PIN de deblocare SIM privind furnizorul de servicii"</string>
<string name="PERSOSUBSTATE_SIM_SIM_ENTRY" msgid="4487435301206073787">"Codul PIN de deblocare SIM"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ENTRY" msgid="768060297218652809">"Introduceți codul PUK"</string>
- <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ENTRY" msgid="7129527319490548930">"Introduceți codul PUK"</string>
- <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ENTRY" msgid="2876126640607573252">"Introduceți codul PUK"</string>
- <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ENTRY" msgid="8952595089930109282">"Introduceți codul PUK"</string>
- <string name="PERSOSUBSTATE_SIM_SIM_PUK_ENTRY" msgid="3013902515773728996">"Introduceți codul PUK"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_PUK_ENTRY" msgid="768060297218652809">"Introdu codul PUK"</string>
+ <string name="PERSOSUBSTATE_SIM_NETWORK_SUBSET_PUK_ENTRY" msgid="7129527319490548930">"Introdu codul PUK"</string>
+ <string name="PERSOSUBSTATE_SIM_CORPORATE_PUK_ENTRY" msgid="2876126640607573252">"Introdu codul PUK"</string>
+ <string name="PERSOSUBSTATE_SIM_SERVICE_PROVIDER_PUK_ENTRY" msgid="8952595089930109282">"Introdu codul PUK"</string>
+ <string name="PERSOSUBSTATE_SIM_SIM_PUK_ENTRY" msgid="3013902515773728996">"Introdu codul PUK"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK1_ENTRY" msgid="2974411408893410289">"Codul PIN de deblocare RUIM Network1"</string>
<string name="PERSOSUBSTATE_RUIM_NETWORK2_ENTRY" msgid="687618528751880721">"Codul PIN de deblocare RUIM Network2"</string>
<string name="PERSOSUBSTATE_RUIM_HRPD_ENTRY" msgid="6810596579655575381">"Codul PIN de deblocare RUIM Hrpd"</string>
<string name="PERSOSUBSTATE_RUIM_CORPORATE_ENTRY" msgid="2715929642540980259">"Codul PIN de deblocare RUIM corporativă"</string>
<string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_ENTRY" msgid="8557791623303951590">"Codul PIN de deblocare RUIM privind furnizorii de servicii"</string>
<string name="PERSOSUBSTATE_RUIM_RUIM_ENTRY" msgid="7382468767274580323">"Codul PIN de deblocare RUIM"</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ENTRY" msgid="6730880791104286987">"Introduceți codul PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ENTRY" msgid="6432126539782267026">"Introduceți codul PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ENTRY" msgid="1730510161529488920">"Introduceți codul PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ENTRY" msgid="3369885925003346830">"Introduceți codul PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ENTRY" msgid="9129139686191167829">"Introduceți codul PUK"</string>
- <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ENTRY" msgid="2869929685874615358">"Introduceți codul PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK1_PUK_ENTRY" msgid="6730880791104286987">"Introdu codul PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_NETWORK2_PUK_ENTRY" msgid="6432126539782267026">"Introdu codul PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_HRPD_PUK_ENTRY" msgid="1730510161529488920">"Introdu codul PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_SERVICE_PROVIDER_PUK_ENTRY" msgid="3369885925003346830">"Introdu codul PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_RUIM_PUK_ENTRY" msgid="9129139686191167829">"Introdu codul PUK"</string>
+ <string name="PERSOSUBSTATE_RUIM_CORPORATE_PUK_ENTRY" msgid="2869929685874615358">"Introdu codul PUK"</string>
<string name="PERSOSUBSTATE_SIM_SPN_ENTRY" msgid="1238663472392741771">"Codul PIN de deblocare SPN"</string>
<string name="PERSOSUBSTATE_SIM_SP_EHPLMN_ENTRY" msgid="3988705848553894358">"Codul PIN de deblocare privind furnizorul de servicii și Equivalent Home PLMN"</string>
<string name="PERSOSUBSTATE_SIM_ICCID_ENTRY" msgid="6186770686690993200">"Codul PIN de deblocare ICCID"</string>
@@ -2271,12 +2271,12 @@
<string name="config_pdp_reject_multi_conn_to_same_pdn_not_allowed" msgid="6024904218067254186"></string>
<string name="window_magnification_prompt_title" msgid="2876703640772778215">"Noi setări de mărire"</string>
<string name="window_magnification_prompt_content" msgid="8159173903032344891">"Acum puteți mări o parte a ecranului"</string>
- <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activați din Setări"</string>
+ <string name="turn_on_magnification_settings_action" msgid="8521433346684847700">"Activează din Setări"</string>
<string name="dismiss_action" msgid="1728820550388704784">"Respingeți"</string>
- <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Deblocați microfonul dispozitivului"</string>
- <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Deblocați camera dispozitivului"</string>
+ <string name="sensor_privacy_start_use_mic_notification_content_title" msgid="2420858361276370367">"Deblochează microfonul dispozitivului"</string>
+ <string name="sensor_privacy_start_use_camera_notification_content_title" msgid="7287720213963466672">"Deblochează camera dispozitivului"</string>
<string name="sensor_privacy_start_use_notification_content_text" msgid="7595608891015777346">"Pentru &lt;b&gt;<xliff:g id="APP">%s</xliff:g>&lt;/b&gt; și toate aplicațiile și serviciile"</string>
- <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Deblocați"</string>
+ <string name="sensor_privacy_start_use_dialog_turn_on_button" msgid="7089318886628390827">"Deblochează"</string>
<string name="sensor_privacy_notification_channel_label" msgid="936036783155261349">"Confidențialitatea privind senzorii"</string>
<string name="splash_screen_view_icon_description" msgid="180638751260598187">"Pictograma aplicației"</string>
<string name="splash_screen_view_branding_description" msgid="7911129347402728216">"Imaginea de branding a aplicației"</string>
@@ -2287,8 +2287,8 @@
<string name="notification_channel_abusive_bg_apps" msgid="6092140213264920355">"Activitate de fundal"</string>
<string name="notification_title_abusive_bg_apps" msgid="994230770856147656">"O aplicație consumă bateria"</string>
<string name="notification_title_long_running_fgs" msgid="8170284286477131587">"O aplicație este încă activă"</string>
- <string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"<xliff:g id="APP">%1$s</xliff:g> rulează în fundal. Atingeți pentru a gestiona utilizarea bateriei."</string>
- <string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> poate afecta autonomia bateriei. Atingeți pentru a examina aplicațiile active."</string>
+ <string name="notification_content_abusive_bg_apps" msgid="5296898075922695259">"<xliff:g id="APP">%1$s</xliff:g> rulează în fundal. Atinge pentru a gestiona utilizarea bateriei."</string>
+ <string name="notification_content_long_running_fgs" msgid="8258193410039977101">"<xliff:g id="APP">%1$s</xliff:g> poate afecta autonomia bateriei. Atinge pentru a examina aplicațiile active."</string>
<string name="notification_action_check_bg_apps" msgid="4758877443365362532">"Verificați aplicațiile active"</string>
<string name="vdm_camera_access_denied" product="default" msgid="6102378580971542473">"Nu se poate accesa camera foto a telefonului de pe <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
<string name="vdm_camera_access_denied" product="tablet" msgid="6895968310395249076">"Nu se poate accesa camera foto a tabletei de pe <xliff:g id="DEVICE">%1$s</xliff:g>"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 2cceafc90b72..bad041a25264 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -263,7 +263,7 @@
<string name="global_action_settings" msgid="4671878836947494217">"Настройки"</string>
<string name="global_action_assist" msgid="2517047220311505805">"Помощник"</string>
<string name="global_action_voice_assist" msgid="6655788068555086695">"Аудиоподсказки"</string>
- <string name="global_action_lockdown" msgid="2475471405907902963">"Блокировка"</string>
+ <string name="global_action_lockdown" msgid="2475471405907902963">"Блокировка входа"</string>
<string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"&gt;999"</string>
<string name="notification_hidden_text" msgid="2835519769868187223">"Новое уведомление"</string>
<string name="notification_channel_virtual_keyboard" msgid="6465975799223304567">"Виртуальная клавиатура"</string>
@@ -585,13 +585,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Использовать блокировку экрана"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Чтобы продолжить, разблокируйте экран."</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Плотно прижмите палец к сканеру."</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Не удалось распознать отпечаток. Повторите попытку."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Не удалось распознать отпечаток пальца. Повторите попытку."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Очистите сканер отпечатков пальцев и повторите попытку."</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Очистите сканер и повторите попытку."</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Плотно прижмите палец к сканеру."</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Вы перемещали палец слишком медленно. Повторите попытку."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Попробуйте сохранить отпечаток другого пальца."</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Слишком светло."</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Вы нажали кнопку питания."</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Попробуйте изменить положение пальца."</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Каждый раз немного меняйте положение пальца."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -603,12 +604,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лицо распознано, нажмите кнопку \"Подтвердить\""</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Сканер недоступен"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не удалось сохранить отпечаток."</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Превышено время ожидания. Повторите попытку."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Время настройки отпечатка пальца истекло. Повторите попытку."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Операция с отпечатком отменена."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Операция с отпечатком пальца отменена пользователем."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Слишком много попыток. Повторите позже."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Слишком много попыток. Используйте другой способ разблокировки экрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Слишком много попыток. Используйте другой способ разблокировки экрана."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Повторите попытку."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не удалось распознать отпечаток пальца. Повторите попытку."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Нет отсканированных отпечатков пальцев"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На этом устройстве нет сканера отпечатков пальцев."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сканер отпечатков пальцев временно отключен."</string>
@@ -636,8 +637,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Обратитесь в сервисный центр."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Невозможно создать модель лица. Повторите попытку."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Слишком светло. Сделайте освещение менее ярким."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Недостаточно света"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Переместите телефон дальше от лица"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Переместите телефон ближе к лицу"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Переместите телефон выше"</string>
@@ -1251,8 +1251,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск приложений."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Окончание загрузки..."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Вы нажали кнопку питания. Обычно это приводит к отключению экрана.\n\nПри добавлении отпечатка пальца слегка прикоснитесь к кнопке."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Нажмите, чтобы отключить экран"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Отключить экран"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Для завершения нужно отключить экран"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Отключить"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Продолжить сканирование отпечатка?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Вы нажали кнопку питания. Обычно это приводит к отключению экрана.\n\nЧтобы отсканировать отпечаток пальца, слегка прикоснитесь к кнопке."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Отключить экран"</string>
@@ -1613,7 +1613,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Телевизор"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Телефон"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Динамики док-станции"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Внешнее устройство"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Наушники"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Система"</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 06ac0f88ca3c..90b71de7a011 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"තිර අගුල භාවිත කරන්න"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ඉදිරියට යාමට ඔබගේ තිර අගුල ඇතුළත් කරන්න"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"සංවේදකය මත තදින් ඔබන්න"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ඇඟිලි සලකුණ පිරිසැකසීමට නොහැකි විය. කරුණාකර නැවත උත්සාහ කරන්න."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"ඇඟිලි සලකුණ හඳුනා ගත නොහැක. නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ඇඟිලි සලකුණු සංවේදකය පිරිසිදු කර නැවත උත්සාහ කරන්න"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"සංවේදකය පිරිසිදු කර නැවත උත්සාහ කරන්න"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"සංවේදකය මත තදින් ඔබන්න"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"ඇඟිල්ල වඩා සෙමෙන් ගෙන යන ලදි. කරුණාකර නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"තවත් ඇඟිලි සලකුණක් උත්සාහ කරන්න"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"දීප්තිය වැඩියි"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"බල එබීම අනාවරණය විය"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"සීරුමාරු කිරීම උත්සාහ කරන්න"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"එක් එක් අවස්ථාවේ ඔබගේ ඇඟිල්ලේ පිහිටීම මදක් වෙනස් කරන්න"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"මුහුණ සත්‍යාපනය කරන ලදී, කරුණාකර තහවුරු කරන්න ඔබන්න"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ඇඟිලි සලකුණු දෘඪාංගය ලද නොහැකිය."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ඇඟිලි සලකුණ පිහිටුවිය නොහැකිය"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"ඇඟිලි සලකුණු කාල නිමාව ළඟා විය. නැවත උත්සාහ කරන්න."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"ඇඟිලි සලකුණු පිහිටුවීම කාලය නිමා විය. නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ඇඟිලි සලකුණු මෙහෙයුම අවලංගු කරන ලදී."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"පරිශීලක විසින් ඇඟිලි සලකුණු මෙහෙයුම අවසන් කරන ලදී."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"උත්සාහයන් ඉතා වැඩි ගණනකි. කරුණාකර පසුව නැවත උත්සාහ කරන්න."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"උත්සාහ ගණන ඉතා වැඩියි. ඒ වෙනුවට තිර අගුල භාවිත කරන්න."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"උත්සාහ ගණන ඉතා වැඩියි. ඒ වෙනුවට තිර අගුල භාවිත කරන්න."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"නැවත උත්සාහ කරන්න."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ඇඟිලි සලකුණ සැකසීමට නොහැක. නැවත උත්සාහ කරන්න."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ඇඟිලි සලකුණු ඇතුළත් කර නොමැත."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"මෙම උපාංගයේ ඇඟිලි සලකුණු සංවේදකයක් නොමැත."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"සංවේදකය තාවකාලිකව අබල කර ඇත."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"අළුත්වැඩියා සැපයුම්කරුවෙකු බලන්න."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"ඔබගේ මුහුණු ආකෘතිය තැනිය නොහැකිය. නැවත උත්සාහ කරන්න."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"දීප්තිය වැඩියි. තවත් මඳ ආලෝකය උත්සාහ කරන්න."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"ප්‍රමාණවත් ආලෝකයක් නැත"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"දුරකථනය තවත් ඈතට ගෙන යන්න"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"දුරකථනය තවත් සමීපයට ගෙන එන්න"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"දුරකථනය තවත් ඉහළට ගෙන යන්න"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"යෙදුම් ආරම්භ කරමින්."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"ඇරඹුම අවසාන කරමින්."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"ඔබ බල බොත්තම එබුවේය — සාමාන්‍යයෙන් මෙය තිරය ක්‍රියාවිරහිත කරයි.\n\nඔබගේ ඇඟිලි සලකුණ පිහිටුවන අතරතුර සැහැල්ලුවෙන් තට්ටු කිරීමට උත්සාහ කරන්න."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"තිරය අක්‍රිය කිරීමට තට්ටු කරන්න"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"තිරය අක්‍රිය කරන්න"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"පිහිටුවීම නිම කිරීමට, තිරය අක්‍රිය කරන්න"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"ක්‍රියාවිරහිත කරන්න"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"ඔබගේ ඇඟිලි සලකුණ සත්‍යාපනය දිගටම කරන්නද?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"ඔබ බල බොත්තම එබුවේය — සාමාන්‍යයෙන් මෙය තිරය ක්‍රියාවිරහිත කරයි.\n\nඔබගේ ඇඟිලි සලකුණ සත්‍යාපනය කිරීමට සැහැල්ලුවෙන් තට්ටු කිරීමට උත්සාහ කරන්න."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"තිරය අක්‍රිය කරන්න"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"රූපවාහිනී"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"දුරකථනය"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"නාදක ඩොක් කරන්න"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"බාහිර උපාංගය"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ඉස් බණු"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"පද්ධතිය"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index cdc682982417..121e41098723 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -585,13 +585,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Použiť zámku obrazovky"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Pokračujte zadaním zámky obrazovky"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Pevne pridržte senzor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Odtlačok prsta sa nepodarilo spracovať. Skúste to znova."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Odtlačok prsta sa nedá rozpoznať. Skúste to znova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Vyčistite senzor odtlačkov prstov a skúste to znova"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Vyčistite senzor a skúste to znova"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Pevne pridržte senzor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Pohli ste prstom príliš pomaly. Skúste to znova."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Vyskúšajte iný odtlačok prsta"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Príliš jasno"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Bolo zistené stlačenie vypínača"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Vyskúšajte upraviť"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Zakaždým trocha zmeňte pozíciu prsta"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -603,12 +604,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Tvár bola overená, stlačte tlačidlo potvrdenia"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardvér na snímanie odtlačku prsta nie je k dispozícii"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Odtlačok prsta sa nedá nastaviť"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Časový limit rozpoznania odtlačku prsta vypršal. Skúste to znova."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Nastavenie odtlačku prsta vypršalo. Skúste to znova."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operácia týkajúca sa odtlačku prsta bola zrušená"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Overenie odtlačku prsta zrušil používateľ."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Príliš veľa pokusov. Skúste to neskôr."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Príliš veľa pokusov. Použite radšej zámku obrazovky."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Príliš veľa pokusov. Použite radšej zámku obrazovky."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Skúste to znova"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Odtlačok prsta sa nedá spracovať. Skúste to znova."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Neregistrovali ste žiadne odtlačky prstov."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Toto zariadenie nemá senzor odtlačkov prstov."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Senzor je dočasne vypnutý."</string>
@@ -636,8 +637,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Navštívte poskytovateľa opráv."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Model tváre sa nedá vytvoriť. Skúste to znova."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Príliš veľa svetla. Skúste jemnejšie osvetlenie."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nie je dostatok svetla"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Oddiaľte telefón"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Priblížte telefón"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Posuňte telefón vyššie"</string>
@@ -1251,8 +1251,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Prebieha spúšťanie aplikácií."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Prebieha dokončovanie spúšťania."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Stlačili ste vypínač. Obvykle tým vypnete obrazovku.\n\nPri nastavovaní odtlačku prsta skúste klepnúť jemne."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Klepnutím vypnite obrazovku"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Vypnúť obrazovku"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Nastavovanie ukončíte vypnutím obrazovky"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Vypnúť"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Pokračovať v overovaní odtlačku prsta?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Stlačili ste vypínač. Obvykle tým vypnete obrazovku.\n\nAk chcete overiť odtlačok prsta, skúste klepnúť jemne."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Vypnúť obrazovku"</string>
@@ -1613,7 +1613,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Televízor"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefón"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Reproduktory doku"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Externé zariadenie"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Slúchadlá"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Systém"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 6da07f8be2d5..8fc73ce8904a 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -585,13 +585,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Uporaba odklepanja s poverilnico"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Odklenite zaslon, če želite nadaljevati."</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Prst dobro pridržite na tipalu."</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Prstnega odtisa ni bilo mogoče obdelati. Poskusite znova."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Prstnega odtisa ni mogoče prepoznati. Poskusite znova."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Očistite tipalo prstnih odtisov in poskusite znova."</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Očistite tipalo in poskusite znova."</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Prst dobro pridržite na tipalu."</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Prepočasen premik prsta. Poskusite znova."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Poskusite z drugim prstnim odtisom."</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Presvetlo je."</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Zaznan je bil pritisk gumba za vklop."</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Poskusite popraviti položaj prsta."</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Vsakič nekoliko spremenite položaj prsta."</string>
<string-array name="fingerprint_acquired_vendor">
@@ -603,12 +604,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Pristnost obraza je preverjena. Pritisnite gumb »Potrdi«."</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Strojna oprema za prstne odtise ni na voljo."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Prstnega odtisa ni mogoče nastaviti."</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Dosežena časovna omejitev za prstni odtis. Poskusite znova."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Čas za nastavitev prstnega odtisa je potekel. Poskusite znova."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Dejanje s prstnim odtisom je bilo preklicano."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Dejanje s prstnim odtisom je preklical uporabnik."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Preveč poskusov. Poskusite znova pozneje."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Preveč poskusov. Odklenite z načinom za zaklepanje zaslona."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Preveč poskusov. Odklenite z zaklepanjem zaslona."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Poskusite znova."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Prstnega odtisa ni mogoče obdelati. Poskusite znova."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Ni registriranih prstnih odtisov."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Ta naprava nima tipala prstnih odtisov."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Tipalo je začasno onemogočeno."</string>
@@ -636,8 +637,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Obiščite ponudnika popravil."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Modela obraza ni mogoče ustvariti. Poskusite znova."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Presvetlo. Poskusite z blažjo osvetlitvijo."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Premalo svetlobe"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Telefon nekoliko odmaknite."</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Bolj približajte telefon."</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Telefon premaknite višje."</string>
@@ -1251,8 +1251,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Zagon aplikacij."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Dokončevanje zagona."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pritisnili ste gumb za vklop, s čimer običajno izklopite zaslon.\n\nPoskusite se narahlo dotakniti med nastavljanjem prstnega odtisa."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Dotaknite se za izklop zaslona"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Izklopi zaslon"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Za končanje nastavitve izklopite zaslon"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Izklopi"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Želite nadaljevati preverjanje prstnega odtisa?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pritisnili ste gumb za vklop, s čimer običajno izklopite zaslon.\n\nZa preverjanje prstnega odtisa se poskusite narahlo dotakniti."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Izklopi zaslon"</string>
@@ -1613,7 +1613,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Televizor"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Zvočniki stojala"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Zunanja naprava"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Slušalke"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistem"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 587215a1a93b..77fe6120a604 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Përdor kyçjen e ekranit"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Fut kyçjen e ekranit për të vazhduar"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Shtyp fort te sensori"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Gjurma e gishtit nuk mund të përpunohej. Provo përsëri."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Nuk mund ta dallojë gjurmën e gishtit. Provo përsëri."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Pastro sensorin e gjurmës së gishtit dhe provo sërish"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Pastro sensorin dhe provo sërish"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Shtyp fort te sensori"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Gishti lëvizi shumë ngadalë. Provo përsëri."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Provo një gjurmë gishti tjetër"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Me shumë ndriçim"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"U zbulua shtypja e \"Energjisë\""</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Provo ta rregullosh"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ndrysho pak pozicionin e gishtit çdo herë"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Fytyra u vërtetua, shtyp \"Konfirmo\""</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hardueri i gjurmës së gishtit nuk mundësohet."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Nuk mund të konfigurohet gjurma e gishtit"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Koha e veprimit për gjurmën e gishtit skadoi. Provo përsëri."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Konfigurimi i gjurmës së gishtit skadoi. Provo përsëri."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Operacioni i gjurmës së gishtit u anulua."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Veprimi i gjurmës së gishtit u anulua nga përdoruesi."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Keni bërë shumë tentativa. Provo përsëri më vonë."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Shumë përpjekje. Përdor më mirë kyçjen e ekranit."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Shumë përpjekje. Përdor më mirë kyçjen e ekranit."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Provo përsëri."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Gjurma e gishtit nuk mund të përpunohet. Provo përsëri."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Nuk ka asnjë gjurmë gishti të regjistruar."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kjo pajisje nuk ka sensor të gjurmës së gishtit."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensori është çaktivizuar përkohësisht."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vizito një ofrues të shërbimit të riparimit."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Modeli i fytyrës nuk krijohet. Provo sërish."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Me shumë ndriçim. Provo një ndriçim më të butë."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Nuk ka dritë të mjaftueshme"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Lëvize telefonin më larg"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Lëvize telefonin më afër"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Lëvize telefonin më lart"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Aplikacionet e fillimit."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Po përfundon nisjen."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Shtype butonin e energjisë — zakonisht, kjo e fik ekranin.\n\nProvo të trokasësh lehtë ndërkohë që konfiguron gjurmën e gishtit."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Trokit për ta fikur ekranin"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Fik ekranin"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Përfundo konfigurimin; fik ekranin"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Çaktivizo"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Të vazhdohet verifikimi i gjurmës?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Shtype butonin e energjisë — zakonisht, kjo e fik ekranin.\n\nProvo të trokasësh lehtë për të verifikuar gjurmën e gishtit."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Fik ekranin"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Televizori"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Altoparlantët e stacionit"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Pajisje e jashtme"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Kufjet"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistemi"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 2acaf5ccd2fd..c38c771bac9a 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -584,13 +584,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Користите закључавање екрана"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Употребите закључавање екрана да бисте наставили"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Јако притисните сензор"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Није успела обрада отиска прста. Пробајте поново."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Препознавање отиска прста није успело. Пробајте поново."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Обришите сензор за отисак прста и пробајте поново"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Обришите сензор и пробајте поново"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Јако притисните сензор"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Превише споро сте померили прст. Пробајте поново."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Пробајте са другим отиском прста"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Превише је светло"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Откривен је притисак дугмета за укључивање"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Пробајте да прилагодите"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Сваки пут помало промените положај прста"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -602,12 +603,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Лице је потврђено. Притисните Потврди"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Хардвер за отиске прстију није доступан."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Подешавање отиска прста није успело"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Временско ограничење за отисак прста је истекло. Пробајте поново."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Време за подешавање отиска прста је истекло. Пробајте поново."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Радња са отиском прста је отказана."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Корисник је отказао радњу са отиском прста."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Превише покушаја. Пробајте поново касније."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Превише покушаја. Користите закључавање екрана уместо тога."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Превише покушаја. Користите закључавање екрана уместо тога."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Пробајте поново."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Обрађивање отиска прста није успело. Пробајте поново."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Није регистрован ниједан отисак прста."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Овај уређај нема сензор за отисак прста."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Сензор је привремено онемогућен."</string>
@@ -635,8 +636,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Посетите добављача за поправке."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Прављење модела лица није успело. Пробајте поново."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Превише је светло. Пробајте са слабијим осветљењем."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Нема довољно светла"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Удаљите телефон"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Приближите телефон"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Померите телефон нагоре"</string>
@@ -1250,8 +1250,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Покретање апликација."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завршавање покретања."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Притиснули сте дугме за укључивање – тиме обично искључујете екран.\n\nПробајте лагано да додирнете док подешавате отисак прста."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Додирните да бисте искључили екран"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Искључи екран"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Завршите подешавање искључивањем екрана"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Искључи"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Настављате верификацију отиска прста?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Притиснули сте дугме за укључивање – тиме обично искључујете екран.\n\nПробајте лагано да додирнете да бисте верификовали отисак прста."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Искључи екран"</string>
@@ -1612,7 +1612,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"ТВ"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Телефон"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Звучници базне станице"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Спољни уређај"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Слушалице"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Систем"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index a6c162aa6b72..e65231d04276 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Använd skärmlåset"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Fortsätt med hjälp av ditt skärmlås"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Tryck på sensorn med ett stadigt tryck"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Fingeravtrycket kändes inte igen. Försök igen."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Rengör fingeravtryckssensorn och försök igen"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Rengör sensorn och försök igen"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Tryck hårt på sensorn"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Du rörde fingret för långsamt. Försök igen."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Testa ett annat fingeravtryck"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Det är för ljust"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Tryckning registrerades"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Testa att justera fingeravtrycket"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Flytta fingret lite varje gång"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ansiktet har autentiserats. Tryck på Bekräfta"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Det finns ingen maskinvara för fingeravtryck."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Det gick inte att konfigurera fingeravtryck"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Tidsgränsen för fingeravtrycket har uppnåtts. Försök igen."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Fingeravtryckskonfigurering nådde tidsgränsen. Försök igen."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Fingeravtrycksåtgärden avbröts."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Fingeravtrycksåtgärden avbröts av användaren."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Du har gjort för många försök. Försök igen senare."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"För många försök. Använd låsskärmen i stället."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"För många försök. Använd låsskärmen i stället."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Försök igen."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Det gick inte att bearbeta fingeravtrycket. Försök igen."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Inga fingeravtryck har registrerats."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Enheten har ingen fingeravtryckssensor."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensorn har tillfälligt inaktiverats."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Besök ett reparationsställe."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Ansiktsmodellen kunde inte skapas. Försök igen."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Det är för ljust. Testa lägre belysning."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"För lite ljus"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Flytta telefonen längre bort"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"För telefonen närmare"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Höj telefonen"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Appar startas."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Uppgraderingen är klar."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Du tryckte på av/på-knappen, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt när du konfigurerar fingeravtrycket."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Tryck för att stänga av skärmen"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Stäng av skärmen"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Stäng av skärmen för att avbryta"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Stäng av"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Vill du verifiera ditt fingeravtryck?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Du tryckte på av/på-knappen, vilket vanligtvis stänger av skärmen.\n\nTesta att trycka lätt för att verifiera ditt fingeravtryck."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Stäng av skärmen"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Mobil"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Dockningsstationens högtalare"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Extern enhet"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Hörlurar"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"System"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index b17f58e18434..8ec29b3164b4 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Tumia mbinu ya kufunga skrini"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Weka mbinu yako ya kufunga skrini ili uendelee"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Bonyeza kwa uthabiti kwenye kitambuzi"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Imeshindwa kuchakata alama ya kidole. Tafadhali jaribu tena."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Imeshindwa kutambua alama ya kidole. Jaribu tena."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Safisha kitambua alama ya kidole kisha ujaribu tena"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Safisha kitambuzi kisha ujaribu tena"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Bonyeza kwa nguvu kwenye kitambuzi"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Kidole kilisogezwa polepole zaidi. Tafadhali jaribu tena."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Jaribu alama nyingine ya kidole"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Inang\'aa mno"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Kubonyeza kitufe cha kuwasha/kuzima kumetambuliwa"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Jaribu kurekebisha"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Badilisha mkao wa kidole chako kiasi kila wakati"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Uso umethibitishwa, tafadhali bonyeza thibitisha"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Maunzi ya alama ya kidole hayapatikani."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Imeshindwa kuweka mipangilio ya alama ya kidole"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Muda wa kuweka alama ya kidole umekwisha. Jaribu tena."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Muda wa kuweka alama ya kidole umeisha. Jaribu tena."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Mchakato wa alama ya kidole umeghairiwa."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Mtumiaji ameghairi uthibitishaji wa alama ya kidole."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Majaribio mengi mno. Jaribu tena baadaye."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Umejaribu mara nyingi mno. Badala yake, tumia mbinu ya kufunga skrini."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Umejaribu mara nyingi mno. Badala yake, tumia mbinu ya kufunga skrini."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Jaribu tena."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Imeshindwa kutambua alama ya kidole. Jaribu tena."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hakuna alama za vidole zilizojumuishwa."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Kifaa hiki hakina kitambua alama ya kidole."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Kitambuzi kimezimwa kwa muda."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Tembelea mtoa huduma za urekebishaji."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Imeshindwa kuunda muundo wa uso wako. Jaribu tena."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Inang\'aa mno. Jaribu mwangaza hafifu"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Hakuna mwangaza wa kutosha"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Sogeza simu mbali kiasi"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Sogeza simu karibu"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Sogeza simu juu zaidi"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Programu zinaanza"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Inamaliza kuwasha."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Umebonyeza kitufe cha kuwasha/kuzima — kwa kawaida, hali hii huzima skrini.\n\nJaribu kugusa taratibu unapoweka mipangilio ya alama ya kidole chako."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Gusa ili uzime skrini"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Zima skrini"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Kusitisha kuweka alama ya kidole, zima skrini"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Zima"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Utaendelea kuthibitisha alama ya kidole chako?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Umebonyeza kitufe cha kuwasha/kuzima — kwa kawaida, hali hii huzima skrini.\n\nJaribu kugusa taratibu ili uthibitishe alama ya kidole chako."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Zima skrini"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Runinga"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Simu"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Vipasa sauti vya kituo"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Kifaa cha Nje"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Vipokeasauti"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Mfumo"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 6a7973e24760..ea5227c363ea 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"திரைப் பூட்டைப் பயன்படுத்து"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"தொடர்வதற்கு உங்கள் திரைப் பூட்டை உள்ளிடுங்கள்"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"சென்சாரின் மீது நன்றாக அழுத்தவும்"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"கைரேகையை அடையாளம் காண முடியவில்லை. மீண்டும் முயலவும்."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"கைரேகை சென்சாரைச் சுத்தம் செய்துவிட்டு மீண்டும் முயலவும்"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"சென்சாரைச் சுத்தம் செய்துவிட்டு மீண்டும் முயலவும்"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"சென்சாரின் மீது நன்றாக அழுத்தவும்"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"விரலை மிகவும் மெதுவாக நகர்த்திவிட்டீர்கள். மீண்டும் முயற்சிக்கவும்."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"வேறு கைரேகையை முயலவும்"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"அதிக வெளிச்சமாக உள்ளது"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"பவர் பட்டனை அழுத்தியது கண்டறியப்பட்டது"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"விரலைச் சரியாக வைக்கவும்"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ஒவ்வொரு முறையும் விரலின் நிலையைச் சிறிதளவு மாற்றுங்கள்"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"முகம் அங்கீகரிக்கப்பட்டது. ’உறுதிப்படுத்துக’ என்பதை அழுத்துக"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"கைரேகை வன்பொருள் இல்லை."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"கைரேகையை அமைக்க முடியவில்லை"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"கைரேகைக்கான நேரம் முடிந்தது. மீண்டும் முயலவும்."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"கைரேகை அமைவுக்கான நேரம் முடிந்துவிட்டது. மீண்டும் முயலவும்."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"கைரேகை செயல்பாடு ரத்துசெய்யப்பட்டது."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"பயனர், கைரேகை உறுதிப்படுத்துதலை ரத்துசெய்தார்."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"அதிகமான முயற்சிகள். பிறகு முயற்சிக்கவும்."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"பலமுறை முயன்றுவிட்டீர்கள். இதற்குப் பதிலாகத் திரைப்பூட்டைப் பயன்படுத்தவும்."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"பலமுறை முயன்றுவிட்டீர்கள். இதற்குப் பதிலாகத் திரைப்பூட்டைப் பயன்படுத்தவும்."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"மீண்டும் முயற்சிக்கவும்."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"கைரேகையைச் செயலாக்க முடியவில்லை. மீண்டும் முயலவும்."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"கைரேகைப் பதிவுகள் எதுவும் இல்லை."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"இந்தச் சாதனத்தில் கைரேகை சென்சார் இல்லை."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"சென்சார் தற்காலிகமாக முடக்கப்பட்டுள்ளது."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"பழுதுபார்ப்புச் சேவை வழங்குநரைத் தொடர்புகொள்ளவும்."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"முகத் தோற்றம் பதிவாகவில்லை. மீண்டும் முயலவும்."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"அதிக ஒளிர்வு. மிதமான ஒளியில் முயலவும்."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"போதிய வெளிச்சம் இல்லை"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"மொபைலை முகத்தில் இருந்து தள்ளிப் பிடிக்கவும்"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"மொபைலை அருகில் நகர்த்தவும்"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"மொபைலை மேலே நகர்த்தவும்"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ஆப்ஸ் தொடங்கப்படுகின்றன."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"துவக்குதலை முடிக்கிறது."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"நீங்கள் பவர் பட்டனை அழுத்தியுள்ளீர்கள் — வழக்கமாக இது திரையை ஆஃப் செய்யும்.\n\nஉங்கள் கைரேகையை அமைக்கும்போது மெதுவாகத் தொடுங்கள்."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"திரையை அணைக்க தட்டவும்"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"திரையை அணை"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"அமைவை நிறைவுசெய்ய திரையை முடக்குங்கள்"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"முடக்கு"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"கைரேகைச் சரிபார்ப்பைத் தொடரவா?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"நீங்கள் பவர் பட்டனை அழுத்தியுள்ளீர்கள் — வழக்கமாக இது திரையை ஆஃப் செய்யும்.\n\nஉங்கள் கைரேகையைச் சரிபார்க்க மெதுவாகத் தொடுங்கள்."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"திரையை ஆஃப் செய்"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"டிவி"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ஃபோன்"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"மொபைல் வைக்கும் கருவியின் ஸ்பீக்கர்கள்"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"வெளிப்புறச் சாதனம்"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ஹெட்ஃபோன்கள்"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"சிஸ்டம்"</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 8c53b1333ba9..66dcac639cba 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"స్క్రీన్ లాక్‌ను ఉపయోగించండి"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"కొనసాగించడానికి మీ స్క్రీన్ లాక్‌ను ఎంటర్ చేయండి"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"సెన్సార్ మీద గట్టిగా నొక్కండి"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడలేదు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"వేలిముద్రను గుర్తించడం సాధ్యపడదు. మళ్లీ ట్రై చేయండి."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"వేలిముద్ర సెన్సార్‌ను క్లీన్ చేసి, మళ్లీ ట్రై చేయండి"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"సెన్సార్‌ను క్లీన్ చేసి, మళ్లీ ట్రై చేయండి"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"సెన్సార్ మీద గట్టిగా నొక్కండి"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"వేలిని చాలా నెమ్మదిగా కదిలించారు. దయచేసి మళ్లీ ప్రయత్నించండి."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"మరొక వేలిముద్రను ట్రై చేయండి"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"వెలుతురు అధికంగా ఉంది"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"పవర్ బటన్ కనుగొనబడింది"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"సర్దుబాటు చేయడానికి ట్రై చేయండి"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ప్రతిసారీ మీ వేళ్ల స్థానాన్ని కొద్దిగా మార్చండి"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ముఖం ప్రమాణీకరించబడింది, దయచేసి ధృవీకరించును నొక్కండి"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"వేలిముద్ర హార్డ్‌వేర్ అందుబాటులో లేదు."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"వేలిముద్రను సెటప్ చేయడం సాధ్యం కాదు"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"వేలిముద్ర గడువు సమయం చేరుకుంది. మళ్లీ ప్రయత్నించండి."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"వేలిముద్ర సెటప్ సమయం ముగిసింది. మళ్లీ ట్రై చేయండి."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"వేలిముద్ర యాక్టివిటీ రద్దయింది."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"వేలిముద్ర చర్యని వినియోగదారు రద్దు చేశారు."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"చాలా ఎక్కువ ప్రయత్నాలు చేశారు. తర్వాత మళ్లీ ప్రయత్నించండి."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్‌ను ఉపయోగించండి."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"చాలా ఎక్కువ సార్లు ప్రయత్నించారు. బదులుగా స్క్రీన్ లాక్‌ను ఉపయోగించండి."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"మళ్లీ ప్రయత్నించండి."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"వేలిముద్రను ప్రాసెస్ చేయడం సాధ్యపడదు. మళ్లీ ట్రై చేయండి."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"వేలిముద్రలు నమోదు చేయబడలేదు."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"ఈ పరికరంలో వేలిముద్ర సెన్సార్ ఎంపిక లేదు."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"సెన్సార్ తాత్కాలికంగా డిజేబుల్ చేయబడింది."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"రిపెయిర్ ప్రొవైడర్‌ను సందర్శించండి."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"మీ ఫేస్‌మోడల్ క్రియేషన్ కుదరదు. మళ్లీ ట్రై చేయండి."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"వెలుతురు అధికంగా ఉంది. తక్కువ ఉండేలా చూడండి."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"తగిన కాంతి లేదు"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ఫోన్‌ను కాస్త దూరంగా జరపండి"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ఫోన్‌ను దగ్గరగా పట్టుకోండి"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ఫోన్‌ను పైకి పట్టుకోండి"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"యాప్‌లను ప్రారంభిస్తోంది."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"బూట్‌ను ముగిస్తోంది."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"మీరు పవర్ బటన్‌ను నొక్కారు — ఇది సాధారణంగా స్క్రీన్‌ను ఆఫ్ చేస్తుంది.\n\nమీ వేలిముద్రను సెటప్ చేస్తున్నప్పుడు తేలికగా ట్యాప్ చేయడానికి ట్రై చేయండి."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"స్క్రీన్‌ను ఆఫ్ చేయడానికి ట్యాప్ చేయండి"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"స్క్రీన్‌ను ఆఫ్ చేయి"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"సెటప్ ముగించడానికి, స్క్రీన్‌ను ఆఫ్ చేయి"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"ఆఫ్ చేయండి"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"మీ వేలిముద్ర వెరిఫై‌ను కొనసాగించాలా?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"మీరు పవర్ బటన్‌ను నొక్కారు — ఇది సాధారణంగా స్క్రీన్‌ను ఆఫ్ చేస్తుంది.\n\nమీ వేలిముద్రను వెరిఫై చేయడానికి తేలికగా ట్యాప్ చేయడం ట్రై చేయండి."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"స్క్రీన్‌ను ఆఫ్ చేయి"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"టీవీ"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"ఫోన్"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"డాక్ స్పీకర్‌లు"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"బాహ్య పరికరం"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"హెడ్‌ఫోన్‌లు"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"సిస్టమ్"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 7826774dadd4..0d1ea17ebf6d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"ใช้การล็อกหน้าจอ"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"ป้อนข้อมูลการล็อกหน้าจอเพื่อดำเนินการต่อ"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"กดเซ็นเซอร์ให้แน่น"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"ประมวลผลลายนิ้วมือไม่ได้ โปรดลองอีกครั้ง"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"ไม่รู้จักลายนิ้วมือ โปรดลองอีกครั้ง"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"ทำความสะอาดเซ็นเซอร์ลายนิ้วมือแล้วลองอีกครั้ง"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"ทำความสะอาดเซ็นเซอร์แล้วลองอีกครั้ง"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"กดเซ็นเซอร์ให้แน่น"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"นิ้วเคลื่อนที่ช้าเกินไป โปรดลองอีกครั้ง"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"ลองลายนิ้วมืออื่น"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"สว่างเกินไป"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"ตรวจพบว่ามีการกดปุ่มเปิด/ปิด"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ลองปรับการวางนิ้ว"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"เปลี่ยนตำแหน่งของนิ้วเล็กน้อยไปเรื่อยๆ ทุกครั้ง"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"ตรวจสอบสิทธิ์ใบหน้าแล้ว โปรดกดยืนยัน"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"ฮาร์ดแวร์ลายนิ้วมือไม่พร้อมใช้งาน"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"ตั้งค่าลายนิ้วมือไม่ได้"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"หมดเวลาใช้ลายนิ้วมือแล้ว โปรดลองอีกครั้ง"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"การตั้งค่าลายนิ้วมือหมดเวลา โปรดลองอีกครั้ง"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"ยกเลิกการทำงานของลายนิ้วมือ"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"ผู้ใช้ยกเลิกการทำงานของลายนิ้วมือ"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"ดำเนินการหลายครั้งเกินไป ลองอีกครั้งในภายหลัง"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"ลองหลายครั้งเกินไป ใช้การล็อกหน้าจอแทน"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"ลองหลายครั้งเกินไป ใช้การล็อกหน้าจอแทน"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"ลองอีกครั้ง"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"ประมวลผลลายนิ้วมือไม่ได้ โปรดลองอีกครั้ง"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"ไม่มีลายนิ้วมือที่ลงทะเบียน"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"อุปกรณ์นี้ไม่มีเซ็นเซอร์ลายนิ้วมือ"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"ปิดใช้เซ็นเซอร์ชั่วคราวแล้ว"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"โปรดติดต่อผู้ให้บริการซ่อม"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"สร้างรูปแบบใบหน้าไม่ได้ โปรดลองอีกครั้ง"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"สว่างเกินไป ลองหาตำแหน่งที่แสงน้อยกว่านี้"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"แสงสว่างไม่เพียงพอ"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"ถือโทรศัพท์ให้ห่างกว่านี้"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"ถือโทรศัพท์ให้ใกล้กว่านี้"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"ยกโทรศัพท์ให้สูงขึ้น"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"กำลังเริ่มต้นแอปพลิเคชัน"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"เสร็จสิ้นการบูต"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"คุณกดปุ่มเปิด/ปิดซึ่งโดยปกติจะเป็นการปิดหน้าจอ\n\nลองแตะเบาๆ ขณะตั้งค่าลายนิ้วมือ"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"แตะเพื่อปิดหน้าจอ"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"ปิดหน้าจอ"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"ปิดหน้าจอเพื่อสิ้นสุดการตั้งค่า"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"ปิด"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"ยืนยันลายนิ้วมือต่อไหม"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"คุณกดปุ่มเปิด/ปิดซึ่งโดยปกติจะเป็นการปิดหน้าจอ\n\nลองแตะเบาๆ เพื่อยืนยันลายนิ้วมือ"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"ปิดหน้าจอ"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"ทีวี"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"โทรศัพท์"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ลำโพงแท่นชาร์จ"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"อุปกรณ์ภายนอก"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"หูฟัง"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"ระบบ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index e9d17a99edf0..84f611213ed2 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Gumamit ng lock ng screen"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Ilagay ang iyong lock ng screen para magpatuloy"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Pumindot nang madiin sa sensor"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Hindi maproseso ang fingerprint. Pakisubukan ulit."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Hindi makilala ang fingerprint. Subukan ulit."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Linisin ang sensor para sa fingerprint at subukan ulit"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Linisin ang sensor at subukan ulit"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Pumindot nang madiin sa sensor"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Masyadong mabagal ang paggalaw ng daliri. Pakisubukan ulit."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Sumubok ng ibang fingerprint"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Masyadong maliwanag"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"May na-detect na pagpindot sa Power"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Subukang isaayos"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Bahagyang baguhin ang posisyon ng iyong daliri sa bawat pagkakataon"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Na-authenticate ang mukha, pakipindot ang kumpirmahin"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Hindi available ang hardware na ginagamitan ng fingerprint."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Hindi ma-set up ang fingerprint"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Nag-time out ang fingerprint. Subukang muli."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Nag-time out ang pag-set up ng fingerprint. Subukan ulit."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Nakansela ang operasyong ginagamitan ng fingerprint."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Kinansela ng user ang operasyon sa fingerprint."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Napakaraming pagtatangka. Subukan ulit sa ibang pagkakataon."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Masyadong maraming pagsubok. Gamitin na lang ang lock ng screen."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Masyadong maraming pagsubok. Gamitin na lang ang lock ng screen."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Subukang muli."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Hindi maproseso ang fingerprint. Subukan ulit."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Walang naka-enroll na fingerprint."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Walang sensor ng fingerprint ang device na ito."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Pansamantalang na-disable ang sensor."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Bumisita sa provider ng pag-aayos."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Hindi magawa ang iyong face model. Subukan ulit."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Masyadong maliwanag. Subukang bawasan ang liwanag."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Hindi sapat ang liwanag"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Ilayo pa ang telepono"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Ilapit pa ang telepono"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Itaas pa ang telepono"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Sinisimulan ang apps."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Pagtatapos ng pag-boot."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Pinindot mo ang power button — karaniwan nitong ino-off ang screen.\n\nSubukang i-tap habang sine-set up ang iyong fingerprint."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Mag-tap para i-off ang screen"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"I-off ang screen"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"I-off ang screen para tapusin ang setup"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"I-off"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Magpatuloy sa pag-verify ng fingerprint?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Pinindot mo ang power button — karaniwan nitong ino-off ang screen.\n\nSubukang i-tap para i-verify ang iyong fingerprint."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"I-off ang screen"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telepono"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Mga speaker ng dock"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"External na Device"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Mga Headphone"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"System"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 762b731a8c1d..e0c15859a0d3 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ekran kilidi kullan"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Devam etmek için ekran kilidinizi girin"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Sensöre sıkıca bastırın"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Parmak izi işlenemedi. Lütfen tekrar deneyin."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Parmak izi tanınamadı. Tekrar deneyin."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Parmak izi sensörünü temizleyip tekrar deneyin"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Sensörü temizleyip tekrar deneyin"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Sensöre sıkıca bastırın"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Parmak hareketi çok yavaştı. Lütfen tekrar deneyin."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Başka bir parmak izi deneyin"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Çok parlak"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Güç düğmesine basma algılandı"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Ayarlamayı deneyin"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Her defasında parmağınızın konumunu biraz değiştirin"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yüz kimliği doğrulandı, lütfen onayla\'ya basın"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Parmak izi donanımı kullanılamıyor."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Parmak izi ayarlanamıyor"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Parmak izi için zaman aşımı oluştu. Tekrar deneyin."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Parmak izi kurulumu zaman aşımına uğradı. Tekrar deneyin."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Parmak izi işlemi iptal edildi."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Parmak izi işlemi kullanıcı tarafından iptal edildi."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Çok fazla deneme yapıldı. Daha sonra tekrar deneyin."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Çok fazla deneme yapıldı. Bunun yerine ekran kilidini kullanın."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Çok fazla deneme yapıldı. Bunun yerine ekran kilidini kullanın."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Tekrar deneyin."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Parmak izi işlenemiyor. Tekrar deneyin."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Parmak izi kaydedilmedi."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu cihazda parmak izi sensörü yok."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensör geçici olarak devre dışı bırakıldı."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Bir onarım hizmeti sağlayıcıyı ziyaret edin."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Yüzünüzün modeli oluşturulamıyor. Tekrar deneyin."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Çok parlak. Parlaklığı daha az bir ışıklandırma deneyin."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Yeterli ışık yok"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Telefonu uzaklaştırın"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Telefonu yaklaştırın"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Telefonu daha yukarı kaldırın"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Uygulamalar başlatılıyor"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Açılış tamamlanıyor."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Güç düğmesine bastınız. Bu düğmeye basıldığında genellikle ekran kapanır.\n\nParmak izinizi tanımlarken hafifçe dokunmayı deneyin."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Ekranı kapatmak için dokunun"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Ekranı kapat"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Ekranı kapatarak kurulumu sonlandırın"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Kapat"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Parmak izi doğrulamaya devam edilsin mi?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Güç düğmesine bastınız. Bu düğmeye basıldığında genellikle ekran kapanır.\n\nParmak izinizi doğrulamak için hafifçe dokunmayı deneyin."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ekranı kapat"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Yuva hoparlörleri"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Harici Cihaz"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Kulaklıklar"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Sistem"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 730cfac172b7..750c16cda777 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -585,13 +585,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Доступ розблокуванням екрана"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Щоб продовжити, введіть дані для розблокування екрана"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Міцно притисніть палець до сканера"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Не вдалось обробити відбиток пальця. Повторіть спробу."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Відбиток пальця не розпізнано. Повторіть спробу."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Очистьте сканер відбитків пальців і повторіть спробу"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Очистьте сканер і повторіть спробу"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Міцно притисніть палець до сканера"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Ви провели пальцем надто повільно. Повторіть спробу."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Спробуйте інший відбиток пальця"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Надто яскраво"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Виявлено натискання кнопки живлення"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Спробуйте відкоригувати відбиток пальця"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Щоразу трохи змінюйте положення пальця"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -603,12 +604,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Обличчя автентифіковано. Натисніть \"Підтвердити\""</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Сканер відбитків пальців недоступний."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Не вдалося створити відбиток пальця"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Час очікування відбитка пальця минув. Повторіть спробу."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Час очікування для налаштування відбитка пальця минув. Повторіть спробу."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Дію з відбитком пальця скасовано."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Користувач скасував дію з відбитком пальця."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Забагато спроб. Спробуйте пізніше."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Забагато спроб. Використайте натомість розблокування екрана."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Забагато спроб. Використайте натомість розблокування екрана."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Повторіть спробу."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Не вдалось обробити відбиток пальця. Повторіть спробу."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Відбитки пальців не зареєстровано."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"На цьому пристрої немає сканера відбитків пальців."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Датчик тимчасово вимкнено."</string>
@@ -636,8 +637,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Зверніться до постачальника послуг із ремонту."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Модель обличчя не створено. Повторіть спробу."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Занадто яскраво. Потрібно менше світла."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Замало світла"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Тримайте телефон далі від обличчя"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Тримайте телефон ближче до обличчя"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Підніміть телефон вище"</string>
@@ -1080,7 +1080,7 @@
<string name="searchview_description_search" msgid="1045552007537359343">"Пошук"</string>
<string name="searchview_description_query" msgid="7430242366971716338">"Пошуковий запит"</string>
<string name="searchview_description_clear" msgid="1989371719192982900">"Очистити запит"</string>
- <string name="searchview_description_submit" msgid="6771060386117334686">"Наіслати запит"</string>
+ <string name="searchview_description_submit" msgid="6771060386117334686">"Надіслати запит"</string>
<string name="searchview_description_voice" msgid="42360159504884679">"Голосовий пошук"</string>
<string name="enable_explore_by_touch_warning_title" msgid="5095399706284943314">"Увімкнути дослідження дотиком?"</string>
<string name="enable_explore_by_touch_warning_message" product="tablet" msgid="1037295476738940824">"<xliff:g id="ACCESSIBILITY_SERVICE_NAME">%1$s</xliff:g> хоче ввімкнути функцію дослідження дотиком. Увімкнувши функцію дослідження дотиком, можна чути або бачити опис елемента, розташованого під вашим пальцем, або виконувати жести для взаємодії з планшетним ПК."</string>
@@ -1251,8 +1251,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Запуск програм."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Завершення завантаження."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ви натиснули кнопку живлення – зазвичай після цього вимикається екран.\n\nЩоб зареєструвати відбиток пальця, спробуйте лише злегка торкнутися датчика."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Натисніть, щоб вимкнути екран"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Вимкнути екран"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Щоб завершити, вимкніть екран"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Вимкнути"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Продовжити підтвердження відбитка?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ви натиснули кнопку живлення – зазвичай після цього вимикається екран.\n\nЩоб підтвердити відбиток пальця, спробуйте лише злегка торкнутися датчика."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Вимкнути екран"</string>
@@ -1613,7 +1613,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"Телевізор"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Телефон"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Динаміки док-станції"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Зовнішній пристрій"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Навушники"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Система"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 6fa00716e8da..c589440aa549 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"اسکرین لاک استعمال کریں"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"جاری رکھنے کے لیے اپنا اسکرین لاک درج کریں"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"سینسر پر اچھی طرح دبائیں"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"فنگر پرنٹ پر کارروائی نہیں کی جا سکی۔ براہ کرم دوبارہ کوشش کریں۔"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"فنگر پرنٹ کی شناخت نہیں کی جا سکی۔ دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"فنگر پرنٹ سینسر صاف کریں اور دوبارہ کوشش کریں"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"سینسر صاف کریں اور دوبارہ کوشش کریں"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"سینسر پر اچھی طرح دبائیں"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"انگلی کو بہت آہستہ ہٹایا گیا۔ براہ کرم دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"دوسرا فنگر پرنٹ آزمائیں"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"کافی روشنی ہے"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"پاور پریس کا پتہ چلا"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"ایڈجسٹ کرنے کی کوشش کریں"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"ہر بار اپنی انگلی کی پوزیشن کو تھوڑا تبدیل کریں"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"چہرے کی تصدیق ہو گئی، براہ کرم \'تصدیق کریں\' کو دبائيں"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"فنگر پرنٹ ہارڈ ویئر دستیاب نہیں ہے۔"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"فنگر پرنٹ کو سیٹ اپ نہیں کیا جا سکا"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"فنگر پرنٹ کی میعاد ختم ہوگئی۔ دوبارہ کوشش کریں۔"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"فنگر پرنٹ کے سیٹ اپ کا وقت ختم ہو گیا۔ دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"فنگر پرنٹ کی کارروائی منسوخ ہوگئی۔"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"صارف نے فنگر پرنٹ کی کارروائی منسوخ کر دی۔"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"کافی زیادہ کوششیں کی گئیں۔ بعد میں دوبارہ کوشش کریں۔"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"کافی زیادہ کوششیں۔ اس کے بجائے اسکرین لاک کا استعمال کریں۔"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"کافی زیادہ کوششیں۔ اس کے بجائے اسکرین لاک کا استعمال کریں۔"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"دوبارہ کوشش کریں۔"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"فنگر پرنٹ پروسیس نہیں ہو سکتا۔ دوبارہ کوشش کریں۔"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"کوئی فنگر پرنٹ مندرج شدہ نہیں ہے۔"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"اس آلہ میں فنگر پرنٹ سینسر نہیں ہے۔"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"سینسر عارضی طور غیر فعال ہے۔"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"ایک مرمت فراہم کنندہ کو ملاحظہ کریں۔"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"آپکے چہرے کا ماڈل تخلیق نہیں ہو سکا۔ پھر کوشش کریں۔"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"کافی روشنی ہے۔ ہلکی روشنی میں آزمائیں۔"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"کافی روشنی نہیں ہے"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"فون کو تھوڑا دور کریں"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"فون کو تھوڑا قریب کریں"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"فون کو تھوڑا اوپر لے جائیں"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"ایپس شروع ہو رہی ہیں۔"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"بوٹ مکمل ہو رہا ہے۔"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"آپ نے پاور بٹن دبایا — اس سے عام طور پر اسکرین آف ہو جاتی ہے۔\n\nاپنے فنگر پرنٹ کو سیٹ اپ کرنے کے دوران ہلکا سا تھپتھپانے کی کوشش کریں۔"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"اسکرین آف کرنے کیلئے تھپتھپائیں"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"اسکرین آف کریں"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"سیٹ اپ ختم کرنے کیلئے، اسکرین آف کریں"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"آف کریں"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"اپنے فنگر پرنٹ کی توثیق کرنا جاری رکھیں؟"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"آپ نے پاور بٹن دبایا — اس سے عام طور پر اسکرین آف ہو جاتی ہے۔\n\nاپنے فنگر پرنٹ کی توثیق کرنے کے لیے ہلکا سا تھپتھپانے کی کوشش کریں۔"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"اسکرین آف کریں"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"فون"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"ڈاک اسپیکرز"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"بیرونی آلہ"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"ہیڈ فونز"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"سسٹم"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index b9a0ae5440b1..a0bcc984a96a 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Ekran qulfi"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Ekran qulfini kiritish bilan davom eting"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Sensorni mahkam bosing"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Barmoq izi aniqlanmadi. Qaytadan urining."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Bu barmoq izi notanish. Qayta urining."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Barmoq izi skanerini tozalang va qayta urining"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Sensorni tozalang va qayta urining"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Sensorni mahkam bosing"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Barmoq juda sekin harakatlandi. Qayta urinib ko‘ring."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Boshqa barmoq izi bilan urining"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Juda yorqin"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Quvvat tugmasi bosildi"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Moslashga urining"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Barmoqni har safar biroz surib joylang"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Yuzingiz aniqlandi, tasdiqlash uchun bosing"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Barmoq izi skaneri ish holatida emas."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Barmoq izi sozlanmadi"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Barmoq izini aniqlash vaqti tugab qoldi. Qayta urinib ko‘ring."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Barmoq izini sozlash vaqti tugadi. Qayta urining."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Barmoq izi tekshiruvi bekor qilindi."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Barmoq izi amali foydalanuvchi tomonidan bekor qilindi"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Urinishlar soni ko‘payib ketdi. Keyinroq qayta urinib ko‘ring."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Juda koʻp urinildi. Ekran qulfi orqali urining."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Juda koʻp urinildi. Ekran qulfi orqali urining."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Qayta urinib ko‘ring."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Barmoq izi tekshirilmadi. Qayta urining."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Hech qanday barmoq izi qayd qilinmagan."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Bu qurilmada barmoq izi skaneri mavjud emas."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Sensor vaqtincha faol emas."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Xizmat koʻrsatish markaziga murojaat qiling."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Yuzingiz modeli yaratilmadi. Qayta urining."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Juda yorqin. Biroz soyaroq joy tanlang."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Yorugʻlik yetarli emas"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Telefonni biroz uzoqroq tuting"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Telefonni yaqinroq tuting"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Telefonni teparoq tuting"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Ilovalar ishga tushirilmoqda."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Tizimni yuklashni tugatish."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Quvvat tugmasini bosdingiz — bu odatda ekranni oʻchiradi.\n\nBarmoq izini qoʻshish vaqtida tugmaga yengilgina tegining."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Ekranni oʻchirish uchun bosing"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Ekranni oʻchirish"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Sozlashni yakunlash uchun ekranni oʻchiring"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Faolsizlantirish"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Barmoq izi tasdiqlashda davom etilsinmi?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Quvvat tugmasini bosdingiz. Bu odatda ekranni oʻchiradi.\n\nBarmoq izingizni tasdiqlash uchun tugmaga yengilgina tegining."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Ekranni oʻchirish"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Telefon"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Taglik karnaylar"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Tashqi qurilma"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Quloq karnaychalari"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Tizim"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index ad4a3899e3b8..c94730a0522e 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Dùng phương thức khóa màn hình"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Hãy nhập phương thức khóa màn hình của bạn để tiếp tục"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Nhấn chắc trên cảm biến"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Không thể xử lý vân tay. Vui lòng thử lại."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Không nhận dạng được vân tay. Hãy thử lại."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Hãy vệ sinh cảm biến vân tay rồi thử lại"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Vệ sinh cảm biến rồi thử lại"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Nhấn chắc trên cảm biến"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Di chuyển ngón tay quá chậm. Vui lòng thử lại."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Hãy thử một vân tay khác"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Quá sáng"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Phát hiện thấy thao tác nhấn nút Nguồn"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Hãy thử điều chỉnh"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Mỗi lần, hãy thay đổi vị trí ngón tay một chút"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Đã xác thực khuôn mặt, vui lòng nhấn để xác nhận"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Phần cứng vân tay không khả dụng."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Không thể thiết lập vân tay"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Đã hết thời gian chờ vân tay. Hãy thử lại."</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Hết thời gian thiết lập vân tay. Hãy thử lại."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Thao tác dùng dấu vân tay bị hủy."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Người dùng đã hủy thao tác dùng dấu vân tay."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Quá nhiều lần thử. Hãy thử lại sau."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Bạn đã thử quá nhiều lần. Hãy dùng phương thức khoá màn hình."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Bạn đã thử quá nhiều lần. Hãy dùng phương thức khoá màn hình."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Thử lại."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Không xử lý được vân tay. Hãy thử lại."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Chưa đăng ký vân tay."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Thiết bị này không có cảm biến vân tay."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Đã tạm thời tắt cảm biến."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Hãy liên hệ với một nhà cung cấp dịch vụ sửa chữa."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Không thể tạo mẫu khuôn mặt của bạn. Hãy thử lại."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Quá sáng. Hãy thử giảm độ sáng."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Không đủ ánh sáng"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Đưa điện thoại ra xa hơn"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Đưa điện thoại lại gần hơn"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Nâng điện thoại lên cao hơn"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Khởi động ứng dụng."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Hoàn tất khởi động."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Bạn đã nhấn nút nguồn – thao tác này thường tắt màn hình.\n\nHãy thử nhấn nhẹ khi thiết lập vân tay của bạn."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Nhấn để tắt màn hình"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Tắt màn hình"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Để kết thúc thiết lập, hãy tắt màn hình"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Tắt"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Tiếp tục xác minh vân tay của bạn?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Bạn đã nhấn nút nguồn – thao tác này thường tắt màn hình.\n\nHãy thử nhấn nhẹ để xác minh vân tay."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Tắt màn hình"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Điện thoại"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Loa đế"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Thiết bị bên ngoài"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Tai nghe"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Hệ thống"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ce5cd53800dd..b81633e440f1 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"使用屏幕锁定凭据"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"输入您的屏幕锁定凭据才能继续"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"请用力按住传感器"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"无法处理指纹,请重试。"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"无法识别指纹,请重试。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"请清洁指纹传感器,然后重试"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"请清洁传感器,然后重试"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"请用力按住传感器"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"手指移动太慢,请重试。"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"请试试其他指纹"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"光线太亮"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"检测到按下“电源”按钮的操作"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"请尝试调整指纹"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"请在每次放手指时略微更改手指的位置"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已验证,请按确认按钮"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指纹硬件无法使用。"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"无法设置指纹"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"指纹录入操作超时,请重试。"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"指纹设置已超时,请重试。"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指纹操作已取消。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"用户取消了指纹操作。"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"尝试次数过多,请稍后重试。"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"尝试次数过多,请通过屏幕锁定功能解锁。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"尝试次数过多,请通过屏幕锁定功能解锁。"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"请重试。"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"无法处理指纹,请重试。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未注册任何指纹。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此设备没有指纹传感器。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"传感器已暂时停用。"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"请联系维修服务提供商。"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"无法创建您的脸部模型,请重试。"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"亮度过高,请尝试使用较柔和的亮度。"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"光线不足"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"请将手机拿远一点"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"请将手机拿近一点"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"请将手机举高一点"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在启动应用。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"即将完成启动。"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"您已按电源按钮,这通常会关闭屏幕。\n\n请尝试在设置指纹时轻轻按一下。"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"按一下即可关闭屏幕"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"关闭屏幕"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"若要结束设置,请关闭屏幕"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"关闭"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"要继续验证您的指纹吗?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"您已按电源按钮,这通常会关闭屏幕。\n\n请尝试轻轻按一下来验证您的指纹。"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"关闭屏幕"</string>
@@ -1482,7 +1482,7 @@
<string name="sync_binding_label" msgid="469249309424662147">"同步"</string>
<string name="accessibility_binding_label" msgid="1974602776545801715">"无障碍"</string>
<string name="wallpaper_binding_label" msgid="1197440498000786738">"壁纸"</string>
- <string name="chooser_wallpaper" msgid="3082405680079923708">"更改壁纸"</string>
+ <string name="chooser_wallpaper" msgid="3082405680079923708">"更换壁纸"</string>
<string name="notification_listener_binding_label" msgid="2702165274471499713">"通知侦听器"</string>
<string name="vr_listener_binding_label" msgid="8013112996671206429">"VR 监听器"</string>
<string name="condition_provider_service_binding_label" msgid="8490641013951857673">"条件提供程序"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"电视"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"手机"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"基座扬声器"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"外部设备"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"耳机"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"系统"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 96c7b0be4f81..d7f33a548cb7 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"使用螢幕鎖定"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"如要繼續操作,請輸入螢幕鎖定解鎖憑證"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"請用力按住感應器"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"無法處理指紋。請再試一次。"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"無法辨識指紋,請再試一次。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"請清潔指紋感應器,然後再試一次"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"請清潔感應器,然後再試一次"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"請用力按住感應器"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"手指移動太慢,請重試。"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"改用其他指紋"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"太亮"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"偵測到按下開關按鈕"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"嘗試調整"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"每次掃瞄時請稍微變更手指的位置"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"面孔已經驗證,請㩒一下 [確認]"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"無法使用指紋軟件。"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"無法設定指紋"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"指紋已逾時。請再試一次。"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋設定逾時,請再試一次。"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋操作已取消。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"使用者已取消指紋操作。"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"嘗試次數過多,請稍後再試。"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"再試一次。"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"無法處理指紋,請再試一次。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未註冊任何指紋"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"此裝置沒有指紋感應器。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"請諮詢維修服務供應商。"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"無法建立面部模型,請再試一次。"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"影像太亮。請嘗試在更暗的環境下使用。"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"光線不足"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"請將手機移開一點"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"請將手機移近一點"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"請將手機向上移"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在啟動應用程式。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"啟動完成。"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"您已按下開關按鈕,這麼做通常會關閉螢幕。\n\n設定指紋時請嘗試輕按開關按鈕。"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"輕按即可關閉螢幕"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"關閉螢幕"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"如要結束設定,請關閉螢幕"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"關閉"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"要繼續驗證指紋嗎?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"您已按下開關按鈕,這麼做通常會關閉螢幕。\n\n嘗試輕按開關按鈕以驗證指紋。"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"關閉螢幕"</string>
@@ -1565,7 +1565,7 @@
<string name="action_bar_home_description_format" msgid="5087107531331621803">"%1$s:%2$s"</string>
<string name="action_bar_home_subtitle_description_format" msgid="4346835454749569826">"%1$s (%2$s):%3$s"</string>
<string name="storage_internal" msgid="8490227947584914460">"內部共用儲存空間"</string>
- <string name="storage_sd_card" msgid="3404740277075331881">"SD 記憶卡"</string>
+ <string name="storage_sd_card" msgid="3404740277075331881">"SD 卡"</string>
<string name="storage_sd_card_label" msgid="7526153141147470509">"<xliff:g id="MANUFACTURER">%s</xliff:g> SD 卡"</string>
<string name="storage_usb_drive" msgid="448030813201444573">"USB 驅動器"</string>
<string name="storage_usb_drive_label" msgid="6631740655876540521">"<xliff:g id="MANUFACTURER">%s</xliff:g> USB 驅動器"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"電視"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"手機"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"插座喇叭"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"外部裝置"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"耳機"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"系統"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 46d95c7fbb45..44aeb104a600 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"使用螢幕鎖定功能"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"如要繼續操作,請輸入螢幕鎖定憑證"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"請確實按住感應器"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"無法處理指紋,請再試一次。"</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"無法辨識指紋,請再試一次。"</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"請清潔指紋感應器,然後再試一次"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"清潔感應器,然後再試一次"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"請確實按住感應器"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"手指移動速度過慢,請再試一次。"</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"改用其他指紋"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"太亮"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"偵測到按下電源鍵"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"請試著調整"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"每次掃描時請稍微改變手指的位置"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"臉孔驗證成功,請按下 [確認] 按鈕"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"指紋硬體無法使用。"</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"無法設定指紋"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"指紋處理作業逾時,請再試一次。"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋設定逾時,請再試一次。"</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"指紋作業已取消。"</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"使用者已取消指紋驗證作業。"</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"嘗試次數過多,請稍後再試。"</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"嘗試次數過多,請改用螢幕鎖定功能。"</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"請再試一次。"</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"無法處理指紋,請再試一次。"</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"未登錄任何指紋。"</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"這個裝置沒有指紋感應器。"</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"感應器已暫時停用。"</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"請洽詢維修供應商。"</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"無法建立臉部模型,請再試一次。"</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"亮度過高,請嘗試使用較柔和的照明方式。"</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"光線不足"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"請將手機拿遠一點"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"請將手機拿近一點"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"請將手機舉高一點"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"正在啟動應用程式。"</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"啟動完成。"</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"你已按下電源鍵,這麼做通常會關閉螢幕。\n\n設定指紋時請試著減輕觸碰電源鍵的力道。"</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"輕觸即可關閉螢幕"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"關閉螢幕"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"如要結束設定,請關閉螢幕"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"關閉"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"要繼續驗證指紋嗎?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"你已按下電源鍵,這麼做通常會關閉螢幕。\n\n驗證指紋時,請試著減輕觸碰電源鍵的力道。"</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"關閉螢幕"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"電視"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"手機"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"座架喇叭"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"外部裝置"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"耳機"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"系統"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 6e640c60724c..d4d1f2a2cb89 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -583,13 +583,14 @@
<string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Sebenzisa isikhiya sesikrini"</string>
<string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Faka ukukhiya isikrini kwakho ukuze uqhubeke"</string>
<string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Cindezela ngokuqinile kunzwa"</string>
- <string name="fingerprint_acquired_insufficient" msgid="2545149524031515411">"Ayikwazanga ukucubungula izigxivizo zeminwe. Sicela uzame futhi."</string>
+ <string name="fingerprint_acquired_insufficient" msgid="623888149088216458">"Ayisazi isigxivizo somunwe. Zama futhi."</string>
<string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Hlanza inzwa yesigxivizo somunwe bese uzame futhi"</string>
<string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Hlanza inzwa bese uzame futhi"</string>
<string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Cindezela ngokuqinile kunzwa"</string>
<string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Umnwe uhanjiswe kancane kakhulu. Sicela uzame futhi."</string>
<string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Zama ezinye izigxivizo zeminwe"</string>
<string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Kukhanya kakhulu"</string>
+ <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Ukucindezela kwamandla kutholiwe"</string>
<string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Zama ukulungisa"</string>
<string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Shintsha indawo yomunwe wakho kancane isikhathi ngasinye"</string>
<string-array name="fingerprint_acquired_vendor">
@@ -601,12 +602,12 @@
<string name="face_authenticated_confirmation_required" msgid="6872632732508013755">"Ukuqinisekiswa kobuso, sicela ucindezele okuthi qinisekisa"</string>
<string name="fingerprint_error_hw_not_available" msgid="4571700896929561202">"Izingxenyekazi zekhompuyutha zezigxivizo zeminwe azitholakali."</string>
<string name="fingerprint_error_no_space" msgid="7285481581905967580">"Ayikwazi ukusetha izigxivizo zeminwe"</string>
- <string name="fingerprint_error_timeout" msgid="2946635815726054226">"Kufinyelelwe isikhathi sokuvala sezigxivizo zeminwe. Zama futhi"</string>
+ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"Ukusethwa kwesigxivizo somunwe kuphelelwe yisikhathi Zama futhi."</string>
<string name="fingerprint_error_canceled" msgid="540026881380070750">"Ukusebenza kwezigxivizo zeminwe kukhanseliwe."</string>
<string name="fingerprint_error_user_canceled" msgid="7685676229281231614">"Umsebenzi wezigxivizo zomunwe ukhanselwe umsebenzisi."</string>
- <string name="fingerprint_error_lockout" msgid="7853461265604738671">"Imizamo eminingi kakhulu. Zama futhi emuva kwesikhathi."</string>
+ <string name="fingerprint_error_lockout" msgid="6626753679019351368">"Imizamo eminingi kakhulu. Sebenzisa ukukhiya isikrini kunalokho."</string>
<string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"Imizamo eminingi kakhulu. Sebenzisa ukukhiya isikrini kunalokho."</string>
- <string name="fingerprint_error_unable_to_process" msgid="1148553603490048742">"Zama futhi."</string>
+ <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"Ayikwazi ukucubungula isigxivizo somunwe. Zama futhi."</string>
<string name="fingerprint_error_no_fingerprints" msgid="8671811719699072411">"Azikho izigxivizo zeminwe ezibhalisiwe."</string>
<string name="fingerprint_error_hw_not_present" msgid="578914350967423382">"Le divayisi ayinayo inzwa yezigxivizo zeminwe."</string>
<string name="fingerprint_error_security_update_required" msgid="7750187320640856433">"Inzwa ikhutshazwe okwesikhashana."</string>
@@ -634,8 +635,7 @@
<string name="fingerprint_recalibrate_notification_content" msgid="8519935717822194943">"Vakashela umhlinzeki wokulungisa."</string>
<string name="face_acquired_insufficient" msgid="6889245852748492218">"Ayikwazi ukusungula imodeli yobuso bakho. Zama futhi."</string>
<string name="face_acquired_too_bright" msgid="8070756048978079164">"Kukhanya kakhulu. Zama ukukhanya okuthambile."</string>
- <!-- no translation found for face_acquired_too_dark (8539853432479385326) -->
- <skip />
+ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Ukukhanya okunganele"</string>
<string name="face_acquired_too_close" msgid="4453646176196302462">"Yisa ifoni kude"</string>
<string name="face_acquired_too_far" msgid="2922278214231064859">"Sondeza ifoni eduze"</string>
<string name="face_acquired_too_high" msgid="8278815780046368576">"Yisa ifoni phezulu"</string>
@@ -1249,8 +1249,8 @@
<string name="android_upgrading_starting_apps" msgid="6206161195076057075">"Qalisa izinhlelo zokusebenza."</string>
<string name="android_upgrading_complete" msgid="409800058018374746">"Qedela ukuqala kabusha."</string>
<string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Ucindezele inkinobho yamandla — lokhu kuvame ukuvala isikrini.\n\nZama ukuthepha kancane ngenkathi usetha isigxivizo sakho somunwe."</string>
- <string name="fp_power_button_enrollment_title" msgid="8997641910928785172">"Thepha ukuze uvale isikrini"</string>
- <string name="fp_power_button_enrollment_button_text" msgid="8351290204990805109">"Vala isikrini"</string>
+ <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Ukuze umise ukusetha, vala isikrini"</string>
+ <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Vala"</string>
<string name="fp_power_button_bp_title" msgid="5585506104526820067">"Qhubeka uqinisekise isigxivizo sakho somunwe?"</string>
<string name="fp_power_button_bp_message" msgid="2983163038168903393">"Ucindezele inkinobho yamandla — lokhu kuvame ukuvala isikrini.\n\nZama ukuthepha kancane ukuze uqinisekise isigxivizo sakho somunwe."</string>
<string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Vala isikrini"</string>
@@ -1611,7 +1611,7 @@
<string name="default_audio_route_name" product="tv" msgid="4908971385068087367">"I-TV"</string>
<string name="default_audio_route_name" product="default" msgid="9213546147739983977">"Ifoni"</string>
<string name="default_audio_route_name_dock_speakers" msgid="1551166029093995289">"Izipikha ze-Dock"</string>
- <string name="default_audio_route_name_external_device" msgid="5474470558160717850">"HDMI"</string>
+ <string name="default_audio_route_name_external_device" msgid="8124229858618975">"Idivayisi Yangaphandle"</string>
<string name="default_audio_route_name_headphones" msgid="6954070994792640762">"Ama-headphone"</string>
<string name="default_audio_route_name_usb" msgid="895668743163316932">"I-USB"</string>
<string name="default_audio_route_category_name" msgid="5241740395748134483">"Isistimu"</string>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index dfada139b4fe..060f440c9c4f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1662,13 +1662,18 @@
darkening hysteresis constraint value is the n-th element of
config_screenDarkeningThresholds.
+ Historically, it has been assumed that this will be an integer array with values in the
+ range of [0, 255]. However, it is now assumed to be a float array with values in the
+ range of [0, 1]. To accommodate both the possibilities, we internally check the scale on
+ which the thresholds are defined, and calibrate it accordingly.
+
The (zero-based) index is calculated as follows: (MAX is the largest index of the array)
condition calculated index
value < level[0] 0
level[n] <= value < level[n+1] n+1
level[MAX] <= value MAX+1 -->
- <integer-array name="config_screenThresholdLevels">
- </integer-array>
+ <array name="config_screenThresholdLevels">
+ </array>
<!-- Array of hysteresis constraint values for brightening, represented as tenths of a
percent. The length of this array is assumed to be one greater than
@@ -4850,9 +4855,6 @@
-->
</array>
- <!-- How long it takes for the HW to start illuminating after the illumination is requested. -->
- <integer name="config_udfps_illumination_transition_ms">50</integer>
-
<!-- Indicates whether device has a power button fingerprint sensor. -->
<bool name="config_is_powerbutton_fps" translatable="false" >false</bool>
@@ -5191,7 +5193,7 @@
<!-- Vertical position of a center of the letterboxed app window.
0 corresponds to the upper side of the screen and 1 to the lower side. If given value < 0
or > 1, it is ignored and central position is used (0.5). -->
- <item name="config_letterboxVerticalPositionMultiplier" format="float" type="dimen">0.5</item>
+ <item name="config_letterboxVerticalPositionMultiplier" format="float" type="dimen">0.0</item>
<!-- Whether horizontal reachability repositioning is allowed for letterboxed fullscreen apps.
-->
diff --git a/core/res/res/values/config_device_idle.xml b/core/res/res/values/config_device_idle.xml
new file mode 100644
index 000000000000..8ed58f326c2d
--- /dev/null
+++ b/core/res/res/values/config_device_idle.xml
@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2022, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- These resources are around just to allow their values to be customized
+ for different hardware and product builds. Do not translate.
+
+ NOTE: The naming convention is "config_camelCaseValue". Some legacy
+ entries do not follow the convention, but all new entries should. -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Default for DeviceIdleController.Constants.FLEX_TIME_SHORT -->
+ <integer name="device_idle_flex_time_short_ms">60000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_AFTER_INACTIVE_TIMEOUT -->
+ <integer name="device_idle_light_after_inactive_to_ms">180000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_TIMEOUT -->
+ <integer name="device_idle_light_idle_to_ms">300000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_FACTOR -->
+ <item name="device_idle_light_idle_factor" format="float" type="integer">2.0</item>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_MAX_IDLE_TIMEOUT -->
+ <integer name="device_idle_light_max_idle_to_ms">900000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MIN_BUDGET -->
+ <integer name="device_idle_light_idle_maintenance_min_budget_ms">60000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LIGHT_IDLE_MAINTENANCE_MAX_BUDGET -->
+ <integer name="device_idle_light_idle_maintenance_max_budget_ms">300000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MIN_LIGHT_MAINTENANCE_TIME -->
+ <integer name="device_idle_min_light_maintenance_time_ms">5000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MIN_DEEP_MAINTENANCE_TIME -->
+ <integer name="device_idle_min_deep_maintenance_time_ms">30000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.INACTIVE_TIMEOUT -->
+ <integer name="device_idle_inactive_to_ms">1800000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.SENSING_TIMEOUT -->
+ <integer name="device_idle_sensing_to_ms">240000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LOCATING_TIMEOUT -->
+ <integer name="device_idle_locating_to_ms">30000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.LOCATION_ACCURACY -->
+ <item name="device_idle_location_accuracy" format="float" type="integer">20.0</item>
+
+ <!-- Default for DeviceIdleController.Constants.MOTION_INACTIVE_TIMEOUT -->
+ <integer name="device_idle_motion_inactive_to_ms">600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MOTION_INACTIVE_TIMEOUT_FLEX -->
+ <integer name="device_idle_motion_inactive_to_flex_ms">60000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_AFTER_INACTIVE_TIMEOUT -->
+ <integer name="device_idle_idle_after_inactive_to_ms">1800000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_PENDING_TIMEOUT -->
+ <integer name="device_idle_idle_pending_to_ms">300000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MAX_IDLE_PENDING_TIMEOUT -->
+ <integer name="device_idle_max_idle_pending_to_ms">600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_PENDING_FACTOR -->
+ <item name="device_idle_idle_pending_factor" format="float" type="integer">2.0</item>
+
+ <!-- Default for DeviceIdleController.Constants.QUICK_DOZE_DELAY_TIMEOUT -->
+ <integer name="device_idle_quick_doze_delay_to_ms">60000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_TIMEOUT -->
+ <integer name="device_idle_idle_to_ms">3600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MAX_IDLE_TIMEOUT -->
+ <integer name="device_idle_max_idle_to_ms">21600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.IDLE_FACTOR -->
+ <item name="device_idle_idle_factor" format="float" type="integer">2.0</item>
+
+ <!-- Default for DeviceIdleController.Constants.MIN_TIME_TO_ALARM -->
+ <integer name="device_idle_min_time_to_alarm_ms">3600000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MAX_TEMP_APP_ALLOWLIST_DURATION_MS -->
+ <integer name="device_idle_max_temp_app_allowlist_duration_ms">300000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.MMS_TEMP_APP_ALLOWLIST_DURATION_MS -->
+ <integer name="device_idle_mms_temp_app_allowlist_duration_ms">60000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.SMS_TEMP_APP_ALLOWLIST_DURATION_MS -->
+ <integer name="device_idle_sms_temp_app_allowlist_duration_ms">20000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.NOTIFICATION_ALLOWLIST_DURATION_MS -->
+ <integer name="device_idle_notification_allowlist_duration_ms">30000</integer>
+
+ <!-- Default for DeviceIdleController.Constants.WAIT_FOR_UNLOCK -->
+ <bool name="device_idle_wait_for_unlock">true</bool>
+
+ <!-- Default for DeviceIdleController.Constants.PRE_IDLE_FACTOR_LONG -->
+ <item name="device_idle_pre_idle_factor_long" format="float" type="integer">1.67</item>
+
+ <!-- Default for DeviceIdleController.Constants.PRE_IDLE_FACTOR_SHORT -->
+ <item name="device_idle_pre_idle_factor_short" format="float" type="integer">0.33</item>
+
+ <!-- Default for DeviceIdleController.Constants.USE_WINDOW_ALARMS -->
+ <bool name="device_idle_use_window_alarms">true</bool>
+</resources>
+
diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml
index 1327d9604001..ea2b988bd237 100644
--- a/core/res/res/values/config_telephony.xml
+++ b/core/res/res/values/config_telephony.xml
@@ -17,12 +17,6 @@
<resources>
<!-- This file defines Android telephony related resources -->
- <!-- Whether force disabling telephony new data stack or not.
- This flag and the old data stack code will be deleted in Android 14.
- -->
- <bool name="config_force_disable_telephony_new_data_stack">false</bool>
- <java-symbol type="bool" name="config_force_disable_telephony_new_data_stack" />
-
<!-- Configure tcp buffer sizes per network type in the form:
network-type:rmem_min,rmem_def,rmem_max,wmem_min,wmem_def,wmem_max
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9214f438826f..9b060593651f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5760,10 +5760,21 @@
<string name="log_access_confirmation_deny">Don\u2019t allow</string>
<!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
- <string name="log_access_confirmation_body">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs.
+ <string name="log_access_confirmation_body" product="default">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs.
\n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.
</string>
+ <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
+ <string name="log_access_confirmation_body" product="tv">Device logs record what happens on your device. Apps can use these logs to find and fix issues.\n\nSome logs may contain sensitive info, so only allow apps you trust to access all device logs.
+ \n\nIf you don’t allow this app to access all device logs, it can still access its own logs. Your device manufacturer may still be able to access some logs or info on your device.\n\nLearn more at g.co/android/devicelogs.
+ </string>
+
+ <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]-->
+ <string name="log_access_confirmation_learn_more" product="default" translatable="false">&lt;a href="https://support.google.com/android?p=system_logs#topic=7313011"&gt;Learn more&lt;/a&gt;</string>
+
+ <!-- Learn more URL for the log access confirmation dialog. [DO NOT TRANSLATE]-->
+ <string name="log_access_confirmation_learn_more" product="tv" translatable="false"></string>
+
<!-- Privacy notice do not show [CHAR LIMIT=20] -->
<string name="log_access_do_not_show_again">Don\u2019t show again</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a535c5097868..c3d40889636d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2705,7 +2705,6 @@
<java-symbol type="bool" name="allow_test_udfps" />
<java-symbol type="array" name="config_udfps_sensor_props" />
<java-symbol type="array" name="config_sfps_sensor_props" />
- <java-symbol type="integer" name="config_udfps_illumination_transition_ms" />
<java-symbol type="bool" name="config_is_powerbutton_fps" />
<java-symbol type="array" name="config_udfps_enroll_stage_thresholds" />
<java-symbol type="array" name="config_sfps_enroll_stage_thresholds" />
@@ -3935,8 +3934,10 @@
<java-symbol type="string" name="log_access_confirmation_deny" />
<java-symbol type="string" name="log_access_confirmation_title" />
<java-symbol type="string" name="log_access_confirmation_body" />
+ <java-symbol type="string" name="log_access_confirmation_learn_more" />
<java-symbol type="layout" name="log_access_user_consent_dialog_permission" />
<java-symbol type="id" name="log_access_dialog_title" />
+ <java-symbol type="id" name="log_access_dialog_body" />
<java-symbol type="id" name="log_access_dialog_allow_button" />
<java-symbol type="id" name="log_access_dialog_deny_button" />
@@ -4414,6 +4415,40 @@
<java-symbol type="array" name="config_notificationMsgPkgsAllowedAsConvos" />
+ <!-- To config device idle -->
+ <java-symbol type="integer" name="device_idle_flex_time_short_ms" />
+ <java-symbol type="integer" name="device_idle_light_after_inactive_to_ms" />
+ <java-symbol type="integer" name="device_idle_light_idle_to_ms" />
+ <java-symbol type="integer" name="device_idle_light_idle_factor" />
+ <java-symbol type="integer" name="device_idle_light_max_idle_to_ms" />
+ <java-symbol type="integer" name="device_idle_light_idle_maintenance_min_budget_ms" />
+ <java-symbol type="integer" name="device_idle_light_idle_maintenance_max_budget_ms" />
+ <java-symbol type="integer" name="device_idle_min_light_maintenance_time_ms" />
+ <java-symbol type="integer" name="device_idle_min_deep_maintenance_time_ms" />
+ <java-symbol type="integer" name="device_idle_inactive_to_ms" />
+ <java-symbol type="integer" name="device_idle_sensing_to_ms" />
+ <java-symbol type="integer" name="device_idle_locating_to_ms" />
+ <java-symbol type="integer" name="device_idle_location_accuracy" />
+ <java-symbol type="integer" name="device_idle_motion_inactive_to_ms" />
+ <java-symbol type="integer" name="device_idle_motion_inactive_to_flex_ms" />
+ <java-symbol type="integer" name="device_idle_idle_after_inactive_to_ms" />
+ <java-symbol type="integer" name="device_idle_idle_pending_to_ms" />
+ <java-symbol type="integer" name="device_idle_max_idle_pending_to_ms" />
+ <java-symbol type="integer" name="device_idle_idle_pending_factor" />
+ <java-symbol type="integer" name="device_idle_quick_doze_delay_to_ms" />
+ <java-symbol type="integer" name="device_idle_idle_to_ms" />
+ <java-symbol type="integer" name="device_idle_max_idle_to_ms" />
+ <java-symbol type="integer" name="device_idle_idle_factor" />
+ <java-symbol type="integer" name="device_idle_min_time_to_alarm_ms" />
+ <java-symbol type="integer" name="device_idle_max_temp_app_allowlist_duration_ms" />
+ <java-symbol type="integer" name="device_idle_mms_temp_app_allowlist_duration_ms" />
+ <java-symbol type="integer" name="device_idle_sms_temp_app_allowlist_duration_ms" />
+ <java-symbol type="integer" name="device_idle_notification_allowlist_duration_ms" />
+ <java-symbol type="bool" name="device_idle_wait_for_unlock" />
+ <java-symbol type="integer" name="device_idle_pre_idle_factor_long" />
+ <java-symbol type="integer" name="device_idle_pre_idle_factor_short" />
+ <java-symbol type="bool" name="device_idle_use_window_alarms" />
+
<!-- Binder heavy hitter watcher configs -->
<java-symbol type="bool" name="config_defaultBinderHeavyHitterWatcherEnabled" />
<java-symbol type="integer" name="config_defaultBinderHeavyHitterWatcherBatchSize" />
diff --git a/core/tests/coretests/src/android/ddm/DdmHandleViewDebugTest.java b/core/tests/coretests/src/android/ddm/DdmHandleViewDebugTest.java
new file mode 100644
index 000000000000..7248983c741c
--- /dev/null
+++ b/core/tests/coretests/src/android/ddm/DdmHandleViewDebugTest.java
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.ddm;
+
+import static android.ddm.DdmHandleViewDebug.deserializeMethodParameters;
+import static android.ddm.DdmHandleViewDebug.serializeReturnValue;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+
+import android.ddm.DdmHandleViewDebug.ViewMethodInvocationSerializationException;
+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.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public final class DdmHandleViewDebugTest {
+ // true
+ private static final byte[] SERIALIZED_BOOLEAN_TRUE = {0x00, 0x5A, 1};
+
+ @Test
+ public void serializeReturnValue_booleanTrue() throws Exception {
+ assertArrayEquals(SERIALIZED_BOOLEAN_TRUE, serializeReturnValue(boolean.class, true));
+ }
+
+ @Test
+ public void deserializeMethodParameters_booleanTrue() throws Exception {
+ expectDeserializedArgument(boolean.class, true, SERIALIZED_BOOLEAN_TRUE);
+ }
+
+ // false
+ private static final byte[] SERIALIZED_BOOLEAN_FALSE = {0x00, 0x5A, 0};
+
+ @Test
+ public void serializeReturnValue_booleanFalse() throws Exception {
+ assertArrayEquals(SERIALIZED_BOOLEAN_FALSE, serializeReturnValue(boolean.class, false));
+ }
+
+ @Test
+ public void deserializeMethodParameters_booleanFalse() throws Exception {
+ expectDeserializedArgument(boolean.class, false, SERIALIZED_BOOLEAN_FALSE);
+ }
+
+ // (byte) 42
+ private static final byte[] SERIALIZED_BYTE = {0x00, 0x42, 42};
+
+ @Test
+ public void serializeReturnValue_byte() throws Exception {
+ assertArrayEquals(SERIALIZED_BYTE, serializeReturnValue(byte.class, (byte) 42));
+ }
+
+ @Test
+ public void deserializeMethodParameters_byte() throws Exception {
+ expectDeserializedArgument(byte.class, (byte) 42, SERIALIZED_BYTE);
+ }
+
+ // '\u1122'
+ private static final byte[] SERIALIZED_CHAR = {0x00, 0x43, 0x11, 0x22};
+
+ @Test
+ public void serializeReturnValue_char() throws Exception {
+ assertArrayEquals(SERIALIZED_CHAR, serializeReturnValue(char.class, '\u1122'));
+ }
+
+ @Test
+ public void deserializeMethodParameters_char() throws Exception {
+ expectDeserializedArgument(char.class, '\u1122', SERIALIZED_CHAR);
+ }
+
+ // (short) 0x1011
+ private static final byte[] SERIALIZED_SHORT = {0x00, 0x53, 0x10, 0x11};
+
+ @Test
+ public void serializeReturnValue_short() throws Exception {
+ assertArrayEquals(SERIALIZED_SHORT,
+ serializeReturnValue(short.class, (short) 0x1011));
+ }
+
+ @Test
+ public void deserializeMethodParameters_short() throws Exception {
+ expectDeserializedArgument(short.class, (short) 0x1011, SERIALIZED_SHORT);
+ }
+
+ // 0x11223344
+ private static final byte[] SERIALIZED_INT = {0x00, 0x49, 0x11, 0x22, 0x33, 0x44};
+
+ @Test
+ public void serializeReturnValue_int() throws Exception {
+ assertArrayEquals(SERIALIZED_INT,
+ serializeReturnValue(int.class, 0x11223344));
+ }
+
+ @Test
+ public void deserializeMethodParameters_int() throws Exception {
+ expectDeserializedArgument(int.class, 0x11223344, SERIALIZED_INT);
+ }
+
+ // 0x0011223344556677L
+ private static final byte[] SERIALIZED_LONG =
+ {0x00, 0x4a, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77};
+
+ @Test
+ public void serializeReturnValue_long() throws Exception {
+ assertArrayEquals(SERIALIZED_LONG,
+ serializeReturnValue(long.class, 0x0011223344556677L));
+ }
+
+ @Test
+ public void deserializeMethodParameters_long() throws Exception {
+ expectDeserializedArgument(long.class, 0x0011223344556677L, SERIALIZED_LONG);
+ }
+
+ // 3.141d
+ private static final byte[] SERIALIZED_DOUBLE =
+ {0x00, 0x44, (byte) 0x40, (byte) 0x09, (byte) 0x20, (byte) 0xc4, (byte) 0x9b,
+ (byte) 0xa5, (byte) 0xe3, (byte) 0x54};
+
+ @Test
+ public void serializeReturnValue_double() throws Exception {
+ assertArrayEquals(
+ SERIALIZED_DOUBLE,
+ serializeReturnValue(double.class, 3.141d));
+ }
+
+ @Test
+ public void deserializeMethodParameters_double() throws Exception {
+ expectDeserializedArgument(double.class, 3.141d, SERIALIZED_DOUBLE);
+ }
+
+ // 3.141f
+ private static final byte[] SERIALIZED_FLOAT =
+ {0x00, 0x46, (byte) 0x40, (byte) 0x49, (byte) 0x06, (byte) 0x25};
+
+ @Test
+ public void serializeReturnValue_float() throws Exception {
+ assertArrayEquals(SERIALIZED_FLOAT,
+ serializeReturnValue(float.class, 3.141f));
+ }
+
+ @Test
+ public void deserializeMethodParameters_float() throws Exception {
+ expectDeserializedArgument(float.class, 3.141f, SERIALIZED_FLOAT);
+ }
+
+ // "foo"
+ private static final byte[] SERIALIZED_ASCII_STRING = {0x00, 0x52, 0, 3, 0x66, 0x6f, 0x6f};
+
+ @Test
+ public void serializeReturnValue_asciiString() throws Exception {
+ assertArrayEquals(SERIALIZED_ASCII_STRING,
+ serializeReturnValue(String.class, "foo"));
+ }
+
+ @Test
+ public void deserializeMethodParameters_asciiString() throws Exception {
+ expectDeserializedArgument(String.class, "foo", SERIALIZED_ASCII_STRING);
+ }
+
+ // "\u1122"
+ private static final byte[] SERIALIZED_NON_ASCII_STRING =
+ {0x00, 0x52, 0, 3, (byte) 0xe1, (byte) 0x84, (byte) 0xa2};
+
+ @Test
+ public void serializeReturnValue_nonAsciiString_encodesAsUtf8() throws Exception {
+ assertArrayEquals(SERIALIZED_NON_ASCII_STRING,
+ serializeReturnValue(String.class, "\u1122"));
+ }
+
+ @Test
+ public void deserializeMethodParameters_decodesFromUtf8() throws Exception {
+ expectDeserializedArgument(String.class, "\u1122", SERIALIZED_NON_ASCII_STRING);
+ }
+
+ // ""
+ private static final byte[] SERIALIZED_EMPTY_STRING = {0x00, 0x52, 0, 0};
+
+ @Test
+ public void serializeReturnValue_emptyString() throws Exception {
+ assertArrayEquals(SERIALIZED_EMPTY_STRING, serializeReturnValue(String.class, ""));
+ }
+
+ @Test
+ public void deserializeMethodParameters_emptyString() throws Exception {
+ expectDeserializedArgument(String.class, "", SERIALIZED_EMPTY_STRING);
+ }
+
+ @Test
+ public void serializeReturnValue_nullString_encodesAsEmptyString() throws Exception {
+ assertArrayEquals(new byte[]{0x00, 0x52, 0, 0}, serializeReturnValue(String.class, null));
+ }
+
+ // Illegal - string length exceeding actual bytes
+ private static final byte[] SERIALIZED_INVALID_STRING =
+ {0x00, 0x52, 0, 3, 0x66};
+
+ @Test
+ public void deserializeMethodParameters_stringPayloadMissing_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(BufferUnderflowException.class,
+ () -> deserializeMethodParameters(args, argTypes,
+ ByteBuffer.wrap(SERIALIZED_INVALID_STRING)));
+ }
+
+ @Test
+ public void serializeAndDeserialize_handlesStringsUpTo64k() throws Exception {
+ char[] chars = new char[65535];
+ Arrays.fill(chars, 'a');
+ String original = new String(chars);
+ byte[] serialized = serializeReturnValue(String.class, original);
+
+ // 2 bytes for the R signature char, 2 bytes char string byte count, 2^16-1 bytes ASCII
+ // payload
+ assertEquals(2 + 2 + 65535, serialized.length);
+
+ // length is unsigned short
+ assertArrayEquals(new byte[]{0x00, 0x52, (byte) 0xff, (byte) 0xff},
+ Arrays.copyOfRange(serialized, 0, 4));
+
+ // length of string must be interpreted as unsigned short, returning original content
+ expectDeserializedArgument(String.class, original, serialized);
+ }
+
+ private static final byte[] SERIALIZED_VOID = {0x00, 0x56};
+
+ @Test
+ public void serializeReturnValue_void() throws Exception {
+ assertArrayEquals(SERIALIZED_VOID, serializeReturnValue(void.class, null));
+ }
+
+ @Test
+ public void deserializeMethodParameters_void_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(ViewMethodInvocationSerializationException.class,
+ () -> deserializeMethodParameters(args, argTypes,
+ ByteBuffer.wrap(SERIALIZED_VOID)));
+ }
+
+ // new byte[]{}
+ private static final byte[] SERIALIZED_EMPTY_BYTE_ARRAY = {0x00, 0x5b, 0x00, 0x42, 0, 0, 0, 0};
+
+ @Test
+ public void serializeReturnValue_emptyByteArray() throws Exception {
+ assertArrayEquals(SERIALIZED_EMPTY_BYTE_ARRAY,
+ serializeReturnValue(byte[].class, new byte[]{}));
+ }
+
+ @Test
+ public void deserializeMethodParameters_emptyByteArray() throws Exception {
+ expectDeserializedArgument(byte[].class, new byte[]{}, SERIALIZED_EMPTY_BYTE_ARRAY);
+ }
+
+ // new byte[]{0, 42}
+ private static final byte[] SERIALIZED_SIMPLE_BYTE_ARRAY =
+ {0x00, 0x5b, 0x00, 0x42, 0, 0, 0, 2, 0, 42};
+
+ @Test
+ public void serializeReturnValue_byteArray() throws Exception {
+ assertArrayEquals(SERIALIZED_SIMPLE_BYTE_ARRAY,
+ serializeReturnValue(byte[].class, new byte[]{0, 42}));
+ }
+
+ @Test
+ public void deserializeMethodParameters_byteArray() throws Exception {
+ expectDeserializedArgument(byte[].class, new byte[]{0, 42}, SERIALIZED_SIMPLE_BYTE_ARRAY);
+ }
+
+ @Test
+ public void serializeReturnValue_largeByteArray_encodesSizeCorrectly() throws Exception {
+ byte[] result = serializeReturnValue(byte[].class, new byte[0x012233]);
+ // 2 bytes for the each [Z signature char, 4 bytes int array length, 0x012233 bytes payload
+ assertEquals(2 + 2 + 4 + 74291, result.length);
+
+ assertArrayEquals(new byte[]{0x00, 0x5b, 0x00, 0x42, 0x00, 0x01, 0x22, 0x33},
+ Arrays.copyOfRange(result, 0, 8));
+ }
+
+ // Illegal - declared size exceeds remaining buffer length
+ private static final byte[] SERIALIZED_INVALID_BYTE_ARRAY =
+ {0x00, 0x5b, 0x00, 0x42, 0, 0, 0, 3, 0, 42};
+
+ @Test
+ public void deserializeMethodParameters_sizeExceedsBuffer_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(BufferUnderflowException.class,
+ () -> deserializeMethodParameters(args, argTypes,
+ ByteBuffer.wrap(SERIALIZED_INVALID_BYTE_ARRAY)));
+ }
+
+ // new int[]{}
+ private static final byte[] SERIALIZED_EMPTY_INT_ARRAY = {0x00, 0x5b, 0x00, 0x49, 0, 0, 0, 0};
+
+ @Test
+ public void serializeReturnValue_nonByteArrayType_throws() throws Exception {
+ assertThrows(ViewMethodInvocationSerializationException.class,
+ () -> serializeReturnValue(int[].class, 42));
+ }
+
+ @Test
+ public void deserializeMethodParameters_nonByteArrayType_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(ViewMethodInvocationSerializationException.class,
+ () -> deserializeMethodParameters(args, argTypes,
+ ByteBuffer.wrap(SERIALIZED_EMPTY_INT_ARRAY)));
+ }
+
+ // new byte[]{0, 42}
+ private static final byte[] SERIALIZED_MULTIPLE_PARAMETERS =
+ {0x00, 0x42, 42, 0x00, 0x5A, 1};
+
+ @Test
+ public void deserializeMethodParameters_multipleParameters() throws Exception {
+ expectDeserializedArguments(new Class[]{byte.class, boolean.class},
+ new Object[]{(byte) 42, true}, SERIALIZED_MULTIPLE_PARAMETERS);
+ }
+
+ // Illegal - type 'X'
+ private static final byte[] SERIALIZED_INVALID_UNKNOWN_TYPE = {0x00, 0x58};
+
+ @Test
+ public void deserializeMethodParameters_unknownType_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(ViewMethodInvocationSerializationException.class,
+ () -> deserializeMethodParameters(args, argTypes,
+ ByteBuffer.wrap(SERIALIZED_INVALID_UNKNOWN_TYPE)));
+ }
+
+ @Test
+ public void deserializeMethodParameters_noArgumentsEmptyPacket_isNoop() throws Exception {
+ Object[] args = new Object[0];
+ Class<?>[] argTypes = new Class<?>[0];
+ deserializeMethodParameters(args, argTypes, ByteBuffer.wrap(new byte[0]));
+ }
+
+ @Test
+ public void deserializeMethodParameters_withArgumentsEmptyPacket_throws() throws Exception {
+ Object[] args = new Object[1];
+ Class<?>[] argTypes = new Class<?>[1];
+ assertThrows(BufferUnderflowException.class,
+ () -> deserializeMethodParameters(args, argTypes, ByteBuffer.wrap(new byte[0])));
+ }
+
+ private static void expectDeserializedArgument(Class<?> expectedType, Object expectedValue,
+ byte[] argumentBuffer) throws Exception {
+ expectDeserializedArguments(new Class[]{expectedType}, new Object[]{expectedValue},
+ argumentBuffer);
+ }
+
+ private static void expectDeserializedArguments(Class<?>[] expectedTypes,
+ Object[] expectedValues, byte[] argumentBuffer) throws Exception {
+ final int argCount = expectedTypes.length;
+ assertEquals("test helper not used correctly", argCount, expectedValues.length);
+ Object[] actualArgs = new Object[argCount];
+ Class<?>[] actualArgTypes = new Class<?>[argCount];
+
+ ByteBuffer buffer = ByteBuffer.wrap(argumentBuffer);
+ deserializeMethodParameters(actualArgs, actualArgTypes, buffer);
+
+ for (int i = 0; i < argCount; i++) {
+ String context = "argument " + i;
+ assertEquals(context, expectedTypes[i], actualArgTypes[i]);
+ if (byte[].class.equals(expectedTypes[i])) {
+ assertArrayEquals((byte[]) expectedValues[i], (byte[]) actualArgs[i]);
+ } else {
+ assertEquals(expectedValues[i], actualArgs[i]);
+ }
+ }
+ }
+}
diff --git a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
index ddc05e09c395..7338c3a3ea8a 100644
--- a/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
+++ b/core/tests/coretests/src/android/graphics/drawable/AdaptiveIconDrawableTest.java
@@ -204,19 +204,6 @@ public class AdaptiveIconDrawableTest extends AndroidTestCase {
assertEquals(100, Color.alpha(bitmap.getPixel(50, 50)));
}
- @Test
- public void testSetBounds() throws Exception {
- mIconDrawable = new AdaptiveIconDrawable(mBackgroundDrawable, mForegroundDrawable);
- mIconDrawable.setBounds(0, 0, 100, 100);
-
- assertEquals(new Rect(-25, -25, 125, 125), mBackgroundDrawable.getBounds());
- assertEquals(new Rect(-25, -25, 125, 125), mForegroundDrawable.getBounds());
-
- mIconDrawable.setBounds(10, 10, 110, 110);
- assertEquals(new Rect(-15, -15, 135, 135), mBackgroundDrawable.getBounds());
- assertEquals(new Rect(-15, -15, 135, 135), mForegroundDrawable.getBounds());
- }
-
//
// Utils
//
diff --git a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
index 3ecc7ff17114..bf65af32e0ac 100644
--- a/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
+++ b/core/tests/coretests/src/android/hardware/input/InputDeviceLightsManagerTest.java
@@ -131,7 +131,7 @@ public class InputDeviceLightsManagerTest {
new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
Light.LIGHT_CAPABILITY_BRIGHTNESS),
new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
0 /* capabilities */)
};
@@ -150,13 +150,13 @@ public class InputDeviceLightsManagerTest {
Light[] mockedLights = {
new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB),
+ Light.LIGHT_CAPABILITY_COLOR_RGB),
new Light(4 /* id */, "Light4", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB)
+ Light.LIGHT_CAPABILITY_COLOR_RGB)
};
mockLights(mockedLights);
@@ -204,7 +204,7 @@ public class InputDeviceLightsManagerTest {
new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_PLAYER_ID,
0 /* capabilities */),
new Light(2 /* id */, "Light2", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS),
+ Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS),
new Light(3 /* id */, "Light3", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
Light.LIGHT_CAPABILITY_BRIGHTNESS)
};
@@ -239,9 +239,9 @@ public class InputDeviceLightsManagerTest {
@Test
public void testLightCapabilities() throws Exception {
Light light = new Light(1 /* id */, "Light1", 0 /* ordinal */, Light.LIGHT_TYPE_INPUT,
- Light.LIGHT_CAPABILITY_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS);
+ Light.LIGHT_CAPABILITY_COLOR_RGB | Light.LIGHT_CAPABILITY_BRIGHTNESS);
assertThat(light.getType()).isEqualTo(Light.LIGHT_TYPE_INPUT);
- assertThat(light.getCapabilities()).isEqualTo(Light.LIGHT_CAPABILITY_RGB
+ assertThat(light.getCapabilities()).isEqualTo(Light.LIGHT_CAPABILITY_COLOR_RGB
| Light.LIGHT_CAPABILITY_BRIGHTNESS);
assertTrue(light.hasBrightnessControl());
assertTrue(light.hasRgbControl());
diff --git a/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java b/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java
new file mode 100644
index 000000000000..ebd78b465b75
--- /dev/null
+++ b/core/tests/coretests/src/android/internal/os/anr/AnrLatencyTrackerTests.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.anr;
+
+import static com.google.common.truth.Truth.assertThat;
+
+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 org.junit.Before;
+import org.junit.Test;
+
+/**
+ * Tests for {@link AnrLatencyTracekr.java}.
+ *
+ */
+public class AnrLatencyTrackerTests {
+
+ private AnrLatencyTracker mLatencyTracker;
+
+ @Before
+ public void setup() {
+ mLatencyTracker = spy(new AnrLatencyTracker(0, 50L));
+ when(mLatencyTracker.getUptimeMillis())
+ .thenReturn(55L)
+ .thenReturn(60L)
+ .thenReturn(70L)
+ .thenReturn(80L)
+ .thenReturn(88L)
+ .thenReturn(99L)
+ .thenReturn(101L)
+ .thenReturn(105L)
+ .thenReturn(108L)
+ .thenReturn(110L)
+ .thenReturn(117L)
+ .thenReturn(121L)
+ .thenReturn(129L)
+ .thenReturn(135L)
+ .thenReturn(150L)
+ .thenReturn(155L)
+ .thenReturn(157L)
+ .thenReturn(158L)
+ .thenReturn(165L)
+ .thenReturn(175L)
+ .thenReturn(188L);
+ }
+
+ @Test
+ public void testNormalScenario() {
+
+ mLatencyTracker.appNotRespondingStarted();
+ mLatencyTracker.waitingOnAnrRecordLockStarted();
+ mLatencyTracker.waitingOnAnrRecordLockEnded();
+ mLatencyTracker.anrRecordPlacingOnQueueWithSize(3);
+ mLatencyTracker.appNotRespondingEnded();
+
+ mLatencyTracker.anrProcessingStarted();
+ mLatencyTracker.updateCpuStatsNowCalled();
+ mLatencyTracker.updateCpuStatsNowReturned();
+ mLatencyTracker.currentPsiStateCalled();
+ mLatencyTracker.currentPsiStateReturned();
+ mLatencyTracker.processCpuTrackerMethodsCalled();
+ mLatencyTracker.processCpuTrackerMethodsReturned();
+ mLatencyTracker.criticalEventLogStarted();
+ mLatencyTracker.criticalEventLogEnded();
+
+ mLatencyTracker.waitingOnGlobalLockStarted();
+ mLatencyTracker.waitingOnGlobalLockEnded();
+ mLatencyTracker.waitingOnPidLockStarted();
+ mLatencyTracker.waitingOnPidLockEnded();
+ mLatencyTracker.waitingOnAMSLockStarted();
+ mLatencyTracker.waitingOnAMSLockEnded();
+ mLatencyTracker.waitingOnProcLockStarted();
+ mLatencyTracker.waitingOnProcLockEnded();
+
+ mLatencyTracker.dumpStackTracesStarted();
+ mLatencyTracker.dumpingFirstPidsStarted();
+ mLatencyTracker.dumpingPidStarted(1);
+ mLatencyTracker.dumpingPidEnded();
+ mLatencyTracker.dumpingFirstPidsEnded();
+ mLatencyTracker.dumpingNativePidsStarted();
+ mLatencyTracker.dumpingPidStarted(3);
+ mLatencyTracker.dumpingPidEnded();
+ mLatencyTracker.dumpingNativePidsEnded();
+ mLatencyTracker.dumpingExtraPidsStarted();
+ mLatencyTracker.dumpingPidStarted(10);
+ mLatencyTracker.dumpingPidEnded();
+ mLatencyTracker.dumpingPidStarted(11);
+ mLatencyTracker.dumpingPidEnded();
+ mLatencyTracker.dumpingExtraPidsEnded();
+ mLatencyTracker.dumpStackTracesEnded();
+ mLatencyTracker.anrProcessingEnded();
+
+ mLatencyTracker.close();
+
+ assertThat(mLatencyTracker.dumpAsCommaSeparatedArrayWithHeader())
+ .isEqualTo("DurationsV1: 50,5,25,8,100,2,3,7,8,15,2,7,13,10,3,4\n\n");
+ verify(mLatencyTracker, times(1)).pushAtom();
+ }
+
+ @Test
+ public void testCloseIdempotency() {
+
+ mLatencyTracker.appNotRespondingStarted();
+ mLatencyTracker.waitingOnAnrRecordLockStarted();
+ mLatencyTracker.waitingOnAnrRecordLockEnded();
+ mLatencyTracker.anrRecordPlacingOnQueueWithSize(3);
+ mLatencyTracker.appNotRespondingEnded();
+
+ mLatencyTracker.anrProcessingStarted();
+ mLatencyTracker.updateCpuStatsNowCalled();
+ mLatencyTracker.updateCpuStatsNowReturned();
+ mLatencyTracker.currentPsiStateCalled();
+ mLatencyTracker.currentPsiStateReturned();
+ mLatencyTracker.processCpuTrackerMethodsCalled();
+ mLatencyTracker.processCpuTrackerMethodsReturned();
+ mLatencyTracker.criticalEventLogStarted();
+ mLatencyTracker.criticalEventLogEnded();
+
+ mLatencyTracker.waitingOnGlobalLockStarted();
+ mLatencyTracker.waitingOnGlobalLockEnded();
+ mLatencyTracker.waitingOnPidLockStarted();
+ mLatencyTracker.waitingOnPidLockEnded();
+ mLatencyTracker.waitingOnAMSLockStarted();
+ mLatencyTracker.waitingOnAMSLockEnded();
+ mLatencyTracker.waitingOnProcLockStarted();
+ mLatencyTracker.waitingOnProcLockEnded();
+
+ mLatencyTracker.dumpStackTracesStarted();
+ mLatencyTracker.dumpingFirstPidsStarted();
+ mLatencyTracker.dumpingPidStarted(1);
+ mLatencyTracker.dumpingPidEnded();
+ mLatencyTracker.dumpingFirstPidsEnded();
+ mLatencyTracker.dumpingNativePidsStarted();
+ mLatencyTracker.dumpingPidStarted(3);
+ mLatencyTracker.dumpingPidEnded();
+ mLatencyTracker.dumpingNativePidsEnded();
+ mLatencyTracker.dumpingExtraPidsStarted();
+ mLatencyTracker.dumpingPidStarted(10);
+ mLatencyTracker.dumpingPidEnded();
+ mLatencyTracker.dumpingPidStarted(11);
+ mLatencyTracker.dumpingPidEnded();
+ mLatencyTracker.dumpingExtraPidsEnded();
+ mLatencyTracker.dumpStackTracesEnded();
+ mLatencyTracker.anrProcessingEnded();
+
+ mLatencyTracker.close();
+ mLatencyTracker.close();
+
+ verify(mLatencyTracker, times(1)).pushAtom();
+ }
+
+ @Test
+ public void testSkipInProcessErrorStateRecordAppNotResponding() {
+
+ mLatencyTracker.appNotRespondingStarted();
+ mLatencyTracker.waitingOnAnrRecordLockStarted();
+ mLatencyTracker.waitingOnAnrRecordLockEnded();
+ mLatencyTracker.anrRecordPlacingOnQueueWithSize(3);
+ mLatencyTracker.appNotRespondingEnded();
+
+ mLatencyTracker.anrProcessingStarted();
+ mLatencyTracker.updateCpuStatsNowCalled();
+ mLatencyTracker.updateCpuStatsNowReturned();
+ mLatencyTracker.currentPsiStateCalled();
+ mLatencyTracker.currentPsiStateReturned();
+ mLatencyTracker.processCpuTrackerMethodsCalled();
+ mLatencyTracker.processCpuTrackerMethodsReturned();
+ mLatencyTracker.criticalEventLogStarted();
+ mLatencyTracker.criticalEventLogEnded();
+
+ mLatencyTracker.waitingOnGlobalLockStarted();
+ mLatencyTracker.waitingOnGlobalLockEnded();
+ mLatencyTracker.waitingOnPidLockStarted();
+ mLatencyTracker.waitingOnPidLockEnded();
+ mLatencyTracker.waitingOnAMSLockStarted();
+ mLatencyTracker.waitingOnAMSLockEnded();
+ mLatencyTracker.waitingOnProcLockStarted();
+ mLatencyTracker.waitingOnProcLockEnded();
+
+ mLatencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
+
+ mLatencyTracker.anrProcessingEnded();
+
+ mLatencyTracker.close();
+
+ verify(mLatencyTracker, times(0)).pushAtom();
+ }
+
+ @Test
+ public void testSkipInDumpTraces() {
+
+ mLatencyTracker.appNotRespondingStarted();
+ mLatencyTracker.waitingOnAnrRecordLockStarted();
+ mLatencyTracker.waitingOnAnrRecordLockEnded();
+ mLatencyTracker.anrRecordPlacingOnQueueWithSize(3);
+ mLatencyTracker.appNotRespondingEnded();
+
+ mLatencyTracker.anrProcessingStarted();
+ mLatencyTracker.updateCpuStatsNowCalled();
+ mLatencyTracker.updateCpuStatsNowReturned();
+ mLatencyTracker.currentPsiStateCalled();
+ mLatencyTracker.currentPsiStateReturned();
+ mLatencyTracker.processCpuTrackerMethodsCalled();
+ mLatencyTracker.processCpuTrackerMethodsReturned();
+ mLatencyTracker.criticalEventLogStarted();
+ mLatencyTracker.criticalEventLogEnded();
+
+ mLatencyTracker.waitingOnGlobalLockStarted();
+ mLatencyTracker.waitingOnGlobalLockEnded();
+ mLatencyTracker.waitingOnPidLockStarted();
+ mLatencyTracker.waitingOnPidLockEnded();
+ mLatencyTracker.waitingOnAMSLockStarted();
+ mLatencyTracker.waitingOnAMSLockEnded();
+ mLatencyTracker.waitingOnProcLockStarted();
+ mLatencyTracker.waitingOnProcLockEnded();
+
+ mLatencyTracker.dumpStackTracesStarted();
+ mLatencyTracker.anrSkippedDumpStackTraces();
+ mLatencyTracker.dumpStackTracesEnded();
+
+ mLatencyTracker.anrProcessingEnded();
+
+ mLatencyTracker.close();
+
+ verify(mLatencyTracker, times(0)).pushAtom();
+ }
+
+}
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 32c3a268153c..91fbe0034133 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -532,6 +532,56 @@ public class FileUtilsTest {
}
@Test
+ public void testParseSize() {
+ assertEquals(0L, FileUtils.parseSize("0MB"));
+ assertEquals(1_024L, FileUtils.parseSize("1024b"));
+ assertEquals(-1L, FileUtils.parseSize(" -1 b "));
+ assertEquals(0L, FileUtils.parseSize(" -0 gib "));
+ assertEquals(1_000L, FileUtils.parseSize("1K"));
+ assertEquals(1_000L, FileUtils.parseSize("1KB"));
+ assertEquals(10_000L, FileUtils.parseSize("10KB"));
+ assertEquals(100_000L, FileUtils.parseSize("100KB"));
+ assertEquals(1_000_000L, FileUtils.parseSize("1000KB"));
+ assertEquals(1_024_000L, FileUtils.parseSize("1000KiB"));
+ assertEquals(70_000_000L, FileUtils.parseSize("070M"));
+ assertEquals(70_000_000L, FileUtils.parseSize("070MB"));
+ assertEquals(73_400_320L, FileUtils.parseSize("70MiB"));
+ assertEquals(700_000_000L, FileUtils.parseSize("700000KB"));
+ assertEquals(200_000_000L, FileUtils.parseSize("+200MB"));
+ assertEquals(1_000_000_000L, FileUtils.parseSize("1000MB"));
+ assertEquals(1_000_000_000L, FileUtils.parseSize("+1000 mb"));
+ assertEquals(644_245_094_400L, FileUtils.parseSize("600GiB"));
+ assertEquals(999_000_000_000L, FileUtils.parseSize("999GB"));
+ assertEquals(999_000_000_000L, FileUtils.parseSize("999 gB"));
+ assertEquals(9_999_000_000_000L, FileUtils.parseSize("9999GB"));
+ assertEquals(9_000_000_000_000L, FileUtils.parseSize(" 9000 GB "));
+ assertEquals(1_234_000_000_000L, FileUtils.parseSize(" 1234 GB "));
+ assertEquals(1_234_567_890_000L, FileUtils.parseSize(" 1234567890 KB "));
+ }
+
+ @Test
+ public void testParseSize_invalidArguments() {
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize(null));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("null"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize(""));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize(" "));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("KB"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("123 dd"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("Invalid"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize(" ABC890 KB "));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("-=+90 KB "));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("123"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("--123"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("-KB"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("++123"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("+"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("+ 1 +"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("+--+ 1 +"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize("1GB+"));
+ assertEquals(Long.MIN_VALUE, FileUtils.parseSize(" + 1234567890 KB "));
+ }
+
+ @Test
public void testTranslateMode() throws Exception {
assertTranslate("r", O_RDONLY, MODE_READ_ONLY);
diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java
index ae4edb97aab0..52846dfbb14b 100644
--- a/core/tests/coretests/src/android/os/ProcessTest.java
+++ b/core/tests/coretests/src/android/os/ProcessTest.java
@@ -72,4 +72,7 @@ public class ProcessTest extends TestCase {
assertEquals(-1, Process.getThreadGroupLeader(BAD_PID));
}
+ public void testGetAdvertisedMem() {
+ assertTrue(Process.getTotalMemory() <= Process.getAdvertisedMem());
+ }
}
diff --git a/core/tests/coretests/src/android/service/notification/ConditionTest.java b/core/tests/coretests/src/android/service/notification/ConditionTest.java
new file mode 100644
index 000000000000..42629ba41287
--- /dev/null
+++ b/core/tests/coretests/src/android/service/notification/ConditionTest.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.notification;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.fail;
+
+import android.net.Uri;
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import com.google.common.base.Strings;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.reflect.Field;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ConditionTest {
+ private static final String CLASS = "android.service.notification.Condition";
+
+ @Test
+ public void testLongFields_inConstructors() {
+ String longString = Strings.repeat("A", 65536);
+ Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530));
+
+ // Confirm strings are truncated via short constructor
+ Condition cond1 = new Condition(longUri, longString, Condition.STATE_TRUE);
+
+ assertEquals(Condition.MAX_STRING_LENGTH, cond1.id.toString().length());
+ assertEquals(Condition.MAX_STRING_LENGTH, cond1.summary.length());
+
+ // Confirm strings are truncated via long constructor
+ Condition cond2 = new Condition(longUri, longString, longString, longString,
+ -1, Condition.STATE_TRUE, Condition.FLAG_RELEVANT_ALWAYS);
+
+ assertEquals(Condition.MAX_STRING_LENGTH, cond2.id.toString().length());
+ assertEquals(Condition.MAX_STRING_LENGTH, cond2.summary.length());
+ assertEquals(Condition.MAX_STRING_LENGTH, cond2.line1.length());
+ assertEquals(Condition.MAX_STRING_LENGTH, cond2.line2.length());
+ }
+
+ @Test
+ public void testLongFields_viaParcel() {
+ // Set fields via reflection to force them to be long, then parcel and unparcel to make sure
+ // it gets truncated upon unparcelling.
+ Condition cond = new Condition(Uri.parse("uri://placeholder"), "placeholder",
+ Condition.STATE_TRUE);
+
+ try {
+ String longString = Strings.repeat("A", 65536);
+ Uri longUri = Uri.parse("uri://" + Strings.repeat("A", 65530));
+ Field id = Class.forName(CLASS).getDeclaredField("id");
+ id.setAccessible(true);
+ id.set(cond, longUri);
+ Field summary = Class.forName(CLASS).getDeclaredField("summary");
+ summary.setAccessible(true);
+ summary.set(cond, longString);
+ Field line1 = Class.forName(CLASS).getDeclaredField("line1");
+ line1.setAccessible(true);
+ line1.set(cond, longString);
+ Field line2 = Class.forName(CLASS).getDeclaredField("line2");
+ line2.setAccessible(true);
+ line2.set(cond, longString);
+ } catch (NoSuchFieldException e) {
+ fail(e.toString());
+ } catch (ClassNotFoundException e) {
+ fail(e.toString());
+ } catch (IllegalAccessException e) {
+ fail(e.toString());
+ }
+
+ Parcel parcel = Parcel.obtain();
+ cond.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+
+ Condition fromParcel = new Condition(parcel);
+ assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.id.toString().length());
+ assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.summary.length());
+ assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line1.length());
+ assertEquals(Condition.MAX_STRING_LENGTH, fromParcel.line2.length());
+ }
+}
diff --git a/core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java b/core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java
index 32fdb5e850bd..787a4055b49d 100644
--- a/core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java
+++ b/core/tests/coretests/src/android/text/LayoutGetRangeForRectTest.java
@@ -90,7 +90,8 @@ public class LayoutGetRangeForRectTest {
mLineCenters = new float[mLayout.getLineCount()];
for (int i = 0; i < mLayout.getLineCount(); ++i) {
- mLineCenters[i] = (mLayout.getLineTop(i) + mLayout.getLineBottomWithoutSpacing(i)) / 2f;
+ mLineCenters[i] = (mLayout.getLineTop(i)
+ + mLayout.getLineBottom(i, /* includeLineSpacing= */ false)) / 2f;
}
mGraphemeClusterSegmentIterator =
diff --git a/core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java b/core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java
new file mode 100644
index 000000000000..4007c43e5944
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/BaseInputConnectionTest.java
@@ -0,0 +1,759 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.inputmethod;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+import android.text.Editable;
+import android.text.InputType;
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.SuggestionSpan;
+import android.view.View;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BaseInputConnectionTest {
+ private static final int[] CURSOR_CAPS_MODES =
+ new int[] {
+ InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS,
+ InputType.TYPE_TEXT_FLAG_CAP_WORDS,
+ InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
+ };
+
+ private BaseInputConnection mBaseInputConnection;
+ private Editable mEditable;
+ private View mMockView;
+
+ @Before
+ public void setUp() throws Exception {
+ mMockView = new View(InstrumentationRegistry.getInstrumentation().getContext());
+ mBaseInputConnection = new BaseInputConnection(mMockView, /*fullEditor=*/ true);
+ mEditable = mBaseInputConnection.getEditable();
+ verifyContent("", 0, 0, -1, -1);
+ }
+
+ @Test
+ public void testCommitText_toEditorWithoutSelectionAndComposing() {
+ // before commit: "|"
+ // after commit: "text1|"
+ assertThat(mBaseInputConnection.commitText("text1", 1)).isTrue();
+ verifyContent("text1", 5, 5, -1, -1);
+
+ // before commit: "text1|"
+ // after commit: "text1text2|"
+ assertThat(mBaseInputConnection.commitText("text2", "text1".length())).isTrue();
+ verifyContent("text1text2", 10, 10, -1, -1);
+
+ // before commit: "text1text2|"
+ // after commit: "text1text2text3|"
+ assertThat(mBaseInputConnection.commitText("text3", 100)).isTrue();
+ verifyContent("text1text2text3", 15, 15, -1, -1);
+
+ // before commit: "text1text2text3|"
+ // after commit: "text1text2text3text4|"
+ // BUG(b/21476564): this behavior is inconsistent with API description.
+ assertThat(mBaseInputConnection.commitText("text4", 0)).isTrue();
+ verifyContent("text1text2text3text4", 20, 20, -1, -1);
+
+ // before commit: "text1text2text3text4|"
+ // after commit: "text1text2text3text|4text5"
+ assertThat(mBaseInputConnection.commitText("text5", -1)).isTrue();
+ verifyContent("text1text2text3text4text5", 19, 19, -1, -1);
+
+ // before commit: "text1text2text3text|4text5"
+ // after commit: "text1text2text3te|xttext64text5"
+ assertThat(mBaseInputConnection.commitText("text6", -2)).isTrue();
+ verifyContent("text1text2text3texttext64text5", 17, 17, -1, -1);
+
+ // before commit: "text1text2text3te|xttext64text5"
+ // after commit: "|text1text2text3tetext7xttext64text5"
+ assertThat(mBaseInputConnection.commitText("text7", -100)).isTrue();
+ verifyContent("text1text2text3tetext7xttext64text5", 0, 0, -1, -1);
+ }
+
+ @Test
+ public void testCommitText_toEditorWithSelection() {
+ // before commit: "123|456|789"
+ // before commit: "123text|789"
+ prepareContent("123456789", 3, 6, -1, -1);
+ assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+ verifyContent("123text789", 7, 7, -1, -1);
+
+ // before commit: "|123|"
+ // before commit: "|text"
+ prepareContent("123", 0, 3, -1, -1);
+ assertThat(mBaseInputConnection.commitText("text", 0)).isTrue();
+ verifyContent("text", 0, 0, -1, -1);
+ }
+
+ @Test
+ public void testCommitText_toEditorWithComposing() {
+ // before commit: "123456|789"
+ // ---
+ // before commit: "123text|789"
+ prepareContent("123456789", 6, 6, 3, 6);
+ assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+ verifyContent("123text789", 7, 7, -1, -1);
+
+ // before commit: "123456789|"
+ // ---
+ // before commit: "123text|789"
+ prepareContent("123456789", 6, 6, 3, 6);
+ assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+ verifyContent("123text789", 7, 7, -1, -1);
+
+ // before commit: "|123456789|"
+ // ---
+ // before commit: "123text|789"
+ prepareContent("123456789", 0, 9, 3, 6);
+ assertThat(mBaseInputConnection.commitText("text", 1)).isTrue();
+ verifyContent("123text789", 7, 7, -1, -1);
+ }
+
+ @Test
+ public void deleteSurroundingText_fromEditorWithoutSelectionAndComposing() {
+ // before delete: "123456789|"
+ // after delete: "123456|"
+ prepareContent("123456789", 9, 9, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(3, 0)).isTrue();
+ verifyContent("123456", 6, 6, -1, -1);
+
+ // before delete: "123456|"
+ // after delete: "|"
+ assertThat(mBaseInputConnection.deleteSurroundingText(100, 0)).isTrue();
+ verifyContent("", 0, 0, -1, -1);
+
+ // before commit: "|123456789"
+ // after delete: "|456789"
+ prepareContent("123456789", 0, 0, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(0, 3)).isTrue();
+ verifyContent("456789", 0, 0, -1, -1);
+
+ // before delete: "|123456789"
+ // after delete: "|"
+ assertThat(mBaseInputConnection.deleteSurroundingText(0, 100)).isTrue();
+ verifyContent("", 0, 0, -1, -1);
+
+ // before delete: "123|456789"
+ // after delete: "1|789"
+ prepareContent("123456789", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(2, 3)).isTrue();
+ verifyContent("1789", 1, 1, -1, -1);
+ }
+
+ @Test
+ public void deleteSurroundingText_fromEditorSelectionOrComposing() {
+ // before delete: "123|456|789"
+ // before delete: "12|456|9"
+ prepareContent("123456789", 3, 6, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(1, 2)).isTrue();
+ verifyContent("124569", 2, 5, -1, -1);
+
+ // before delete: "12|456|9"
+ // before delete: "|456|"
+ assertThat(mBaseInputConnection.deleteSurroundingText(100, 100)).isTrue();
+ verifyContent("456", 0, 3, -1, -1);
+
+ // before commit: "123456|789"
+ // ---
+ // before commit: "1[456]|89"
+ prepareContent("123456789", 6, 6, 3, 6);
+ assertThat(mBaseInputConnection.deleteSurroundingText(2, 1)).isTrue();
+ verifyContent("145689", 4, 4, 1, 4);
+
+ // before commit: "1234|56789"
+ // - --
+ // before commit: "124|56|89"
+ // - --
+ prepareContent("123456789", 4, 4, 3, 6);
+ assertThat(mBaseInputConnection.deleteSurroundingText(1, 1)).isTrue();
+ verifyContent("1245689", 3, 3, 2, 5);
+ }
+
+ @Test
+ public void deleteSurroundingText_negativeLength_willBeIgnored() {
+ // before delete: "123|45678"
+ // after delete: "123|45678"
+ prepareContent("123456789", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(-1, -1)).isTrue();
+ verifyContent("123456789", 3, 3, -1, -1);
+
+ // before delete: "123|45678"
+ // after delete: "123|5678"
+ assertThat(mBaseInputConnection.deleteSurroundingText(-1, 1)).isTrue();
+ verifyContent("12356789", 3, 3, -1, -1);
+
+ // before delete: "123|45678"
+ // after delete: "12|45678"
+ prepareContent("123456789", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.deleteSurroundingText(1, -1)).isTrue();
+ verifyContent("12456789", 2, 2, -1, -1);
+ }
+
+ @Test
+ public void testFinishComposingText() {
+ // before finish composing: "123456|789"
+ // ---
+ // before finish composing: "123456|789"
+ prepareContent("123456789", 6, 6, 3, 6);
+ assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+ verifyContent("123456789", 6, 6, -1, -1);
+
+ // before finish composing: "123456789|"
+ // ---
+ // before finish composing: "123456789|"
+ prepareContent("123456789", 9, 9, 3, 6);
+ assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+ verifyContent("123456789", 9, 9, -1, -1);
+
+ // before finish composing: "|123456789|"
+ // ---
+ // before finish composing: "|123456789|"
+ prepareContent("123456789", 0, 9, 3, 6);
+ assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+ verifyContent("123456789", 0, 9, -1, -1);
+
+ // before finish composing: "1234|5|6789|"
+ // ---- - ----
+ // before finish composing: "1234|5|6789"
+ prepareContent("123456789", 4, 5, 0, 9);
+ assertThat(mBaseInputConnection.finishComposingText()).isTrue();
+ verifyContent("123456789", 4, 5, -1, -1);
+ }
+
+ @Test
+ public void testGetCursorCapsMode() {
+ // "|"
+ prepareContent("", 0, 0, -1, -1);
+ verifyCursorCapsModeWithMode("", 0);
+
+ // Hello|
+ prepareContent("Hello", 5, 5, -1, -1);
+ verifyCursorCapsModeWithMode("Hello", 5);
+
+ // Hello. |
+ prepareContent("Hello. ", 7, 7, -1, -1);
+ verifyCursorCapsModeWithMode("Hello. ", 7);
+
+ // Hello. |Hi|
+ prepareContent("Hello. Hi", 7, 9, -1, -1);
+ verifyCursorCapsModeWithMode("Hello. Hi", 7);
+
+ // Hello. |
+ // -----
+ prepareContent("Hello. ", 7, 7, 0, 5);
+ verifyCursorCapsModeWithMode("Hello. ", 7);
+ }
+
+ private void verifyCursorCapsModeWithMode(CharSequence text, int off) {
+ for (int reqMode : CURSOR_CAPS_MODES) {
+ assertThat(mBaseInputConnection.getCursorCapsMode(reqMode))
+ .isEqualTo(TextUtils.getCapsMode(text, off, reqMode));
+ }
+ }
+
+ @Test
+ public void testSetComposingText_toEditorWithoutSelectionAndComposing() {
+ // before set composing text: "|"
+ // after set composing text: "abc|"
+ // ---
+ assertThat(mBaseInputConnection.setComposingText("abc", 1)).isTrue();
+ verifyContent("abc", 3, 3, 0, 3);
+
+ // before set composing text: "abc|"
+ // after set composing text: "abcdef|"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("def", 100)).isTrue();
+ verifyContent("abcdef", 6, 6, 3, 6);
+
+ // before set composing text: "abc|"
+ // after set composing text: "abcdef|"
+ // ---
+ // BUG(b/21476564): this behavior is inconsistent with API description.
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("def", 0)).isTrue();
+ verifyContent("abcdef", 6, 6, 3, 6);
+
+ // before set composing text: "abc|"
+ // after set composing text: "ab|cdef"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("def", -1)).isTrue();
+ verifyContent("abcdef", 2, 2, 3, 6);
+
+ // before set composing text: "abc|"
+ // after set composing text: "|abcdef"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("def", -100)).isTrue();
+ verifyContent("abcdef", 0, 0, 3, 6);
+ }
+
+ @Test
+ public void testSetComposingText_toEditorWithComposing() {
+ // before set composing text: "abc|"
+ // ---
+ // after set composing text: "def|"
+ // ---
+ prepareContent("abc", 3, 3, 0, 3);
+ assertThat(mBaseInputConnection.setComposingText("def", 1)).isTrue();
+ verifyContent("def", 3, 3, 0, 3);
+
+ // before set composing text: "abc|"
+ // ---
+ // after set composing text: "hijkl|"
+ // -----
+ assertThat(mBaseInputConnection.setComposingText("hijkl", 1)).isTrue();
+ verifyContent("hijkl", 5, 5, 0, 5);
+
+ // before set composing text: "hijkl|"
+ // -----
+ // after set composing text: "|mn"
+ // --
+ assertThat(mBaseInputConnection.setComposingText("mn", 0)).isTrue();
+ verifyContent("mn", 0, 0, 0, 2);
+
+ // before set composing text: "|mn"
+ // --
+ // after set composing text: "|opq"
+ // ---
+ assertThat(mBaseInputConnection.setComposingText("opq", -1)).isTrue();
+ verifyContent("opq", 0, 0, 0, 3);
+ }
+
+ @Test
+ public void testSetComposingText_toEditorWithSelection() {
+ // before set composing text: "|abc|"
+ // after set composing text: "defgh|"
+ // -----
+ prepareContent("abc", 0, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("defgh", 1)).isTrue();
+ verifyContent("defgh", 5, 5, 0, 5);
+
+ // before set composing text: "a|bcdef|g"
+ // after set composing text: "a|123g"
+ // ---
+ prepareContent("abcdefg", 1, 6, -1, -1);
+ assertThat(mBaseInputConnection.setComposingText("123", 0)).isTrue();
+ verifyContent("a123g", 1, 1, 1, 4);
+
+ // before set composing text: "a|bcdef|g"
+ // ---
+ // after set composing text: "ab123456|fg"
+ // ------
+ prepareContent("abcdefg", 1, 6, 2, 5);
+ assertThat(mBaseInputConnection.setComposingText("123456", 1)).isTrue();
+ verifyContent("ab123456fg", 8, 8, 2, 8);
+
+ // before set composing text: "a|bc"
+ // ----
+ // after set composing text: "|12345"
+ // -----
+ prepareContent("abc", 1, 1, 0, 3);
+ assertThat(mBaseInputConnection.setComposingText("12345", -1)).isTrue();
+ verifyContent("12345", 0, 0, 0, 5);
+ }
+
+ @Test
+ public void testSetComposingRegion_toEditorWithoutSelectionAndComposing() {
+ // before set composing region: "|"
+ // after set composing region: "|"
+ assertThat(mBaseInputConnection.setComposingRegion(1, 1)).isTrue();
+ verifyContent("", 0, 0, -1, -1);
+
+ // before set composing region: "abc|"
+ // after set composing region: "abc|"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(0, 3)).isTrue();
+ verifyContent("abc", 3, 3, 0, 3);
+
+ // before set composing region: "abc|"
+ // after set composing region: "abc|"
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(1, 1)).isTrue();
+ verifyContent("abc", 3, 3, -1, -1);
+
+ // before set composing region: "abc|"
+ // after set composing region: "abc|"
+ // -
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(1, 2)).isTrue();
+ verifyContent("abc", 3, 3, 1, 2);
+
+ // before set composing region: "abc|"
+ // after set composing region: "abc|"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(3, 0)).isTrue();
+ verifyContent("abc", 3, 3, 0, 3);
+
+ // before set composing region: "abc|"
+ // after set composing region: "abc|"
+ // ---
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(-100, 100)).isTrue();
+ verifyContent("abc", 3, 3, 0, 3);
+ }
+
+ @Test
+ public void testSetComposingRegion_toEditorWithSelection() {
+ // before set composing region: "|abc|"
+ // after set composing region: "|abc|"
+ // ---
+ prepareContent("abc", 0, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(0, 3)).isTrue();
+ verifyContent("abc", 0, 3, 0, 3);
+
+ // before set composing region: "ab|cd|ef"
+ // after set composing region: "ab|cd|ef"
+ // - -- -
+ prepareContent("abcdef", 2, 4, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(1, 5)).isTrue();
+ verifyContent("abcdef", 2, 4, 1, 5);
+ }
+
+ @Test
+ public void testSetComposingRegion_toEditorWithComposing() {
+ // before set composing region: "abc|"
+ // ---
+ // after set composing region: "abc|"
+ // -
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setComposingRegion(1, 2)).isTrue();
+ verifyContent("abc", 3, 3, 1, 2);
+
+ // before set composing region: "ab|cd|ef"
+ // --
+ // after set composing region: "ab|cd|ef"
+ // - -- -
+ prepareContent("abcdef", 2, 4, 2, 4);
+ assertThat(mBaseInputConnection.setComposingRegion(1, 5)).isTrue();
+ verifyContent("abcdef", 2, 4, 1, 5);
+ }
+
+ @Test
+ public void testSetSelection_toEditorWithoutComposing() {
+ // before set selection: "|"
+ // after set selection: "|"
+ assertThat(mBaseInputConnection.setSelection(0, 0)).isTrue();
+ assertThat(mBaseInputConnection.setSelection(1, 1)).isTrue();
+ assertThat(mBaseInputConnection.setSelection(-1, -1)).isTrue();
+
+ // before set selection: "abc|"
+ // after set selection: "a|b|c"
+ prepareContent("abc", 3, 3, -1, -1);
+ assertThat(mBaseInputConnection.setSelection(1, 1)).isTrue();
+ verifyContent("abc", 1, 1, -1, -1);
+
+ // before set selection: "abcdef|"
+ // after set selection: "ab|cd|ef"
+ prepareContent("abcdef", 6, 6, -1, -1);
+ assertThat(mBaseInputConnection.setSelection(4, 2)).isTrue();
+ verifyContent("abcdef", 4, 2, -1, -1);
+
+ // before set selection: "|abc"
+ // after set selection: "|abc"
+ prepareContent("abc", 0, 0, -1, -1);
+ assertThat(mBaseInputConnection.setSelection(0, 100)).isTrue();
+ verifyContent("abc", 0, 0, -1, -1);
+
+ // before set selection: "|abc"
+ // after set selection: "ab|c"
+ prepareContent("abc", 0, 0, -1, -1);
+ assertThat(mBaseInputConnection.setSelection(2, 2)).isTrue();
+ verifyContent("abc", 2, 2, -1, -1);
+
+ // before set selection: "|abc"
+ // after set selection: "|abc"
+ prepareContent("abc", 0, 0, -1, -1);
+ assertThat(mBaseInputConnection.setSelection(-1, 2)).isTrue();
+ verifyContent("abc", 0, 0, -1, -1);
+ }
+
+ @Test
+ public void testSetSelection_toEditorWithComposing() {
+ // before set selection: "abc|"
+ // ---
+ // after set selection: "a|bc"
+ // - --
+ prepareContent("abc", 3, 3, 0, 3);
+ assertThat(mBaseInputConnection.setSelection(1, 1)).isTrue();
+ verifyContent("abc", 1, 1, 0, 3);
+
+ // before set selection: "abcdef|"
+ // ---
+ // after set selection: "|abcdef|"
+ // ---
+ prepareContent("abcdef", 6, 6, 2, 5);
+ assertThat(mBaseInputConnection.setSelection(0, 6)).isTrue();
+ verifyContent("abcdef", 0, 6, 2, 5);
+ }
+
+ @Test
+ public void testGetText_noStyle() {
+ // "123|456|789"
+ prepareContent("123456789", 3, 6, -1, -1);
+
+ verifyContentEquals(mBaseInputConnection.getTextBeforeCursor(1, 0), "3");
+ verifyContentEquals(mBaseInputConnection.getTextAfterCursor(1, 0), "7");
+ verifyContentEquals(mBaseInputConnection.getSelectedText(0), "456");
+ // This falls back to default implementation in {@code InputConnection}, which always return
+ // -1 for offset.
+ assertThat(
+ mBaseInputConnection
+ .getSurroundingText(1, 1, 0)
+ .isEqualTo(new SurroundingText("34567", 1, 4, -1)))
+ .isTrue();
+
+ verifyContentEquals(mBaseInputConnection.getTextBeforeCursor(100, 0), "123");
+ verifyContentEquals(mBaseInputConnection.getTextAfterCursor(100, 0), "789");
+ assertThat(
+ mBaseInputConnection
+ .getSurroundingText(100, 100, 0)
+ .isEqualTo(new SurroundingText("123456789", 3, 6, -1)))
+ .isTrue();
+
+ verifyContentEquals(mBaseInputConnection.getTextBeforeCursor(0, 0), "");
+ verifyContentEquals(mBaseInputConnection.getTextAfterCursor(0, 0), "");
+ assertThat(
+ mBaseInputConnection
+ .getSurroundingText(0, 0, 0)
+ .isEqualTo(new SurroundingText("456", 0, 3, -1)))
+ .isTrue();
+
+ int cursorCapsMode =
+ TextUtils.getCapsMode(
+ "123456789",
+ 3,
+ TextUtils.CAP_MODE_CHARACTERS
+ | TextUtils.CAP_MODE_WORDS
+ | TextUtils.CAP_MODE_SENTENCES);
+ TextSnapshot expectedTextSnapshot =
+ new TextSnapshot(
+ new SurroundingText("123456789", 3, 6, -1), -1, -1, cursorCapsMode);
+ verifyTextSnapshotContentEquals(mBaseInputConnection.takeSnapshot(), expectedTextSnapshot);
+ }
+
+ @Test
+ public void testGetText_withStyle() {
+ // "123|456|789"
+ SpannableStringBuilder text = new SpannableStringBuilder("123456789");
+ SuggestionSpan suggestionSpanA =
+ new SuggestionSpan(Locale.US, new String[] {"a"}, SuggestionSpan.FLAG_EASY_CORRECT);
+ SuggestionSpan suggestionSpanB =
+ new SuggestionSpan(Locale.US, new String[] {"b"}, SuggestionSpan.FLAG_EASY_CORRECT);
+ SuggestionSpan suggestionSpanC =
+ new SuggestionSpan(Locale.US, new String[] {"c"}, SuggestionSpan.FLAG_EASY_CORRECT);
+ text.setSpan(suggestionSpanA, 0, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(suggestionSpanB, 3, 6, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ text.setSpan(suggestionSpanC, 6, 9, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ prepareContent(text, 3, 6, -1, -1);
+
+ verifySpannableString(
+ mBaseInputConnection.getTextBeforeCursor(1, InputConnection.GET_TEXT_WITH_STYLES),
+ "3",
+ 1,
+ new int[][] {new int[] {0, 1}},
+ new Object[] {suggestionSpanA});
+ verifySpannableString(
+ mBaseInputConnection.getTextAfterCursor(1, InputConnection.GET_TEXT_WITH_STYLES),
+ "7",
+ 1,
+ new int[][] {new int[] {0, 1}},
+ new Object[] {suggestionSpanC});
+ verifySpannableString(
+ mBaseInputConnection.getSelectedText(InputConnection.GET_TEXT_WITH_STYLES),
+ "456",
+ 1,
+ new int[][] {new int[] {0, 3}},
+ new Object[] {suggestionSpanB});
+ CharSequence surroundTextString =
+ TextUtils.concat(
+ text.subSequence(0, 3), text.subSequence(3, 6), text.subSequence(6, 9));
+ assertThat(
+ mBaseInputConnection
+ .getSurroundingText(100, 100, InputConnection.GET_TEXT_WITH_STYLES)
+ .isEqualTo(new SurroundingText(surroundTextString, 3, 6, -1)))
+ .isTrue();
+
+ int cursorCapsMode =
+ TextUtils.getCapsMode(
+ "123456789",
+ 3,
+ TextUtils.CAP_MODE_CHARACTERS
+ | TextUtils.CAP_MODE_WORDS
+ | TextUtils.CAP_MODE_SENTENCES);
+ TextSnapshot expectedTextSnapshot =
+ new TextSnapshot(
+ new SurroundingText(surroundTextString, 3, 6, -1), -1, -1, cursorCapsMode);
+ verifyTextSnapshotContentEquals(mBaseInputConnection.takeSnapshot(), expectedTextSnapshot);
+ }
+
+ @Test
+ public void testReplaceText_toEditorWithoutSelectionAndComposing() {
+ // before replace: "|"
+ // after replace: "text1|"
+ assertThat(mBaseInputConnection.replaceText(0, 0, "text1", 1, null)).isTrue();
+ verifyContent("text1", 5, 5, -1, -1);
+
+ // before replace: "text1|"
+ // after replace: "text2|"
+ assertThat(mBaseInputConnection.replaceText(0, 5, "text2", 1, null)).isTrue();
+ verifyContent("text2", 5, 5, -1, -1);
+
+ // before replace: "text1|"
+ // after replace: "|text3"
+ assertThat(mBaseInputConnection.replaceText(0, 5, "text3", -1, null)).isTrue();
+ verifyContent("text3", 0, 0, -1, -1);
+
+ // before replace: "|text3"
+ // after replace: "ttext4|t3"
+ // BUG(b/21476564): this behavior is inconsistent with API description.
+ assertThat(mBaseInputConnection.replaceText(1, 3, "text4", 1, null)).isTrue();
+ verifyContent("ttext4t3", 6, 6, -1, -1);
+
+ // before replace: "ttext4|t3"
+ // after replace: "|text5t3"
+ assertThat(mBaseInputConnection.replaceText(0, 6, "text5", -1, null)).isTrue();
+ verifyContent("text5t3", 0, 0, -1, -1);
+ }
+
+ @Test
+ public void testReplaceText_toEditorWithSelection() {
+ // before replace: "123|456|789"
+ // before replace: "123text|6789"
+ prepareContent("123456789", 3, 6, -1, -1);
+ assertThat(mBaseInputConnection.replaceText(3, 5, "text", 1, null)).isTrue();
+ verifyContent("123text6789", 7, 7, -1, -1);
+
+ // before replace: "|123|"
+ // before replace: "|text23"
+ prepareContent("123", 0, 3, -1, -1);
+ assertThat(mBaseInputConnection.replaceText(0, 1, "text", 0, null)).isTrue();
+ verifyContent("text23", 0, 0, -1, -1);
+ }
+
+ @Test
+ public void testReplaceText_toEditorWithComposing() {
+ // before replace: "123456|789"
+ // ---
+ // before replace: "123456text|"
+ prepareContent("123456789", 6, 6, 3, 6);
+ assertThat(mBaseInputConnection.replaceText(6, 9, "text", 1, null)).isTrue();
+ verifyContent("123456text", 10, 10, -1, -1);
+
+ // before replace: "123456789|"
+ // ---
+ // before replace: "text|123456789"
+ prepareContent("123456789", 9, 9, 3, 6);
+ assertThat(mBaseInputConnection.replaceText(0, 0, "text", 1, null)).isTrue();
+ verifyContent("text123456789", 4, 4, -1, -1);
+
+ // before replace: "|123456789|"
+ // ---
+ // before replace: "12text|9"
+ prepareContent("123456789", 0, 9, 3, 6);
+ assertThat(mBaseInputConnection.replaceText(2, 8, "text", 1, null)).isTrue();
+ verifyContent("12text9", 6, 6, -1, -1);
+ }
+
+ private void prepareContent(
+ CharSequence text,
+ int selectionStart,
+ int selectionEnd,
+ int composingSpanStart,
+ int composingSpanEnd) {
+ mEditable.clear();
+ mEditable.append(text);
+ Selection.setSelection(mEditable, selectionStart, selectionEnd);
+ if (isValidComposingSpan(text.length(), composingSpanStart, composingSpanEnd)) {
+ BaseInputConnection.setComposingSpans(mEditable, composingSpanStart, composingSpanEnd);
+ }
+ verifyContent(text, selectionStart, selectionEnd, composingSpanStart, composingSpanEnd);
+ }
+
+ private boolean isValidComposingSpan(
+ int textLength, int composingSpanStart, int composingSpanEnd) {
+ return composingSpanStart >= 0
+ && composingSpanStart <= textLength
+ && composingSpanEnd >= 0
+ && composingSpanEnd <= textLength;
+ }
+
+ private void verifyContent(
+ CharSequence text,
+ int selectionStart,
+ int selectionEnd,
+ int composingSpanStart,
+ int composingSpanEnd) {
+ assertThat(mEditable).isNotNull();
+ verifyContentEquals(mEditable, text.toString());
+ assertThat(Selection.getSelectionStart(mEditable)).isEqualTo(selectionStart);
+ assertThat(Selection.getSelectionEnd(mEditable)).isEqualTo(selectionEnd);
+ assertThat(BaseInputConnection.getComposingSpanStart(mEditable))
+ .isEqualTo(composingSpanStart);
+ assertThat(BaseInputConnection.getComposingSpanEnd(mEditable)).isEqualTo(composingSpanEnd);
+ }
+
+ private void verifySpannableString(
+ CharSequence text,
+ String expectedString,
+ int expectedSpanSize,
+ int[][] expectedSpanRanges,
+ Object[] expectedSpans) {
+ verifyContentEquals(text, expectedString);
+ SpannableStringBuilder spannableString = new SpannableStringBuilder(text);
+ Object[] spanList = spannableString.getSpans(0, text.length(), Object.class);
+ assertThat(spanList).isNotNull();
+ assertThat(spanList).hasLength(expectedSpanSize);
+ for (int i = 0; i < expectedSpanSize; i++) {
+ assertThat(spannableString.getSpanStart(spanList[i]))
+ .isEqualTo(expectedSpanRanges[i][0]);
+ assertThat(spannableString.getSpanEnd(spanList[i])).isEqualTo(expectedSpanRanges[i][1]);
+ }
+ for (int i = 0; i < expectedSpanSize; i++) {
+ assertThat(spanList[i]).isEqualTo(expectedSpans[i]);
+ }
+ }
+
+ private void verifyContentEquals(CharSequence text, String expectedText) {
+ assertThat(text.toString().contentEquals(expectedText)).isTrue();
+ }
+
+ private void verifyTextSnapshotContentEquals(TextSnapshot t1, TextSnapshot t2) {
+ assertThat(t1.getCompositionStart()).isEqualTo(t2.getCompositionStart());
+ assertThat(t1.getCompositionEnd()).isEqualTo(t2.getCompositionEnd());
+ assertThat(t1.getCursorCapsMode()).isEqualTo(t2.getCursorCapsMode());
+ assertThat(t1.getSurroundingText().isEqualTo(t2.getSurroundingText())).isTrue();
+ }
+}
diff --git a/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
index 50ce335cbec6..047f33074460 100644
--- a/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/SurroundingTextTest.java
@@ -16,18 +16,21 @@
package android.view.inputmethod;
-import static org.hamcrest.CoreMatchers.is;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.Assert.assertFalse;
+import static com.google.common.truth.Truth.assertThat;
import android.os.Parcel;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.style.SuggestionSpan;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Locale;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SurroundingTextTest {
@@ -35,22 +38,22 @@ public class SurroundingTextTest {
@Test
public void testSurroundingTextBasicCreation() {
SurroundingText surroundingText1 = new SurroundingText("test", 0, 0, 0);
- assertThat(surroundingText1.getText(), is("test"));
- assertThat(surroundingText1.getSelectionStart(), is(0));
- assertThat(surroundingText1.getSelectionEnd(), is(0));
- assertThat(surroundingText1.getOffset(), is(0));
+ assertThat(surroundingText1.getText().toString()).isEqualTo("test");
+ assertThat(surroundingText1.getSelectionStart()).isEqualTo(0);
+ assertThat(surroundingText1.getSelectionEnd()).isEqualTo(0);
+ assertThat(surroundingText1.getOffset()).isEqualTo(0);
SurroundingText surroundingText2 = new SurroundingText("", -1, -1, -1);
- assertThat(surroundingText2.getText(), is(""));
- assertThat(surroundingText2.getSelectionStart(), is(-1));
- assertThat(surroundingText2.getSelectionEnd(), is(-1));
- assertThat(surroundingText2.getOffset(), is(-1));
+ assertThat(surroundingText2.getText().toString()).isEmpty();
+ assertThat(surroundingText2.getSelectionStart()).isEqualTo(-1);
+ assertThat(surroundingText2.getSelectionEnd()).isEqualTo(-1);
+ assertThat(surroundingText2.getOffset()).isEqualTo(-1);
SurroundingText surroundingText3 = new SurroundingText("hello", 0, 5, 0);
- assertThat(surroundingText3.getText(), is("hello"));
- assertThat(surroundingText3.getSelectionStart(), is(0));
- assertThat(surroundingText3.getSelectionEnd(), is(5));
- assertThat(surroundingText3.getOffset(), is(0));
+ assertThat(surroundingText3.getText().toString()).isEqualTo("hello");
+ assertThat(surroundingText3.getSelectionStart()).isEqualTo(0);
+ assertThat(surroundingText3.getSelectionEnd()).isEqualTo(5);
+ assertThat(surroundingText3.getOffset()).isEqualTo(0);
}
@Test
@@ -62,20 +65,73 @@ public class SurroundingTextTest {
parcel.setDataPosition(0);
SurroundingText surroundingTextFromParcel =
SurroundingText.CREATOR.createFromParcel(parcel);
- assertThat(surroundingText.getText(), is("text"));
- assertThat(surroundingText.getSelectionStart(), is(0));
- assertThat(surroundingText.getSelectionEnd(), is(1));
- assertThat(surroundingText.getOffset(), is(2));
- assertThat(surroundingTextFromParcel.getText(), is("text"));
- assertThat(surroundingTextFromParcel.getSelectionStart(), is(0));
- assertThat(surroundingTextFromParcel.getSelectionEnd(), is(1));
- assertThat(surroundingTextFromParcel.getOffset(), is(2));
+ assertThat(surroundingText.getText().toString()).isEqualTo("text");
+ assertThat(surroundingText.getSelectionStart()).isEqualTo(0);
+ assertThat(surroundingText.getSelectionEnd()).isEqualTo(1);
+ assertThat(surroundingText.getOffset()).isEqualTo(2);
+ assertThat(surroundingTextFromParcel.getText().toString()).isEqualTo("text");
+ assertThat(surroundingTextFromParcel.getSelectionStart()).isEqualTo(0);
+ assertThat(surroundingTextFromParcel.getSelectionEnd()).isEqualTo(1);
+ assertThat(surroundingTextFromParcel.getOffset()).isEqualTo(2);
}
@Test
- public void testIsEqualComparesText() {
- final SurroundingText text1 = new SurroundingText("hello", 0, 1, 0);
- final SurroundingText text2 = new SurroundingText("there", 0, 1, 0);
- assertFalse(text1.isEqualTo(text2));
+ public void testIsEqualComparesText_isNotEqualTo() {
+ final SurroundingText text = new SurroundingText("hello", 0, 1, 0);
+
+ verifySurroundingTextNotEquals(text, new SurroundingText("there", 0, 1, 0));
+ verifySurroundingTextNotEquals(text, new SurroundingText("hello", 0, 1, -1));
+ verifySurroundingTextNotEquals(text, new SurroundingText("hello", 0, 0, 0));
+ verifySurroundingTextNotEquals(text, new SurroundingText("hello", 1, 1, 0));
+
+ SpannableString spannableString = new SpannableString("hello");
+ spannableString.setSpan(
+ new SuggestionSpan(
+ Locale.US, new String[] {"Hello"}, SuggestionSpan.FLAG_EASY_CORRECT),
+ 0,
+ 5,
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ verifySurroundingTextNotEquals(text, new SurroundingText(spannableString, 1, 1, 0));
+ }
+
+ private void verifySurroundingTextNotEquals(SurroundingText text1, SurroundingText text2) {
+ assertThat(text1.isEqualTo(text2)).isFalse();
+ assertThat(text1).isNotEqualTo(text2);
+ assertThat(text1.equals(text2)).isFalse();
+ assertThat(text1.hashCode()).isNotEqualTo(text2.hashCode());
+ assertThat(text1 == text2).isFalse();
+ }
+
+ @Test
+ public void testIsEqualComparesText_isEqualTo() {
+ final SurroundingText text = new SurroundingText("hello", 0, 1, 0);
+
+ verifySurroundingTextEquals(
+ text,
+ new SurroundingText("hello", 0, 1, 0),
+ /*equals=*/ false,
+ /*isSameInstance=*/ false);
+ verifySurroundingTextEquals(text, text, /*equals=*/ true, /*isSameInstance=*/ true);
+ }
+
+ private void verifySurroundingTextEquals(
+ SurroundingText text1, SurroundingText text2, boolean equals, boolean isSameInstance) {
+ assertThat(text1.isEqualTo(text2)).isTrue();
+ if (equals) {
+ assertThat(text1).isEqualTo(text2);
+ assertThat(text1.equals(text2)).isTrue();
+ assertThat(text1.hashCode()).isEqualTo(text2.hashCode());
+ } else {
+ assertThat(text1).isNotEqualTo(text2);
+ assertThat(text1.equals(text2)).isFalse();
+ assertThat(text1.hashCode()).isNotEqualTo(text2.hashCode());
+ }
+ if (isSameInstance) {
+ assertThat(text1 == text2).isTrue();
+ assertThat(text1).isSameInstanceAs(text2);
+ } else {
+ assertThat(text1 == text2).isFalse();
+ assertThat(text1).isNotSameInstanceAs(text2);
+ }
}
}
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 4770fa6315aa..7674135719e9 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -262,6 +262,8 @@ public class InteractionJankMonitorTest {
&& f.getType() == int.class)
.collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));
+ assertThat(enumsMap.size() - 1).isEqualTo(cujs.size());
+
cujs.forEach(f -> {
final int cuj = getIntFieldChecked(f);
final String cujName = f.getName();
@@ -279,7 +281,9 @@ public class InteractionJankMonitorTest {
.that(expectedEnumName.equals(enumName))
.isTrue();
mExpect
- .withMessage(formatSimple("getNameOfCuj(%d) not matches %s", cuj, cujName))
+ .withMessage(
+ formatSimple("getNameOfCuj(%d) not matches: %s, expected=%s",
+ cuj, cujName, expectedNameOfCuj))
.that(cujName.equals(expectedNameOfCuj))
.isTrue();
});
diff --git a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
index a22dd1c63ddf..516dee7dc9aa 100644
--- a/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/LongArrayMultiStateCounterTest.java
@@ -161,4 +161,33 @@ public class LongArrayMultiStateCounterTest {
assertThrows(RuntimeException.class,
() -> LongArrayMultiStateCounter.CREATOR.createFromParcel(parcel));
}
+
+ @Test
+ public void combineValues() {
+ long[] values = new long[] {0, 1, 2, 3, 42};
+ LongArrayMultiStateCounter.LongArrayContainer container =
+ new LongArrayMultiStateCounter.LongArrayContainer(values.length);
+ container.setValues(values);
+
+ long[] out = new long[3];
+ int[] indexes = {2, 1, 1, 0, 0};
+ boolean nonZero = container.combineValues(out, indexes);
+ assertThat(nonZero).isTrue();
+ assertThat(out).isEqualTo(new long[]{45, 3, 0});
+
+ // All zeros
+ container.setValues(new long[]{0, 0, 0, 0, 0});
+ nonZero = container.combineValues(out, indexes);
+ assertThat(nonZero).isFalse();
+ assertThat(out).isEqualTo(new long[]{0, 0, 0});
+
+ // Index out of range
+ IndexOutOfBoundsException e1 = assertThrows(
+ IndexOutOfBoundsException.class,
+ () -> container.combineValues(out, new int[]{0, 1, -1, 0, 0}));
+ assertThat(e1.getMessage()).isEqualTo("Index -1 is out of bounds: [0, 2]");
+ IndexOutOfBoundsException e2 = assertThrows(IndexOutOfBoundsException.class,
+ () -> container.combineValues(out, new int[]{0, 1, 4, 0, 0}));
+ assertThat(e2.getMessage()).isEqualTo("Index 4 is out of bounds: [0, 2]");
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
new file mode 100644
index 000000000000..e384e699333b
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/LatencyTrackerTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import static android.text.TextUtils.formatSimple;
+
+import static com.android.internal.util.LatencyTracker.STATSD_ACTION;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@SmallTest
+public class LatencyTrackerTest {
+ private static final String ENUM_NAME_PREFIX = "UIACTION_LATENCY_REPORTED__ACTION__";
+
+ @Rule
+ public final Expect mExpect = Expect.create();
+
+ @Test
+ public void testCujsMapToEnumsCorrectly() {
+ List<Field> actions = Arrays.stream(LatencyTracker.class.getDeclaredFields())
+ .filter(f -> f.getName().startsWith("ACTION_")
+ && Modifier.isStatic(f.getModifiers())
+ && f.getType() == int.class)
+ .collect(Collectors.toList());
+
+ Map<Integer, String> enumsMap = Arrays.stream(FrameworkStatsLog.class.getDeclaredFields())
+ .filter(f -> f.getName().startsWith(ENUM_NAME_PREFIX)
+ && Modifier.isStatic(f.getModifiers())
+ && f.getType() == int.class)
+ .collect(Collectors.toMap(this::getIntFieldChecked, Field::getName));
+
+ assertThat(enumsMap.size() - 1).isEqualTo(actions.size());
+
+ actions.forEach(f -> {
+ final int action = getIntFieldChecked(f);
+ final String actionName = f.getName();
+ final String expectedEnumName = formatSimple("%s%s", ENUM_NAME_PREFIX, actionName);
+ final int enumKey = STATSD_ACTION[action];
+ final String enumName = enumsMap.get(enumKey);
+ final String expectedActionName = LatencyTracker.getNameOfAction(enumKey);
+ mExpect
+ .withMessage(formatSimple(
+ "%s (%d) not matches %s (%d)", actionName, action, enumName, enumKey))
+ .that(expectedEnumName.equals(enumName))
+ .isTrue();
+ mExpect
+ .withMessage(
+ formatSimple("getNameOfAction(%d) not matches: %s, expected=%s",
+ enumKey, actionName, expectedActionName))
+ .that(actionName.equals(expectedActionName))
+ .isTrue();
+ });
+ }
+
+ @Test
+ public void testCujTypeEnumCorrectlyDefined() throws Exception {
+ List<Field> cujEnumFields =
+ Arrays.stream(LatencyTracker.class.getDeclaredFields())
+ .filter(field -> field.getName().startsWith("ACTION_")
+ && Modifier.isStatic(field.getModifiers())
+ && field.getType() == int.class)
+ .collect(Collectors.toList());
+
+ HashSet<Integer> allValues = new HashSet<>();
+ for (Field field : cujEnumFields) {
+ int fieldValue = field.getInt(null);
+ assertWithMessage(
+ "Field %s must have a mapping to a value in STATSD_ACTION",
+ field.getName())
+ .that(fieldValue < STATSD_ACTION.length)
+ .isTrue();
+ assertWithMessage("All CujType values must be unique. Field %s repeats existing value.",
+ field.getName())
+ .that(allValues.add(fieldValue))
+ .isTrue();
+ }
+ }
+
+ private int getIntFieldChecked(Field field) {
+ try {
+ return field.getInt(null);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+}
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
index 4c247427ef8f..9e39e13265bd 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateManagerGlobalTest.java
@@ -16,11 +16,12 @@
package android.hardware.devicestate;
+import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -36,7 +37,6 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import org.mockito.Mockito;
import java.util.HashSet;
import java.util.Set;
@@ -59,6 +59,7 @@ public final class DeviceStateManagerGlobalTest {
public void setUp() {
mService = new TestDeviceStateManagerService();
mDeviceStateManagerGlobal = new DeviceStateManagerGlobal(mService);
+ assertFalse(mService.mCallbacks.isEmpty());
}
@Test
@@ -79,8 +80,8 @@ public final class DeviceStateManagerGlobalTest {
verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback2).onStateChanged(eq(mService.getMergedState()));
- Mockito.reset(callback1);
- Mockito.reset(callback2);
+ reset(callback1);
+ reset(callback2);
// Change the supported states and verify callback
mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE });
@@ -88,8 +89,8 @@ public final class DeviceStateManagerGlobalTest {
verify(callback2).onSupportedStatesChanged(eq(mService.getSupportedStates()));
mService.setSupportedStates(new int[]{ DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE });
- Mockito.reset(callback1);
- Mockito.reset(callback2);
+ reset(callback1);
+ reset(callback2);
// Change the base state and verify callback
mService.setBaseState(OTHER_DEVICE_STATE);
@@ -98,8 +99,8 @@ public final class DeviceStateManagerGlobalTest {
verify(callback2).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback2).onStateChanged(eq(mService.getMergedState()));
- Mockito.reset(callback1);
- Mockito.reset(callback2);
+ reset(callback1);
+ reset(callback2);
// Change the requested state and verify callback
DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
@@ -120,7 +121,7 @@ public final class DeviceStateManagerGlobalTest {
verify(callback).onSupportedStatesChanged(eq(mService.getSupportedStates()));
verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback).onStateChanged(eq(mService.getMergedState()));
- Mockito.reset(callback);
+ reset(callback);
mDeviceStateManagerGlobal.unregisterDeviceStateCallback(callback);
@@ -130,33 +131,86 @@ public final class DeviceStateManagerGlobalTest {
}
@Test
- public void submittingRequestRegistersCallback() {
- assertTrue(mService.mCallbacks.isEmpty());
+ public void submitRequest() {
+ DeviceStateCallback callback = mock(DeviceStateCallback.class);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
+ ConcurrentUtils.DIRECT_EXECUTOR);
- DeviceStateRequest request = DeviceStateRequest.newBuilder(DEFAULT_DEVICE_STATE).build();
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
+ reset(callback);
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
- assertFalse(mService.mCallbacks.isEmpty());
+ verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
+ reset(callback);
+
+ mDeviceStateManagerGlobal.cancelStateRequest();
+
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
}
@Test
- public void submitRequest() {
+ public void submitBaseStateOverrideRequest() {
DeviceStateCallback callback = mock(DeviceStateCallback.class);
mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
ConcurrentUtils.DIRECT_EXECUTOR);
+ verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback).onStateChanged(eq(mService.getBaseState()));
- Mockito.reset(callback);
+ reset(callback);
DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
- mDeviceStateManagerGlobal.requestState(request, null /* executor */, null /* callback */);
+ mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */,
+ null /* callback */);
+ verify(callback).onBaseStateChanged(eq(OTHER_DEVICE_STATE));
verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
- Mockito.reset(callback);
+ reset(callback);
- mDeviceStateManagerGlobal.cancelStateRequest();
+ mDeviceStateManagerGlobal.cancelBaseStateOverride();
+
+ verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
+ verify(callback).onStateChanged(eq(mService.getBaseState()));
+ }
+
+ @Test
+ public void submitBaseAndEmulatedStateOverride() {
+ DeviceStateCallback callback = mock(DeviceStateCallback.class);
+ mDeviceStateManagerGlobal.registerDeviceStateCallback(callback,
+ ConcurrentUtils.DIRECT_EXECUTOR);
+ verify(callback).onBaseStateChanged(eq(mService.getBaseState()));
verify(callback).onStateChanged(eq(mService.getBaseState()));
+ reset(callback);
+
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(OTHER_DEVICE_STATE).build();
+ mDeviceStateManagerGlobal.requestBaseStateOverride(request, null /* executor */,
+ null /* callback */);
+
+ verify(callback).onBaseStateChanged(eq(OTHER_DEVICE_STATE));
+ verify(callback).onStateChanged(eq(OTHER_DEVICE_STATE));
+ assertEquals(OTHER_DEVICE_STATE, mService.getBaseState());
+ reset(callback);
+
+ DeviceStateRequest secondRequest = DeviceStateRequest.newBuilder(
+ DEFAULT_DEVICE_STATE).build();
+
+ mDeviceStateManagerGlobal.requestState(secondRequest, null, null);
+
+ assertEquals(OTHER_DEVICE_STATE, mService.getBaseState());
+ verify(callback).onStateChanged(eq(DEFAULT_DEVICE_STATE));
+ reset(callback);
+
+ mDeviceStateManagerGlobal.cancelStateRequest();
+
+ verify(callback).onStateChanged(OTHER_DEVICE_STATE);
+ reset(callback);
+
+ mDeviceStateManagerGlobal.cancelBaseStateOverride();
+
+ verify(callback).onBaseStateChanged(DEFAULT_DEVICE_STATE);
+ verify(callback).onStateChanged(DEFAULT_DEVICE_STATE);
}
@Test
@@ -169,7 +223,7 @@ public final class DeviceStateManagerGlobalTest {
callback /* callback */);
verify(callback).onRequestActivated(eq(request));
- Mockito.reset(callback);
+ reset(callback);
mDeviceStateManagerGlobal.cancelStateRequest();
@@ -203,13 +257,16 @@ public final class DeviceStateManagerGlobalTest {
private int[] mSupportedStates = new int[] { DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE };
private int mBaseState = DEFAULT_DEVICE_STATE;
private Request mRequest;
+ private Request mBaseStateRequest;
private Set<IDeviceStateManagerCallback> mCallbacks = new HashSet<>();
private DeviceStateInfo getInfo() {
+ final int mergedBaseState = mBaseStateRequest == null
+ ? mBaseState : mBaseStateRequest.state;
final int mergedState = mRequest == null
- ? mBaseState : mRequest.state;
- return new DeviceStateInfo(mSupportedStates, mBaseState, mergedState);
+ ? mergedBaseState : mRequest.state;
+ return new DeviceStateInfo(mSupportedStates, mergedBaseState, mergedState);
}
private void notifyDeviceStateInfoChanged() {
@@ -238,7 +295,7 @@ public final class DeviceStateManagerGlobalTest {
try {
callback.onDeviceStateInfoChanged(getInfo());
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
}
}
@@ -249,7 +306,7 @@ public final class DeviceStateManagerGlobalTest {
try {
callback.onRequestCanceled(mRequest.token);
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
}
}
}
@@ -262,7 +319,7 @@ public final class DeviceStateManagerGlobalTest {
try {
callback.onRequestActive(token);
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
}
}
}
@@ -275,7 +332,46 @@ public final class DeviceStateManagerGlobalTest {
try {
callback.onRequestCanceled(token);
} catch (RemoteException e) {
- // Do nothing. Should never happen.
+ e.rethrowFromSystemServer();
+ }
+ }
+ notifyDeviceStateInfoChanged();
+ }
+
+ @Override
+ public void requestBaseStateOverride(IBinder token, int state, int flags) {
+ if (mBaseStateRequest != null) {
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestCanceled(mBaseStateRequest.token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ final Request request = new Request(token, state, flags);
+ mBaseStateRequest = request;
+ notifyDeviceStateInfoChanged();
+
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestActive(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ @Override
+ public void cancelBaseStateOverride() throws RemoteException {
+ IBinder token = mBaseStateRequest.token;
+ mBaseStateRequest = null;
+ for (IDeviceStateManagerCallback callback : mCallbacks) {
+ try {
+ callback.onRequestCanceled(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
}
}
notifyDeviceStateInfoChanged();
@@ -296,7 +392,7 @@ public final class DeviceStateManagerGlobalTest {
}
public int getBaseState() {
- return mBaseState;
+ return getInfo().baseState;
}
public int getMergedState() {
diff --git a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
index 940ca96fac4f..4679a9ea6f66 100644
--- a/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/LockPatternUtilsTest.java
@@ -19,14 +19,15 @@ package com.android.internal.util;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_MANAGED;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
-import static org.junit.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
@@ -51,8 +52,11 @@ import com.android.internal.widget.IWeakEscrowTokenActivatedListener;
import com.android.internal.widget.IWeakEscrowTokenRemovedListener;
import com.android.internal.widget.LockPatternUtils;
+import com.google.android.collect.Lists;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.nio.charset.StandardCharsets;
@@ -172,13 +176,61 @@ public class LockPatternUtilsTest {
}
@Test
- public void testGetEnabledTrustAgentsNotNull() throws RemoteException {
+ public void testSetEnabledTrustAgents() throws RemoteException {
int testUserId = 10;
ILockSettings ils = createTestLockSettings();
- when(ils.getString(anyString(), any(), anyInt())).thenReturn("");
+ ArgumentCaptor<String> valueCaptor = ArgumentCaptor.forClass(String.class);
+ doNothing().when(ils).setString(anyString(), valueCaptor.capture(), anyInt());
+ List<ComponentName> enabledTrustAgents = Lists.newArrayList(
+ ComponentName.unflattenFromString("com.android/.TrustAgent"),
+ ComponentName.unflattenFromString("com.test/.TestAgent"));
+
+ mLockPatternUtils.setEnabledTrustAgents(enabledTrustAgents, testUserId);
+
+ assertThat(valueCaptor.getValue()).isEqualTo("com.android/.TrustAgent,com.test/.TestAgent");
+ }
+
+ @Test
+ public void testGetEnabledTrustAgents() throws RemoteException {
+ int testUserId = 10;
+ ILockSettings ils = createTestLockSettings();
+ when(ils.getString(anyString(), any(), anyInt())).thenReturn(
+ "com.android/.TrustAgent,com.test/.TestAgent");
+
List<ComponentName> trustAgents = mLockPatternUtils.getEnabledTrustAgents(testUserId);
- assertNotNull(trustAgents);
- assertEquals(0, trustAgents.size());
+
+ assertThat(trustAgents).containsExactly(
+ ComponentName.unflattenFromString("com.android/.TrustAgent"),
+ ComponentName.unflattenFromString("com.test/.TestAgent"));
+ }
+
+ @Test
+ public void testSetKnownTrustAgents() throws RemoteException {
+ int testUserId = 10;
+ ILockSettings ils = createTestLockSettings();
+ ArgumentCaptor<String> valueCaptor = ArgumentCaptor.forClass(String.class);
+ doNothing().when(ils).setString(anyString(), valueCaptor.capture(), anyInt());
+ List<ComponentName> knownTrustAgents = Lists.newArrayList(
+ ComponentName.unflattenFromString("com.android/.TrustAgent"),
+ ComponentName.unflattenFromString("com.test/.TestAgent"));
+
+ mLockPatternUtils.setKnownTrustAgents(knownTrustAgents, testUserId);
+
+ assertThat(valueCaptor.getValue()).isEqualTo("com.android/.TrustAgent,com.test/.TestAgent");
+ }
+
+ @Test
+ public void testGetKnownTrustAgents() throws RemoteException {
+ int testUserId = 10;
+ ILockSettings ils = createTestLockSettings();
+ when(ils.getString(anyString(), any(), anyInt())).thenReturn(
+ "com.android/.TrustAgent,com.test/.TestAgent");
+
+ List<ComponentName> trustAgents = mLockPatternUtils.getKnownTrustAgents(testUserId);
+
+ assertThat(trustAgents).containsExactly(
+ ComponentName.unflattenFromString("com.android/.TrustAgent"),
+ ComponentName.unflattenFromString("com.test/.TestAgent"));
}
private ILockSettings createTestLockSettings() {
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 682483d9ce63..cbdef846ceaf 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -373,12 +373,6 @@
"group": "WM_DEBUG_WINDOW_TRANSITIONS",
"at": "com\/android\/server\/wm\/TransitionController.java"
},
- "-1750384749": {
- "message": "Launch on display check: allow launch on public display",
- "level": "DEBUG",
- "group": "WM_DEBUG_TASKS",
- "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
- },
"-1750206390": {
"message": "Exception thrown when creating surface for client %s (%s). %s",
"level": "WARN",
@@ -907,6 +901,12 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
+ "-1253056469": {
+ "message": "Launch on display check: %s launch for userId=%d on displayId=%d",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_TASKS",
+ "at": "com\/android\/server\/wm\/ActivityTaskSupervisor.java"
+ },
"-1248645819": {
"message": "\tAdd container=%s",
"level": "DEBUG",
@@ -3115,6 +3115,12 @@
"group": "WM_DEBUG_STARTING_WINDOW",
"at": "com\/android\/server\/wm\/WindowStateAnimator.java"
},
+ "829869827": {
+ "message": "Cannot launch dream activity due to invalid state. dreaming: %b packageName: %s",
+ "level": "ERROR",
+ "group": "WM_DEBUG_DREAM",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+ },
"835814848": {
"message": "%s",
"level": "INFO",
@@ -4141,6 +4147,12 @@
"group": "WM_DEBUG_WINDOW_ORGANIZER",
"at": "com\/android\/server\/wm\/TaskOrganizerController.java"
},
+ "1918771553": {
+ "message": "Dream packageName does not match active dream. Package %s does not match %s or %s",
+ "level": "ERROR",
+ "group": "WM_DEBUG_DREAM",
+ "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
+ },
"1921821199": {
"message": "Preserving %s until the new one is added",
"level": "VERBOSE",
@@ -4383,6 +4395,9 @@
"WM_DEBUG_DRAW": {
"tag": "WindowManager"
},
+ "WM_DEBUG_DREAM": {
+ "tag": "WindowManager"
+ },
"WM_DEBUG_FOCUS": {
"tag": "WindowManager"
},
diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl
index a186aca4648f..af96c743f400 100644
--- a/data/keyboards/Generic.kl
+++ b/data/keyboards/Generic.kl
@@ -302,6 +302,11 @@ key 317 BUTTON_THUMBL
key 318 BUTTON_THUMBR
+key 329 STYLUS_BUTTON_TERTIARY
+key 331 STYLUS_BUTTON_PRIMARY
+key 332 STYLUS_BUTTON_SECONDARY
+
+
# key 352 "KEY_OK"
key 353 DPAD_CENTER
# key 354 "KEY_GOTO"
@@ -424,6 +429,8 @@ key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE
key usage 0x0c0173 MEDIA_AUDIO_TRACK
key usage 0x0c019C PROFILE_SWITCH
key usage 0x0c01A2 ALL_APPS
+key usage 0x0d0044 STYLUS_BUTTON_PRIMARY
+key usage 0x0d005a STYLUS_BUTTON_SECONDARY
# Joystick and game controller axes.
# Axes that are not mapped will be assigned generic axis numbers by the input subsystem.
diff --git a/data/sounds/AudioTv.mk b/data/sounds/AudioTv.mk
index fd53aff73a50..d288e0e8f41d 100644
--- a/data/sounds/AudioTv.mk
+++ b/data/sounds/AudioTv.mk
@@ -15,7 +15,6 @@
LOCAL_PATH := frameworks/base/data/sounds
PRODUCT_COPY_FILES += \
- $(LOCAL_PATH)/Alarm_Beep_01.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/alarms/Alarm_Beep_02.ogg \
$(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/Effect_Tick.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressDelete.ogg \
$(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:$(TARGET_COPY_OUT_PRODUCT)/media/audio/ui/KeypressInvalid.ogg \
diff --git a/docs/html/reference/images/input_method/stylus_handwriting/delete_range_gesture_rects.png b/docs/html/reference/images/input_method/stylus_handwriting/delete_range_gesture_rects.png
new file mode 100644
index 000000000000..185582017ed6
--- /dev/null
+++ b/docs/html/reference/images/input_method/stylus_handwriting/delete_range_gesture_rects.png
Binary files differ
diff --git a/docs/html/reference/images/input_method/stylus_handwriting/select_range_gesture_rects.png b/docs/html/reference/images/input_method/stylus_handwriting/select_range_gesture_rects.png
new file mode 100644
index 000000000000..e0801267fe6d
--- /dev/null
+++ b/docs/html/reference/images/input_method/stylus_handwriting/select_range_gesture_rects.png
Binary files differ
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index a8ab6d9e494e..54d64280c6f7 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -670,8 +670,9 @@ public abstract class BaseCanvas {
/**
* @hide
*/
- public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
- nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
+ public void punchHole(float left, float top, float right, float bottom, float rx, float ry,
+ float alpha) {
+ nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, alpha);
}
/**
@@ -823,5 +824,5 @@ public abstract class BaseCanvas {
float hOffset, float vOffset, int flags, long nativePaint);
private static native void nPunchHole(long renderer, float left, float top, float right,
- float bottom, float rx, float ry);
+ float bottom, float rx, float ry, float alpha);
}
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index d06f665631cc..1ba79b87e87c 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -610,8 +610,9 @@ public class BaseRecordingCanvas extends Canvas {
* @hide
*/
@Override
- public void punchHole(float left, float top, float right, float bottom, float rx, float ry) {
- nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry);
+ public void punchHole(float left, float top, float right, float bottom, float rx, float ry,
+ float alpha) {
+ nPunchHole(mNativeCanvasWrapper, left, top, right, bottom, rx, ry, alpha);
}
@FastNative
@@ -742,5 +743,5 @@ public class BaseRecordingCanvas extends Canvas {
@FastNative
private static native void nPunchHole(long renderer, float left, float top, float right,
- float bottom, float rx, float ry);
+ float bottom, float rx, float ry, float alpha);
}
diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java
index ca3c84729388..31df474eb10c 100644
--- a/graphics/java/android/graphics/ColorSpace.java
+++ b/graphics/java/android/graphics/ColorSpace.java
@@ -24,7 +24,7 @@ import android.annotation.Size;
import android.annotation.SuppressAutoDoc;
import android.annotation.SuppressLint;
import android.hardware.DataSpace;
-import android.hardware.DataSpace.NamedDataSpace;
+import android.hardware.DataSpace.ColorDataSpace;
import android.util.SparseIntArray;
import libcore.util.NativeAllocationRegistry;
@@ -1406,7 +1406,7 @@ public abstract class ColorSpace {
*/
@SuppressLint("MethodNameUnits")
@Nullable
- public static ColorSpace getFromDataSpace(@NamedDataSpace int dataSpace) {
+ public static ColorSpace getFromDataSpace(@ColorDataSpace int dataSpace) {
int index = sDataToColorSpaces.get(dataSpace, -1);
if (index != -1) {
return ColorSpace.get(index);
@@ -1425,7 +1425,7 @@ public abstract class ColorSpace {
* @return the dataspace value.
*/
@SuppressLint("MethodNameUnits")
- public @NamedDataSpace int getDataSpace() {
+ public @ColorDataSpace int getDataSpace() {
int index = sDataToColorSpaces.indexOfValue(getId());
if (index != -1) {
return sDataToColorSpaces.keyAt(index);
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 0e67f1f4842a..c6731d112b93 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -47,9 +47,7 @@ import java.io.File;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.Optional;
import java.util.concurrent.Executor;
-import java.util.stream.Stream;
import sun.misc.Cleaner;
@@ -1142,6 +1140,16 @@ public class HardwareRenderer {
}
/**
+ * Sets whether or not the current process is a system or persistent process. Used to influence
+ * the chosen memory usage policy.
+ *
+ * @hide
+ **/
+ public static void setIsSystemOrPersistent() {
+ nSetIsSystemOrPersistent(true);
+ }
+
+ /**
* Returns true if HardwareRender will produce output.
*
* This value is global to the process and affects all uses of HardwareRenderer,
@@ -1204,30 +1212,6 @@ public class HardwareRenderer {
private static class ProcessInitializer {
static ProcessInitializer sInstance = new ProcessInitializer();
- // Magic values from android/data_space.h
- private static final int INTERNAL_DATASPACE_SRGB = 142671872;
- private static final int INTERNAL_DATASPACE_DISPLAY_P3 = 143261696;
- private static final int INTERNAL_DATASPACE_SCRGB = 411107328;
-
- private enum Dataspace {
- DISPLAY_P3(ColorSpace.Named.DISPLAY_P3, INTERNAL_DATASPACE_DISPLAY_P3),
- SCRGB(ColorSpace.Named.EXTENDED_SRGB, INTERNAL_DATASPACE_SCRGB),
- SRGB(ColorSpace.Named.SRGB, INTERNAL_DATASPACE_SRGB);
-
- private final ColorSpace.Named mColorSpace;
- private final int mNativeDataspace;
- Dataspace(ColorSpace.Named colorSpace, int nativeDataspace) {
- this.mColorSpace = colorSpace;
- this.mNativeDataspace = nativeDataspace;
- }
-
- static Optional<Dataspace> find(ColorSpace colorSpace) {
- return Stream.of(Dataspace.values())
- .filter(d -> ColorSpace.get(d.mColorSpace).equals(colorSpace))
- .findFirst();
- }
- }
-
private boolean mInitialized = false;
private boolean mDisplayInitialized = false;
@@ -1296,6 +1280,7 @@ public class HardwareRenderer {
initDisplayInfo();
nSetIsHighEndGfx(ActivityManager.isHighEndGfx());
+ nSetIsLowRam(ActivityManager.isLowRamDeviceStatic());
// Defensively clear out the context in case we were passed a context that can leak
// if we live longer than it, e.g. an activity context.
mContext = null;
@@ -1314,26 +1299,55 @@ public class HardwareRenderer {
return;
}
- Display display = dm.getDisplay(Display.DEFAULT_DISPLAY);
- if (display == null) {
+ final Display defaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
+ if (defaultDisplay == null) {
Log.d(LOG_TAG, "Failed to find default display for display-based configuration");
return;
}
- Dataspace wideColorDataspace =
- Optional.ofNullable(display.getPreferredWideGamutColorSpace())
- .flatMap(Dataspace::find)
- // Default to SRGB if the display doesn't support wide color
- .orElse(Dataspace.SRGB);
-
- // Grab the physical screen dimensions from the active display mode
- // Strictly speaking the screen resolution may not always be constant - it is for
- // sizing the font cache for the underlying rendering thread. Since it's a
- // heuristic we don't need to be always 100% correct.
- Mode activeMode = display.getMode();
- nInitDisplayInfo(activeMode.getPhysicalWidth(), activeMode.getPhysicalHeight(),
- display.getRefreshRate(), wideColorDataspace.mNativeDataspace,
- display.getAppVsyncOffsetNanos(), display.getPresentationDeadlineNanos());
+ final Display[] allDisplays = dm.getDisplays();
+ if (allDisplays.length == 0) {
+ Log.d(LOG_TAG, "Failed to query displays");
+ return;
+ }
+
+ final Mode activeMode = defaultDisplay.getMode();
+ final ColorSpace defaultWideColorSpace =
+ defaultDisplay.getPreferredWideGamutColorSpace();
+ int wideColorDataspace = defaultWideColorSpace != null
+ ? defaultWideColorSpace.getDataSpace() : 0;
+ // largest width & height are used to size the default HWUI cache sizes. So find the
+ // largest display resolution we could encounter & use that as the guidance. The actual
+ // memory policy in play will interpret these values differently.
+ int largestWidth = activeMode.getPhysicalWidth();
+ int largestHeight = activeMode.getPhysicalHeight();
+
+ for (int i = 0; i < allDisplays.length; i++) {
+ final Display display = allDisplays[i];
+ // Take the first wide gamut dataspace as the source of truth
+ // Possibly should do per-HardwareRenderer wide gamut dataspace so we can use the
+ // target display's ideal instead
+ if (wideColorDataspace == 0) {
+ ColorSpace cs = display.getPreferredWideGamutColorSpace();
+ if (cs != null) {
+ wideColorDataspace = cs.getDataSpace();
+ }
+ }
+ Mode[] modes = display.getSupportedModes();
+ for (int j = 0; j < modes.length; j++) {
+ Mode mode = modes[j];
+ int width = mode.getPhysicalWidth();
+ int height = mode.getPhysicalHeight();
+ if ((width * height) > (largestWidth * largestHeight)) {
+ largestWidth = width;
+ largestHeight = height;
+ }
+ }
+ }
+
+ nInitDisplayInfo(largestWidth, largestHeight, defaultDisplay.getRefreshRate(),
+ wideColorDataspace, defaultDisplay.getAppVsyncOffsetNanos(),
+ defaultDisplay.getPresentationDeadlineNanos());
mDisplayInitialized = true;
}
@@ -1418,6 +1432,10 @@ public class HardwareRenderer {
private static native void nSetIsHighEndGfx(boolean isHighEndGfx);
+ private static native void nSetIsLowRam(boolean isLowRam);
+
+ private static native void nSetIsSystemOrPersistent(boolean isSystemOrPersistent);
+
private static native int nSyncAndDrawFrame(long nativeProxy, long[] frameInfo, int size);
private static native void nDestroy(long nativeProxy, long rootRenderNode);
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index 25e1922fc38e..3b7d0e14893c 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -195,6 +195,8 @@ public class Typeface {
@UnsupportedAppUsage
public final long native_instance;
+ private final String mSystemFontFamilyName;
+
private final Runnable mCleaner;
/** @hide */
@@ -272,6 +274,14 @@ public class Typeface {
}
/**
+ * Returns the system font family name if the typeface was created from a system font family,
+ * otherwise returns null.
+ */
+ public final @Nullable String getSystemFontFamilyName() {
+ return mSystemFontFamilyName;
+ }
+
+ /**
* Returns true if the system has the font family with the name [familyName]. For example
* querying with "sans-serif" would check if the "sans-serif" family is defined in the system
* and return true if does.
@@ -870,7 +880,7 @@ public class Typeface {
final int italic =
(mStyle == null || mStyle.getSlant() == FontStyle.FONT_SLANT_UPRIGHT) ? 0 : 1;
return new Typeface(nativeCreateFromArray(
- ptrArray, fallbackTypeface.native_instance, weight, italic));
+ ptrArray, fallbackTypeface.native_instance, weight, italic), null);
}
}
@@ -935,7 +945,8 @@ public class Typeface {
}
}
- typeface = new Typeface(nativeCreateFromTypeface(ni, style));
+ typeface = new Typeface(nativeCreateFromTypeface(ni, style),
+ family.getSystemFontFamilyName());
styles.put(style, typeface);
}
return typeface;
@@ -1003,7 +1014,8 @@ public class Typeface {
}
typeface = new Typeface(
- nativeCreateFromTypefaceWithExactStyle(base.native_instance, weight, italic));
+ nativeCreateFromTypefaceWithExactStyle(base.native_instance, weight, italic),
+ base.getSystemFontFamilyName());
innerCache.put(key, typeface);
}
return typeface;
@@ -1013,7 +1025,10 @@ public class Typeface {
public static Typeface createFromTypefaceWithVariation(@Nullable Typeface family,
@NonNull List<FontVariationAxis> axes) {
final Typeface base = family == null ? Typeface.DEFAULT : family;
- return new Typeface(nativeCreateFromTypefaceWithVariation(base.native_instance, axes));
+ Typeface typeface = new Typeface(
+ nativeCreateFromTypefaceWithVariation(base.native_instance, axes),
+ base.getSystemFontFamilyName());
+ return typeface;
}
/**
@@ -1108,7 +1123,7 @@ public class Typeface {
}
return new Typeface(nativeCreateFromArray(
ptrArray, 0, RESOLVE_BY_FONT_TABLE,
- RESOLVE_BY_FONT_TABLE));
+ RESOLVE_BY_FONT_TABLE), null);
}
/**
@@ -1116,13 +1131,14 @@ public class Typeface {
*
* @param families array of font families
*/
- private static Typeface createFromFamilies(@Nullable FontFamily[] families) {
+ private static Typeface createFromFamilies(@NonNull String familyName,
+ @Nullable FontFamily[] families) {
final long[] ptrArray = new long[families.length];
for (int i = 0; i < families.length; ++i) {
ptrArray[i] = families[i].getNativePtr();
}
return new Typeface(nativeCreateFromArray(ptrArray, 0,
- RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE));
+ RESOLVE_BY_FONT_TABLE, RESOLVE_BY_FONT_TABLE), familyName);
}
/**
@@ -1162,12 +1178,17 @@ public class Typeface {
ptrArray[i] = families[i].mNativePtr;
}
return new Typeface(nativeCreateFromArray(
- ptrArray, fallbackTypeface.native_instance, weight, italic));
+ ptrArray, fallbackTypeface.native_instance, weight, italic), null);
}
// don't allow clients to call this directly
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private Typeface(long ni) {
+ this(ni, null);
+ }
+
+ // don't allow clients to call this directly
+ private Typeface(long ni, @Nullable String systemFontFamilyName) {
if (ni == 0) {
throw new RuntimeException("native typeface cannot be made");
}
@@ -1176,6 +1197,7 @@ public class Typeface {
mCleaner = sRegistry.registerNativeAllocation(this, native_instance);
mStyle = nativeGetStyle(ni);
mWeight = nativeGetWeight(ni);
+ mSystemFontFamilyName = systemFontFamilyName;
}
/**
@@ -1200,7 +1222,8 @@ public class Typeface {
List<FontConfig.Alias> aliases,
Map<String, Typeface> outSystemFontMap) {
for (Map.Entry<String, FontFamily[]> entry : fallbacks.entrySet()) {
- outSystemFontMap.put(entry.getKey(), createFromFamilies(entry.getValue()));
+ outSystemFontMap.put(entry.getKey(),
+ createFromFamilies(entry.getKey(), entry.getValue()));
}
for (int i = 0; i < aliases.size(); ++i) {
@@ -1215,8 +1238,8 @@ public class Typeface {
continue;
}
final int weight = alias.getWeight();
- final Typeface newFace = weight == 400 ? base :
- new Typeface(nativeCreateWeightAlias(base.native_instance, weight));
+ final Typeface newFace = weight == 400 ? base : new Typeface(
+ nativeCreateWeightAlias(base.native_instance, weight), alias.getName());
outSystemFontMap.put(alias.getName(), newFace);
}
}
@@ -1288,7 +1311,7 @@ public class Typeface {
buffer.position(buffer.position() + typefacesBytesCount);
for (long nativePtr : nativePtrs) {
String name = readString(buffer);
- out.put(name, new Typeface(nativePtr));
+ out.put(name, new Typeface(nativePtr, name));
}
return nativePtrs;
}
diff --git a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
index 47de37d76167..688425a77ab5 100644
--- a/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
+++ b/graphics/java/android/graphics/drawable/AdaptiveIconDrawable.java
@@ -26,6 +26,8 @@ import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
import android.graphics.BlendMode;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -37,6 +39,8 @@ import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.PathParser;
@@ -102,8 +106,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
private static final float DEFAULT_VIEW_PORT_SCALE = 1f / (1 + 2 * EXTRA_INSET_PERCENTAGE);
/**
- * Unused path.
- * TODO: Remove once the layoutLib is updated
+ * Clip path defined in R.string.config_icon_mask.
*/
private static Path sMask;
@@ -111,10 +114,9 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
* Scaled mask based on the view bounds.
*/
private final Path mMask;
- private final Path mMaskTransformed;
+ private final Path mMaskScaleOnly;
private final Matrix mMaskMatrix;
private final Region mTransparentRegion;
- private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
/**
* Indices used to access {@link #mLayerState.mChildDrawable} array for foreground and
@@ -129,10 +131,19 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
*/
LayerState mLayerState;
+ private Shader mLayersShader;
+ private Bitmap mLayersBitmap;
+
private final Rect mTmpOutRect = new Rect();
private Rect mHotspotBounds;
private boolean mMutated;
+ private boolean mSuspendChildInvalidation;
+ private boolean mChildRequestedInvalidation;
+ private final Canvas mCanvas;
+ private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG |
+ Paint.FILTER_BITMAP_FLAG);
+
/**
* Constructor used for xml inflation.
*/
@@ -146,16 +157,19 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
*/
AdaptiveIconDrawable(@Nullable LayerState state, @Nullable Resources res) {
mLayerState = createConstantState(state, res);
- // config_icon_mask from context bound resource may have been changed using
+ // config_icon_mask from context bound resource may have been chaged using
// OverlayManager. Read that one first.
Resources r = ActivityThread.currentActivityThread() == null
? Resources.getSystem()
: ActivityThread.currentActivityThread().getApplication().getResources();
- mMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
- mMaskTransformed = new Path();
+ // TODO: either make sMask update only when config_icon_mask changes OR
+ // get rid of it all-together in layoutlib
+ sMask = PathParser.createPathFromPathData(r.getString(R.string.config_icon_mask));
+ mMask = new Path(sMask);
+ mMaskScaleOnly = new Path(mMask);
mMaskMatrix = new Matrix();
+ mCanvas = new Canvas();
mTransparentRegion = new Region();
- mPaint.setColor(Color.BLACK);
}
private ChildDrawable createChildDrawable(Drawable drawable) {
@@ -266,7 +280,7 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
* @return the mask path object used to clip the drawable
*/
public Path getIconMask() {
- return mMaskTransformed;
+ return mMask;
}
/**
@@ -308,47 +322,92 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
if (bounds.isEmpty()) {
return;
}
- // Set the child layer bounds bigger than the view port size
- // by {@link #DEFAULT_VIEW_PORT_SCALE}
- float cX = bounds.exactCenterX();
- float cY = bounds.exactCenterY();
- float insetWidth = bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2);
- float insetHeight = bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2);
- final Rect outRect = mTmpOutRect;
- outRect.set(
- (int) (cX - insetWidth),
- (int) (cY - insetHeight),
- (int) (cX + insetWidth),
- (int) (cY + insetHeight));
+ updateLayerBounds(bounds);
+ }
+
+ private void updateLayerBounds(Rect bounds) {
+ if (bounds.isEmpty()) {
+ return;
+ }
+ try {
+ suspendChildInvalidation();
+ updateLayerBoundsInternal(bounds);
+ updateMaskBoundsInternal(bounds);
+ } finally {
+ resumeChildInvalidation();
+ }
+ }
+
+ /**
+ * Set the child layer bounds bigger than the view port size by {@link #DEFAULT_VIEW_PORT_SCALE}
+ */
+ private void updateLayerBoundsInternal(Rect bounds) {
+ int cX = bounds.width() / 2;
+ int cY = bounds.height() / 2;
for (int i = 0, count = mLayerState.N_CHILDREN; i < count; i++) {
final ChildDrawable r = mLayerState.mChildren[i];
- if (r.mDrawable != null) {
- r.mDrawable.setBounds(outRect);
+ final Drawable d = r.mDrawable;
+ if (d == null) {
+ continue;
}
+
+ int insetWidth = (int) (bounds.width() / (DEFAULT_VIEW_PORT_SCALE * 2));
+ int insetHeight = (int) (bounds.height() / (DEFAULT_VIEW_PORT_SCALE * 2));
+ final Rect outRect = mTmpOutRect;
+ outRect.set(cX - insetWidth, cY - insetHeight, cX + insetWidth, cY + insetHeight);
+
+ d.setBounds(outRect);
}
+ }
+
+ private void updateMaskBoundsInternal(Rect b) {
+ // reset everything that depends on the view bounds
+ mMaskMatrix.setScale(b.width() / MASK_SIZE, b.height() / MASK_SIZE);
+ sMask.transform(mMaskMatrix, mMaskScaleOnly);
- // Update the clipping mask
- mMaskMatrix.setScale(bounds.width() / MASK_SIZE, bounds.height() / MASK_SIZE);
- mMaskMatrix.postTranslate(bounds.left, bounds.top);
- mMask.transform(mMaskMatrix, mMaskTransformed);
+ mMaskMatrix.postTranslate(b.left, b.top);
+ sMask.transform(mMaskMatrix, mMask);
- // Clear the transparent region, it is calculated lazily
+ if (mLayersBitmap == null || mLayersBitmap.getWidth() != b.width()
+ || mLayersBitmap.getHeight() != b.height()) {
+ mLayersBitmap = Bitmap.createBitmap(b.width(), b.height(), Bitmap.Config.ARGB_8888);
+ }
+
+ mPaint.setShader(null);
mTransparentRegion.setEmpty();
+ mLayersShader = null;
}
@Override
public void draw(Canvas canvas) {
- int saveCount = canvas.save();
- canvas.clipPath(mMaskTransformed);
- canvas.drawPaint(mPaint);
- if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) {
- mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(canvas);
+ if (mLayersBitmap == null) {
+ return;
}
- if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) {
- mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(canvas);
+ if (mLayersShader == null) {
+ mCanvas.setBitmap(mLayersBitmap);
+ mCanvas.drawColor(Color.BLACK);
+ if (mLayerState.mChildren[BACKGROUND_ID].mDrawable != null) {
+ mLayerState.mChildren[BACKGROUND_ID].mDrawable.draw(mCanvas);
+ }
+ if (mLayerState.mChildren[FOREGROUND_ID].mDrawable != null) {
+ mLayerState.mChildren[FOREGROUND_ID].mDrawable.draw(mCanvas);
+ }
+ mLayersShader = new BitmapShader(mLayersBitmap, TileMode.CLAMP, TileMode.CLAMP);
+ mPaint.setShader(mLayersShader);
+ }
+ if (mMaskScaleOnly != null) {
+ Rect bounds = getBounds();
+ canvas.translate(bounds.left, bounds.top);
+ canvas.drawPath(mMaskScaleOnly, mPaint);
+ canvas.translate(-bounds.left, -bounds.top);
}
- canvas.restoreToCount(saveCount);
+ }
+
+ @Override
+ public void invalidateSelf() {
+ mLayersShader = null;
+ super.invalidateSelf();
}
@Override
@@ -541,9 +600,37 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
return false;
}
+ /**
+ * Temporarily suspends child invalidation.
+ *
+ * @see #resumeChildInvalidation()
+ */
+ private void suspendChildInvalidation() {
+ mSuspendChildInvalidation = true;
+ }
+
+ /**
+ * Resumes child invalidation after suspension, immediately performing an
+ * invalidation if one was requested by a child during suspension.
+ *
+ * @see #suspendChildInvalidation()
+ */
+ private void resumeChildInvalidation() {
+ mSuspendChildInvalidation = false;
+
+ if (mChildRequestedInvalidation) {
+ mChildRequestedInvalidation = false;
+ invalidateSelf();
+ }
+ }
+
@Override
public void invalidateDrawable(@NonNull Drawable who) {
- invalidateSelf();
+ if (mSuspendChildInvalidation) {
+ mChildRequestedInvalidation = true;
+ } else {
+ invalidateSelf();
+ }
}
@Override
@@ -627,13 +714,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
@Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
- final ChildDrawable[] array = mLayerState.mChildren;
- for (int i = 0; i < mLayerState.N_CHILDREN; i++) {
- final Drawable dr = array[i].mDrawable;
- if (dr != null) {
- dr.setAlpha(alpha);
- }
- }
}
@Override
@@ -736,6 +816,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
}
}
+ if (changed) {
+ updateLayerBounds(getBounds());
+ }
+
return changed;
}
@@ -751,6 +835,10 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
}
}
+ if (changed) {
+ updateLayerBounds(getBounds());
+ }
+
return changed;
}
@@ -891,7 +979,6 @@ public class AdaptiveIconDrawable extends Drawable implements Drawable.Callback
int mDensity;
// The density to use when inflating/looking up the children drawables. A value of 0 means
-
// use the system's density.
int mSrcDensityOverride = 0;
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index d9a7994d6c4a..dbd918e35d70 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -29,6 +29,8 @@ import libcore.util.EmptyArray;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.MGF1ParameterSpec;
import java.util.Collection;
import java.util.Locale;
@@ -675,6 +677,26 @@ public abstract class KeyProperties {
}
}
+ /**
+ * @hide
+ */
+ @NonNull public static @DigestEnum
+ AlgorithmParameterSpec fromKeymasterToMGF1ParameterSpec(int digest) {
+ switch (digest) {
+ default:
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ return MGF1ParameterSpec.SHA1;
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ return MGF1ParameterSpec.SHA224;
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ return MGF1ParameterSpec.SHA256;
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ return MGF1ParameterSpec.SHA384;
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ return MGF1ParameterSpec.SHA512;
+ }
+ }
+
@NonNull
public static @DigestEnum String fromKeymasterToSignatureAlgorithmDigest(int digest) {
switch (digest) {
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
index e808c5cc51bd..7571e44a7713 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreCipherSpiBase.java
@@ -69,6 +69,7 @@ import javax.crypto.spec.SecretKeySpec;
*/
abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStoreCryptoOperation {
private static final String TAG = "AndroidKeyStoreCipherSpiBase";
+ public static final String DEFAULT_MGF1_DIGEST = "SHA-1";
// Fields below are populated by Cipher.init and KeyStore.begin and should be preserved after
// doFinal finishes.
@@ -133,24 +134,28 @@ abstract class AndroidKeyStoreCipherSpiBase extends CipherSpi implements KeyStor
if ("RSA/ECB/OAEPWithSHA-224AndMGF1Padding".equals(transform)) {
OAEPParameterSpec spec =
new OAEPParameterSpec("SHA-224", "MGF1",
- new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ new MGF1ParameterSpec(DEFAULT_MGF1_DIGEST),
+ PSource.PSpecified.DEFAULT);
mCipher.init(opmode, key, spec, random);
} else if ("RSA/ECB/OAEPWithSHA-256AndMGF1Padding".equals(transform)) {
OAEPParameterSpec spec =
new OAEPParameterSpec("SHA-256", "MGF1",
- new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ new MGF1ParameterSpec(DEFAULT_MGF1_DIGEST),
+ PSource.PSpecified.DEFAULT);
mCipher.init(opmode, key, spec, random);
} else if ("RSA/ECB/OAEPWithSHA-384AndMGF1Padding".equals(transform)) {
OAEPParameterSpec spec =
new OAEPParameterSpec("SHA-384", "MGF1",
- new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ new MGF1ParameterSpec(DEFAULT_MGF1_DIGEST),
+ PSource.PSpecified.DEFAULT);
mCipher.init(opmode, key, spec, random);
} else if ("RSA/ECB/OAEPWithSHA-512AndMGF1Padding".equals(transform)) {
OAEPParameterSpec spec =
new OAEPParameterSpec("SHA-512", "MGF1",
- new MGF1ParameterSpec("SHA1"), PSource.PSpecified.DEFAULT);
+ new MGF1ParameterSpec(DEFAULT_MGF1_DIGEST),
+ PSource.PSpecified.DEFAULT);
mCipher.init(opmode, key, spec, random);
} else {
mCipher.init(opmode, key, random);
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index cdc1085a5015..acc0005154b4 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -16,6 +16,8 @@
package android.security.keystore2;
+import static android.security.keystore2.AndroidKeyStoreCipherSpiBase.DEFAULT_MGF1_DIGEST;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityThread;
@@ -908,6 +910,26 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
params.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_PADDING, padding
));
+ if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) {
+ final boolean[] hasDefaultMgf1DigestBeenAdded = {false};
+ ArrayUtils.forEach(mKeymasterDigests, (digest) -> {
+ params.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, digest
+ ));
+ hasDefaultMgf1DigestBeenAdded[0] |=
+ digest.equals(KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST));
+ });
+ /* Because of default MGF1 digest is SHA-1. It has to be added in Key
+ * characteristics. Otherwise, crypto operations will fail with Incompatible
+ * MGF1 digest.
+ */
+ if (!hasDefaultMgf1DigestBeenAdded[0]) {
+ params.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
+ KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
+ ));
+ }
+ }
});
ArrayUtils.forEach(mKeymasterSignaturePaddings, (padding) -> {
params.add(KeyStore2ParameterUtils.makeEnum(
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
index 5848247809e7..e9b66aafc262 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreRSACipherSpi.java
@@ -161,10 +161,11 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
*/
abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi {
- private static final String MGF_ALGORITGM_MGF1 = "MGF1";
+ private static final String MGF_ALGORITHM_MGF1 = "MGF1";
private int mKeymasterDigest = -1;
private int mDigestOutputSizeBytes;
+ private int mKeymasterMgf1Digest = KeymasterDefs.KM_DIGEST_SHA1; // Default MGF1 digest
OAEPWithMGF1Padding(int keymasterDigest) {
super(KeymasterDefs.KM_PAD_RSA_OAEP);
@@ -189,10 +190,10 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
+ ". Only OAEPParameterSpec supported");
}
OAEPParameterSpec spec = (OAEPParameterSpec) params;
- if (!MGF_ALGORITGM_MGF1.equalsIgnoreCase(spec.getMGFAlgorithm())) {
+ if (!MGF_ALGORITHM_MGF1.equalsIgnoreCase(spec.getMGFAlgorithm())) {
throw new InvalidAlgorithmParameterException(
"Unsupported MGF: " + spec.getMGFAlgorithm()
- + ". Only " + MGF_ALGORITGM_MGF1 + " supported");
+ + ". Only " + MGF_ALGORITHM_MGF1 + " supported");
}
String jcaDigest = spec.getDigestAlgorithm();
int keymasterDigest;
@@ -225,11 +226,6 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
}
MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) mgfParams;
String mgf1JcaDigest = mgfSpec.getDigestAlgorithm();
- if (!KeyProperties.DIGEST_SHA1.equalsIgnoreCase(mgf1JcaDigest)) {
- throw new InvalidAlgorithmParameterException(
- "Unsupported MGF1 digest: " + mgf1JcaDigest
- + ". Only " + KeyProperties.DIGEST_SHA1 + " supported");
- }
PSource pSource = spec.getPSource();
if (!(pSource instanceof PSource.PSpecified)) {
throw new InvalidAlgorithmParameterException(
@@ -244,6 +240,7 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
+ ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
}
mKeymasterDigest = keymasterDigest;
+ mKeymasterMgf1Digest = KeyProperties.Digest.toKeymaster(mgf1JcaDigest);
mDigestOutputSizeBytes =
(KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
}
@@ -273,10 +270,10 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
protected final AlgorithmParameters engineGetParameters() {
OAEPParameterSpec spec =
new OAEPParameterSpec(
- KeyProperties.Digest.fromKeymaster(mKeymasterDigest),
- MGF_ALGORITGM_MGF1,
- MGF1ParameterSpec.SHA1,
- PSource.PSpecified.DEFAULT);
+ KeyProperties.Digest.fromKeymaster(mKeymasterDigest),
+ MGF_ALGORITHM_MGF1,
+ KeyProperties.Digest.fromKeymasterToMGF1ParameterSpec(mKeymasterMgf1Digest),
+ PSource.PSpecified.DEFAULT);
try {
AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP");
params.init(spec);
@@ -298,6 +295,9 @@ abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase
parameters.add(KeyStore2ParameterUtils.makeEnum(
KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest
));
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST, mKeymasterMgf1Digest
+ ));
}
@Override
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
index dfda356ec35b..94bf122877bd 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreSpi.java
@@ -16,6 +16,8 @@
package android.security.keystore2;
+import static android.security.keystore2.AndroidKeyStoreCipherSpiBase.DEFAULT_MGF1_DIGEST;
+
import android.annotation.NonNull;
import android.hardware.biometrics.BiometricManager;
import android.hardware.security.keymint.HardwareAuthenticatorType;
@@ -511,6 +513,28 @@ public class AndroidKeyStoreSpi extends KeyStoreSpi {
KeymasterDefs.KM_TAG_PADDING,
padding
));
+ if (padding == KeymasterDefs.KM_PAD_RSA_OAEP) {
+ if (spec.isDigestsSpecified()) {
+ boolean hasDefaultMgf1DigestBeenAdded = false;
+ for (String digest : spec.getDigests()) {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
+ KeyProperties.Digest.toKeymaster(digest)
+ ));
+ hasDefaultMgf1DigestBeenAdded |= digest.equals(DEFAULT_MGF1_DIGEST);
+ }
+ /* Because of default MGF1 digest is SHA-1. It has to be added in Key
+ * characteristics. Otherwise, crypto operations will fail with Incompatible
+ * MGF1 digest.
+ */
+ if (!hasDefaultMgf1DigestBeenAdded) {
+ importArgs.add(KeyStore2ParameterUtils.makeEnum(
+ KeymasterDefs.KM_TAG_RSA_OAEP_MGF_DIGEST,
+ KeyProperties.Digest.toKeymaster(DEFAULT_MGF1_DIGEST)
+ ));
+ }
+ }
+ }
}
for (String padding : spec.getSignaturePaddings()) {
importArgs.add(KeyStore2ParameterUtils.makeEnum(
diff --git a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
index dcdd7defd752..54955c6b7fab 100644
--- a/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStore2ParameterUtils.java
@@ -78,6 +78,7 @@ public abstract class KeyStore2ParameterUtils {
kp.value = KeyParameterValue.blockMode(v);
break;
case Tag.DIGEST:
+ case Tag.RSA_OAEP_MGF_DIGEST:
kp.value = KeyParameterValue.digest(v);
break;
case Tag.EC_CURVE:
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
index 7e9c4189dabb..fb0a9db6a20b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java
@@ -41,7 +41,7 @@ public class WindowExtensionsImpl implements WindowExtensions {
// TODO(b/241126279) Introduce constants to better version functionality
@Override
public int getVendorApiLevel() {
- return 2;
+ return 1;
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
index febd7917dff9..74303e2fab7c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizer.java
@@ -31,6 +31,7 @@ import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
+import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -93,6 +94,7 @@ class JetpackTaskFragmentOrganizer extends TaskFragmentOrganizer {
}
/** No longer overrides the animation if the transition is on the given Task. */
+ @GuardedBy("mLock")
void stopOverrideSplitAnimation(int taskId) {
if (mAnimationController != null) {
mAnimationController.unregisterRemoteAnimations(taskId);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
index c8ac0fc73ff9..00be5a6e3416 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitContainer.java
@@ -17,8 +17,10 @@
package androidx.window.extensions.embedding;
import android.app.Activity;
+import android.content.res.Configuration;
import android.util.Pair;
import android.util.Size;
+import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -32,14 +34,18 @@ class SplitContainer {
private final TaskFragmentContainer mSecondaryContainer;
@NonNull
private final SplitRule mSplitRule;
+ @NonNull
+ private SplitAttributes mSplitAttributes;
SplitContainer(@NonNull TaskFragmentContainer primaryContainer,
@NonNull Activity primaryActivity,
@NonNull TaskFragmentContainer secondaryContainer,
- @NonNull SplitRule splitRule) {
+ @NonNull SplitRule splitRule,
+ @NonNull SplitAttributes splitAttributes) {
mPrimaryContainer = primaryContainer;
mSecondaryContainer = secondaryContainer;
mSplitRule = splitRule;
+ mSplitAttributes = splitAttributes;
if (shouldFinishPrimaryWithSecondary(splitRule)) {
if (mPrimaryContainer.getRunningActivityCount() == 1
@@ -72,6 +78,26 @@ class SplitContainer {
return mSplitRule;
}
+ @NonNull
+ SplitAttributes getSplitAttributes() {
+ return mSplitAttributes;
+ }
+
+ /**
+ * Updates the {@link SplitAttributes} to this container.
+ * It is usually used when there's a folding state change or
+ * {@link SplitController#onTaskFragmentParentInfoChanged(WindowContainerTransaction, int,
+ * Configuration)}.
+ */
+ void setSplitAttributes(@NonNull SplitAttributes splitAttributes) {
+ mSplitAttributes = splitAttributes;
+ }
+
+ @NonNull
+ TaskContainer getTaskContainer() {
+ return getPrimaryContainer().getTaskContainer();
+ }
+
/** Returns the minimum dimension pair of primary container and secondary container. */
@NonNull
Pair<Size, Size> getMinDimensionsPair() {
@@ -141,6 +167,7 @@ class SplitContainer {
+ " primaryContainer=" + mPrimaryContainer
+ " secondaryContainer=" + mSecondaryContainer
+ " splitRule=" + mSplitRule
+ + " splitAttributes" + mSplitAttributes
+ "}";
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 6af3d2bf4915..203ece091e46 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -19,6 +19,7 @@ package androidx.window.extensions.embedding;
import static android.app.ActivityManager.START_SUCCESS;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_TASK_FRAGMENT_INFO;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
@@ -40,12 +41,13 @@ import static androidx.window.extensions.embedding.SplitContainer.shouldFinishAs
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPAND_FAILED_NO_TF_INFO;
import static androidx.window.extensions.embedding.SplitPresenter.getActivityIntentMinDimensionsPair;
import static androidx.window.extensions.embedding.SplitPresenter.getNonEmbeddedActivityBounds;
-import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSideBySide;
+import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSplit;
import android.app.Activity;
import android.app.ActivityClient;
import android.app.ActivityOptions;
import android.app.ActivityThread;
+import android.app.Application;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
@@ -62,19 +64,25 @@ import android.util.Log;
import android.util.Pair;
import android.util.Size;
import android.util.SparseArray;
+import android.view.WindowMetrics;
import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
import androidx.annotation.GuardedBy;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
+import androidx.window.extensions.WindowExtensionsProvider;
+import androidx.window.extensions.layout.WindowLayoutComponentImpl;
import com.android.internal.annotations.VisibleForTesting;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -96,6 +104,23 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
private final List<EmbeddingRule> mSplitRules = new ArrayList<>();
/**
+ * A developer-defined {@link SplitAttributes} calculator to compute the current
+ * {@link SplitAttributes} with the current device and window states.
+ * It is registered via {@link #setSplitAttributesCalculator(SplitAttributesCalculator)}
+ * and unregistered via {@link #clearSplitAttributesCalculator()}.
+ * This is called when:
+ * <ul>
+ * <li>{@link SplitPresenter#updateSplitContainer(SplitContainer, TaskFragmentContainer,
+ * WindowContainerTransaction)}</li>
+ * <li>There's a started Activity which matches {@link SplitPairRule} </li>
+ * <li>Checking whether the place holder should be launched if there's a Activity matches
+ * {@link SplitPlaceholderRule} </li>
+ * </ul>
+ */
+ @GuardedBy("mLock")
+ @Nullable
+ private SplitAttributesCalculator mSplitAttributesCalculator;
+ /**
* Map from Task id to {@link TaskContainer} which contains all TaskFragment and split pair info
* below it.
* When the app is host of multiple Tasks, there can be multiple splits controlled by the same
@@ -105,26 +130,65 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@GuardedBy("mLock")
final SparseArray<TaskContainer> mTaskContainers = new SparseArray<>();
- // Callback to Jetpack to notify about changes to split states.
- @NonNull
+ /** Callback to Jetpack to notify about changes to split states. */
+ @Nullable
private Consumer<List<SplitInfo>> mEmbeddingCallback;
private final List<SplitInfo> mLastReportedSplitStates = new ArrayList<>();
private final Handler mHandler;
final Object mLock = new Object();
private final ActivityStartMonitor mActivityStartMonitor;
+ @NonNull
+ final WindowLayoutComponentImpl mWindowLayoutComponent;
public SplitController() {
+ this((WindowLayoutComponentImpl) Objects.requireNonNull(WindowExtensionsProvider
+ .getWindowExtensions().getWindowLayoutComponent()));
+ }
+
+ @VisibleForTesting
+ SplitController(@NonNull WindowLayoutComponentImpl windowLayoutComponent) {
final MainThreadExecutor executor = new MainThreadExecutor();
mHandler = executor.mHandler;
mPresenter = new SplitPresenter(executor, this);
- ActivityThread activityThread = ActivityThread.currentActivityThread();
+ final ActivityThread activityThread = ActivityThread.currentActivityThread();
+ final Application application = activityThread.getApplication();
// Register a callback to be notified about activities being created.
- activityThread.getApplication().registerActivityLifecycleCallbacks(
- new LifecycleCallbacks());
+ application.registerActivityLifecycleCallbacks(new LifecycleCallbacks());
// Intercept activity starts to route activities to new containers if necessary.
Instrumentation instrumentation = activityThread.getInstrumentation();
+
mActivityStartMonitor = new ActivityStartMonitor();
instrumentation.addMonitor(mActivityStartMonitor);
+ mWindowLayoutComponent = windowLayoutComponent;
+ mWindowLayoutComponent.addFoldingStateChangedCallback(new FoldingFeatureListener());
+ }
+
+ private class FoldingFeatureListener implements Consumer<List<CommonFoldingFeature>> {
+ @Override
+ public void accept(List<CommonFoldingFeature> foldingFeatures) {
+ synchronized (mLock) {
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ for (int i = 0; i < mTaskContainers.size(); i++) {
+ final TaskContainer taskContainer = mTaskContainers.valueAt(i);
+ if (!taskContainer.isVisible()) {
+ continue;
+ }
+ if (taskContainer.getDisplayId() != DEFAULT_DISPLAY) {
+ continue;
+ }
+ // TODO(b/238948678): Support reporting display features in all windowing modes.
+ if (taskContainer.isInMultiWindow()) {
+ continue;
+ }
+ if (taskContainer.isEmpty()) {
+ continue;
+ }
+ updateContainersInTask(wct, taskContainer);
+ updateAnimationOverride(taskContainer);
+ }
+ mPresenter.applyTransaction(wct);
+ }
+ }
}
/** Updates the embedding rules applied to future activity launches. */
@@ -139,6 +203,26 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
+ @Override
+ public void setSplitAttributesCalculator(@NonNull SplitAttributesCalculator calculator) {
+ synchronized (mLock) {
+ mSplitAttributesCalculator = calculator;
+ }
+ }
+
+ @Override
+ public void clearSplitAttributesCalculator() {
+ synchronized (mLock) {
+ mSplitAttributesCalculator = null;
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ SplitAttributesCalculator getSplitAttributesCalculator() {
+ return mSplitAttributesCalculator;
+ }
+
@NonNull
List<EmbeddingRule> getSplitRules() {
return mSplitRules;
@@ -181,7 +265,8 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
onTaskFragmentVanished(wct, info);
break;
case TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED:
- onTaskFragmentParentInfoChanged(wct, taskId, change.getTaskConfiguration());
+ onTaskFragmentParentInfoChanged(wct, taskId,
+ change.getTaskFragmentParentInfo());
break;
case TYPE_TASK_FRAGMENT_ERROR:
final Bundle errorBundle = change.getErrorBundle();
@@ -336,22 +421,33 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*
* @param wct The {@link WindowContainerTransaction} to make any changes with if needed.
* @param taskId Id of the parent Task that is changed.
- * @param parentConfig Config of the parent Task.
+ * @param parentInfo {@link TaskFragmentParentInfo} of the parent Task.
*/
@VisibleForTesting
@GuardedBy("mLock")
void onTaskFragmentParentInfoChanged(@NonNull WindowContainerTransaction wct,
- int taskId, @NonNull Configuration parentConfig) {
- onTaskConfigurationChanged(taskId, parentConfig);
- if (isInPictureInPicture(parentConfig)) {
- // No need to update presentation in PIP until the Task exit PIP.
- return;
- }
+ int taskId, @NonNull TaskFragmentParentInfo parentInfo) {
final TaskContainer taskContainer = getTaskContainer(taskId);
if (taskContainer == null || taskContainer.isEmpty()) {
Log.e(TAG, "onTaskFragmentParentInfoChanged on empty Task id=" + taskId);
return;
}
+ taskContainer.updateTaskFragmentParentInfo(parentInfo);
+ if (!taskContainer.isVisible()) {
+ // Don't update containers if the task is not visible. We only update containers when
+ // parentInfo#isVisibleRequested is true.
+ return;
+ }
+ onTaskContainerInfoChanged(taskContainer, parentInfo.getConfiguration());
+ if (isInPictureInPicture(parentInfo.getConfiguration())) {
+ // No need to update presentation in PIP until the Task exit PIP.
+ return;
+ }
+ updateContainersInTask(wct, taskContainer);
+ }
+
+ private void updateContainersInTask(@NonNull WindowContainerTransaction wct,
+ @NonNull TaskContainer taskContainer) {
// Update all TaskFragments in the Task. Make a copy of the list since some may be
// removed on updating.
final List<TaskFragmentContainer> containers =
@@ -476,6 +572,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
/** Called on receiving {@link #onTaskFragmentVanished} for cleanup. */
+ @GuardedBy("mLock")
private void cleanupTaskFragment(@NonNull IBinder taskFragmentToken) {
for (int i = mTaskContainers.size() - 1; i >= 0; i--) {
final TaskContainer taskContainer = mTaskContainers.valueAt(i);
@@ -491,14 +588,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
}
- private void onTaskConfigurationChanged(int taskId, @NonNull Configuration config) {
- final TaskContainer taskContainer = mTaskContainers.get(taskId);
- if (taskContainer == null) {
- return;
- }
+ @GuardedBy("mLock")
+ private void onTaskContainerInfoChanged(@NonNull TaskContainer taskContainer,
+ @NonNull Configuration config) {
final boolean wasInPip = taskContainer.isInPictureInPicture();
final boolean isInPIp = isInPictureInPicture(config);
- taskContainer.setWindowingMode(config.windowConfiguration.getWindowingMode());
// We need to check the animation override when enter/exit PIP or has bounds changed.
boolean shouldUpdateAnimationOverride = wasInPip != isInPIp;
@@ -516,37 +610,49 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Updates if we should override transition animation. We only want to override if the Task
* bounds is large enough for at least one split rule.
*/
+ @GuardedBy("mLock")
private void updateAnimationOverride(@NonNull TaskContainer taskContainer) {
if (ENABLE_SHELL_TRANSITIONS) {
// TODO(b/207070762): cleanup with legacy app transition
// Animation will be handled by WM Shell with Shell transition enabled.
return;
}
- if (!taskContainer.isTaskBoundsInitialized()
- || !taskContainer.isWindowingModeInitialized()) {
+ if (!taskContainer.isTaskBoundsInitialized()) {
// We don't know about the Task bounds/windowingMode yet.
return;
}
- // We only want to override if it supports split.
- if (supportSplit(taskContainer)) {
+ // We only want to override if the TaskContainer may show split.
+ if (mayShowSplit(taskContainer)) {
mPresenter.startOverrideSplitAnimation(taskContainer.getTaskId());
} else {
mPresenter.stopOverrideSplitAnimation(taskContainer.getTaskId());
}
}
- private boolean supportSplit(@NonNull TaskContainer taskContainer) {
+ /** Returns whether the given {@link TaskContainer} may show in split. */
+ // Suppress GuardedBy warning because lint asks to mark this method as
+ // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
+ @GuardedBy("mLock")
+ private boolean mayShowSplit(@NonNull TaskContainer taskContainer) {
// No split inside PIP.
if (taskContainer.isInPictureInPicture()) {
return false;
}
+ // Always assume the TaskContainer if SplitAttributesCalculator is set
+ if (mSplitAttributesCalculator != null) {
+ return true;
+ }
// Check if the parent container bounds can support any split rule.
for (EmbeddingRule rule : mSplitRules) {
if (!(rule instanceof SplitRule)) {
continue;
}
- if (shouldShowSideBySide(taskContainer.getTaskBounds(), (SplitRule) rule)) {
+ final SplitRule splitRule = (SplitRule) rule;
+ final SplitAttributes splitAttributes = mPresenter.computeSplitAttributes(
+ taskContainer.getTaskProperties(), splitRule, null /* minDimensionsPair */);
+ if (shouldShowSplit(splitAttributes)) {
return true;
}
}
@@ -690,14 +796,18 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* Starts an activity to side of the launchingActivity with the provided split config.
*/
+ // Suppress GuardedBy warning because lint ask to mark this method as
+ // @GuardedBy(container.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
private void startActivityToSide(@NonNull WindowContainerTransaction wct,
@NonNull Activity launchingActivity, @NonNull Intent intent,
@Nullable Bundle options, @NonNull SplitRule sideRule,
- @Nullable Consumer<Exception> failureCallback, boolean isPlaceholder) {
+ @NonNull SplitAttributes splitAttributes, @Nullable Consumer<Exception> failureCallback,
+ boolean isPlaceholder) {
try {
mPresenter.startActivityToSide(wct, launchingActivity, intent, options, sideRule,
- isPlaceholder);
+ splitAttributes, isPlaceholder);
} catch (Exception e) {
if (failureCallback != null) {
failureCallback.accept(e);
@@ -724,6 +834,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
/** Whether the given new launched activity is in a split with a rule matched. */
+ // Suppress GuardedBy warning because lint asks to mark this method as
+ // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
+ @GuardedBy("mLock")
private boolean isNewActivityInSplitWithRuleMatched(@NonNull Activity launchedActivity) {
final TaskFragmentContainer container = getContainerWithActivity(launchedActivity);
final SplitContainer splitContainer = getActiveSplitForContainer(container);
@@ -817,8 +931,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final TaskFragmentContainer primaryContainer = getContainerWithActivity(
primaryActivity);
final SplitContainer splitContainer = getActiveSplitForContainer(primaryContainer);
+ final WindowMetrics taskWindowMetrics = mPresenter.getTaskWindowMetrics(primaryActivity);
if (splitContainer != null && primaryContainer == splitContainer.getPrimaryContainer()
- && canReuseContainer(splitRule, splitContainer.getSplitRule())) {
+ && canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics)) {
// Can launch in the existing secondary container if the rules share the same
// presentation.
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
@@ -948,6 +1063,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
*/
@VisibleForTesting
@Nullable
+ @GuardedBy("mLock")
TaskFragmentContainer resolveStartActivityIntent(@NonNull WindowContainerTransaction wct,
int taskId, @NonNull Intent intent, @Nullable Activity launchingActivity) {
/*
@@ -1010,6 +1126,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* Returns an empty expanded {@link TaskFragmentContainer} that we can launch an activity into.
*/
+ @GuardedBy("mLock")
@Nullable
private TaskFragmentContainer createEmptyExpandedContainer(
@NonNull WindowContainerTransaction wct, @NonNull Intent intent, int taskId,
@@ -1051,8 +1168,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
final TaskFragmentContainer existingContainer = getContainerWithActivity(primaryActivity);
final SplitContainer splitContainer = getActiveSplitForContainer(existingContainer);
+ final WindowMetrics taskWindowMetrics = mPresenter.getTaskWindowMetrics(primaryActivity);
if (splitContainer != null && existingContainer == splitContainer.getPrimaryContainer()
- && (canReuseContainer(splitRule, splitContainer.getSplitRule())
+ && (canReuseContainer(splitRule, splitContainer.getSplitRule(), taskWindowMetrics)
// TODO(b/231845476) we should always respect clearTop.
|| !respectClearTop)
&& mPresenter.expandSplitContainerIfNeeded(wct, splitContainer, primaryActivity,
@@ -1091,12 +1209,14 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return newContainer(pendingAppearedActivity, pendingAppearedActivity, taskId);
}
+ @GuardedBy("mLock")
TaskFragmentContainer newContainer(@NonNull Activity pendingAppearedActivity,
@NonNull Activity activityInTask, int taskId) {
return newContainer(pendingAppearedActivity, null /* pendingAppearedIntent */,
activityInTask, taskId);
}
+ @GuardedBy("mLock")
TaskFragmentContainer newContainer(@NonNull Intent pendingAppearedIntent,
@NonNull Activity activityInTask, int taskId) {
return newContainer(null /* pendingAppearedActivity */, pendingAppearedIntent,
@@ -1120,7 +1240,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
throw new IllegalArgumentException("activityInTask must not be null,");
}
if (!mTaskContainers.contains(taskId)) {
- mTaskContainers.put(taskId, new TaskContainer(taskId));
+ mTaskContainers.put(taskId, new TaskContainer(taskId, activityInTask));
}
final TaskContainer taskContainer = mTaskContainers.get(taskId);
final TaskFragmentContainer container = new TaskFragmentContainer(pendingAppearedActivity,
@@ -1132,10 +1252,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
Log.w(TAG, "Can't find bounds from activity=" + activityInTask);
}
}
- if (!taskContainer.isWindowingModeInitialized()) {
- taskContainer.setWindowingMode(activityInTask.getResources().getConfiguration()
- .windowConfiguration.getWindowingMode());
- }
updateAnimationOverride(taskContainer);
return container;
}
@@ -1144,12 +1260,16 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Creates and registers a new split with the provided containers and configuration. Finishes
* existing secondary containers if found for the given primary container.
*/
+ // Suppress GuardedBy warning because lint ask to mark this method as
+ // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
+ @GuardedBy("mLock")
void registerSplit(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer primaryContainer, @NonNull Activity primaryActivity,
@NonNull TaskFragmentContainer secondaryContainer,
- @NonNull SplitRule splitRule) {
+ @NonNull SplitRule splitRule, @NonNull SplitAttributes splitAttributes) {
final SplitContainer splitContainer = new SplitContainer(primaryContainer, primaryActivity,
- secondaryContainer, splitRule);
+ secondaryContainer, splitRule, splitAttributes);
// Remove container later to prevent pinning escaping toast showing in lock task mode.
if (splitRule instanceof SplitPairRule && ((SplitPairRule) splitRule).shouldClearTop()) {
removeExistingSecondaryContainers(wct, primaryContainer);
@@ -1300,6 +1420,12 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// Skip position update - one or both containers are finished.
return;
}
+ final TaskContainer taskContainer = splitContainer.getTaskContainer();
+ final SplitRule splitRule = splitContainer.getSplitRule();
+ final Pair<Size, Size> minDimensionsPair = splitContainer.getMinDimensionsPair();
+ final SplitAttributes splitAttributes = mPresenter.computeSplitAttributes(
+ taskContainer.getTaskProperties(), splitRule, minDimensionsPair);
+ splitContainer.setSplitAttributes(splitAttributes);
if (dismissPlaceholderIfNecessary(wct, splitContainer)) {
// Placeholder was finished, the positions will be updated when its container is emptied
return;
@@ -1373,6 +1499,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return launchPlaceholderIfNecessary(wct, topActivity, false /* isOnCreated */);
}
+ // Suppress GuardedBy warning because lint ask to mark this method as
+ // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
@GuardedBy("mLock")
boolean launchPlaceholderIfNecessary(@NonNull WindowContainerTransaction wct,
@NonNull Activity activity, boolean isOnCreated) {
@@ -1399,18 +1528,20 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return false;
}
+ final TaskContainer.TaskProperties taskProperties = mPresenter.getTaskProperties(activity);
final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(activity,
placeholderRule.getPlaceholderIntent());
- if (!shouldShowSideBySide(
- mPresenter.getParentContainerBounds(activity), placeholderRule,
- minDimensionsPair)) {
+ final SplitAttributes splitAttributes = mPresenter.computeSplitAttributes(taskProperties,
+ placeholderRule, minDimensionsPair);
+ if (!SplitPresenter.shouldShowSplit(splitAttributes)) {
return false;
}
// TODO(b/190433398): Handle failed request
final Bundle options = getPlaceholderOptions(activity, isOnCreated);
startActivityToSide(wct, activity, placeholderRule.getPlaceholderIntent(), options,
- placeholderRule, null /* failureCallback */, true /* isPlaceholder */);
+ placeholderRule, splitAttributes, null /* failureCallback */,
+ true /* isPlaceholder */);
return true;
}
@@ -1435,6 +1566,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
return options.toBundle();
}
+ // Suppress GuardedBy warning because lint ask to mark this method as
+ // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
@VisibleForTesting
@GuardedBy("mLock")
boolean dismissPlaceholderIfNecessary(@NonNull WindowContainerTransaction wct,
@@ -1447,11 +1581,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
// The placeholder should remain after it was first shown.
return false;
}
-
- if (shouldShowSideBySide(splitContainer)) {
+ final SplitAttributes splitAttributes = splitContainer.getSplitAttributes();
+ if (SplitPresenter.shouldShowSplit(splitAttributes)) {
return false;
}
-
mPresenter.cleanupContainer(wct, splitContainer.getSecondaryContainer(),
false /* shouldFinishDependent */);
return true;
@@ -1461,6 +1594,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Returns the rule to launch a placeholder for the activity with the provided component name
* if it is configured in the split config.
*/
+ @GuardedBy("mLock")
private SplitPlaceholderRule getPlaceholderRule(@NonNull Activity activity) {
for (EmbeddingRule rule : mSplitRules) {
if (!(rule instanceof SplitPlaceholderRule)) {
@@ -1477,6 +1611,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* Notifies listeners about changes to split states if necessary.
*/
+ @GuardedBy("mLock")
private void updateCallbackIfNecessary() {
if (mEmbeddingCallback == null) {
return;
@@ -1498,6 +1633,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* null, that indicates that the active split states are in an intermediate state and should
* not be reported.
*/
+ @GuardedBy("mLock")
@Nullable
private List<SplitInfo> getActiveSplitStates() {
List<SplitInfo> splitStates = new ArrayList<>();
@@ -1517,12 +1653,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
final ActivityStack secondaryContainer = container.getSecondaryContainer()
.toActivityStack();
final SplitInfo splitState = new SplitInfo(primaryContainer, secondaryContainer,
- // Splits that are not showing side-by-side are reported as having 0 split
- // ratio, since by definition in the API the primary container occupies no
- // width of the split when covered by the secondary.
- shouldShowSideBySide(container)
- ? container.getSplitRule().getSplitRatio()
- : 0.0f);
+ container.getSplitAttributes());
splitStates.add(splitState);
}
}
@@ -1560,6 +1691,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* Returns a split rule for the provided pair of primary activity and secondary activity intent
* if available.
*/
+ @GuardedBy("mLock")
@Nullable
private SplitPairRule getSplitRule(@NonNull Activity primaryActivity,
@NonNull Intent secondaryActivityIntent) {
@@ -1578,6 +1710,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
/**
* Returns a split rule for the provided pair of primary and secondary activities if available.
*/
+ @GuardedBy("mLock")
@Nullable
private SplitPairRule getSplitRule(@NonNull Activity primaryActivity,
@NonNull Activity secondaryActivity) {
@@ -1652,6 +1785,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* 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.
*/
+ @GuardedBy("mLock")
private boolean shouldExpand(@Nullable Activity activity, @Nullable Intent intent) {
for (EmbeddingRule rule : mSplitRules) {
if (!(rule instanceof ActivityRule)) {
@@ -1677,6 +1811,10 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* 'sticky' and the placeholder was finished when fully overlapping the primary container.
* @return {@code true} if the associated container should be retained (and not be finished).
*/
+ // Suppress GuardedBy warning because lint ask to mark this method as
+ // @GuardedBy(mPresenter.mController.mLock), which is mLock itself
+ @SuppressWarnings("GuardedBy")
+ @GuardedBy("mLock")
boolean shouldRetainAssociatedContainer(@NonNull TaskFragmentContainer finishingContainer,
@NonNull TaskFragmentContainer associatedContainer) {
SplitContainer splitContainer = getActiveSplitForContainers(associatedContainer,
@@ -1695,7 +1833,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
}
// Decide whether the associated container should be retained based on the current
// presentation mode.
- if (shouldShowSideBySide(splitContainer)) {
+ if (shouldShowSplit(splitContainer)) {
return !shouldFinishAssociatedContainerWhenAdjacent(finishBehavior);
} else {
return !shouldFinishAssociatedContainerWhenStacked(finishBehavior);
@@ -1888,23 +2026,33 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
* If the two rules have the same presentation, we can reuse the same {@link SplitContainer} if
* there is any.
*/
- private static boolean canReuseContainer(@NonNull SplitRule rule1, @NonNull SplitRule rule2) {
+ private static boolean canReuseContainer(@NonNull SplitRule rule1, @NonNull SplitRule rule2,
+ @NonNull WindowMetrics parentWindowMetrics) {
if (!isContainerReusableRule(rule1) || !isContainerReusableRule(rule2)) {
return false;
}
- return haveSamePresentation((SplitPairRule) rule1, (SplitPairRule) rule2);
+ return haveSamePresentation((SplitPairRule) rule1, (SplitPairRule) rule2,
+ parentWindowMetrics);
}
/** Whether the two rules have the same presentation. */
- private static boolean haveSamePresentation(@NonNull SplitPairRule rule1,
- @NonNull SplitPairRule rule2) {
+ @VisibleForTesting
+ static boolean haveSamePresentation(@NonNull SplitPairRule rule1,
+ @NonNull SplitPairRule rule2, @NonNull WindowMetrics parentWindowMetrics) {
+ if (rule1.getTag() != null || rule2.getTag() != null) {
+ // Tag must be unique if it is set. We don't want to reuse the container if the rules
+ // have different tags because they can have different SplitAttributes later through
+ // SplitAttributesCalculator.
+ return Objects.equals(rule1.getTag(), rule2.getTag());
+ }
+ // If both rules don't have tag, compare all SplitRules' properties that may affect their
+ // SplitAttributes.
// TODO(b/231655482): add util method to do the comparison in SplitPairRule.
- return rule1.getSplitRatio() == rule2.getSplitRatio()
- && rule1.getLayoutDirection() == rule2.getLayoutDirection()
- && rule1.getFinishPrimaryWithSecondary()
- == rule2.getFinishPrimaryWithSecondary()
- && rule1.getFinishSecondaryWithPrimary()
- == rule2.getFinishSecondaryWithPrimary();
+ return rule1.getDefaultSplitAttributes().equals(rule2.getDefaultSplitAttributes())
+ && rule1.checkParentMetrics(parentWindowMetrics)
+ == rule2.checkParentMetrics(parentWindowMetrics)
+ && rule1.getFinishPrimaryWithSecondary() == rule2.getFinishPrimaryWithSecondary()
+ && rule1.getFinishSecondaryWithPrimary() == rule2.getFinishSecondaryWithPrimary();
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 2ef8e4c64855..79603233ae14 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -22,11 +22,11 @@ import android.app.Activity;
import android.app.ActivityThread;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
@@ -42,9 +42,21 @@ import androidx.annotation.GuardedBy;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType.ExpandContainersSplitType;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType.HingeSplitType;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType.RatioSplitType;
+import androidx.window.extensions.embedding.SplitAttributesCalculator.SplitAttributesCalculatorParams;
+import androidx.window.extensions.embedding.TaskContainer.TaskProperties;
+import androidx.window.extensions.layout.DisplayFeature;
+import androidx.window.extensions.layout.FoldingFeature;
+import androidx.window.extensions.layout.WindowLayoutInfo;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -66,11 +78,25 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
})
private @interface Position {}
+ private static final int CONTAINER_POSITION_LEFT = 0;
+ private static final int CONTAINER_POSITION_TOP = 1;
+ private static final int CONTAINER_POSITION_RIGHT = 2;
+ private static final int CONTAINER_POSITION_BOTTOM = 3;
+
+ @IntDef(value = {
+ CONTAINER_POSITION_LEFT,
+ CONTAINER_POSITION_TOP,
+ CONTAINER_POSITION_RIGHT,
+ CONTAINER_POSITION_BOTTOM,
+ })
+ private @interface ContainerPosition {}
+
/**
* Result of {@link #expandSplitContainerIfNeeded(WindowContainerTransaction, SplitContainer,
* Activity, Activity, Intent)}.
* No need to expand the splitContainer because screen is big enough to
- * {@link #shouldShowSideBySide(Rect, SplitRule, Pair)} and minimum dimensions is satisfied.
+ * {@link #shouldShowSplit(SplitAttributes)} and minimum dimensions is
+ * satisfied.
*/
static final int RESULT_NOT_EXPANDED = 0;
/**
@@ -78,7 +104,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* Activity, Activity, Intent)}.
* The splitContainer should be expanded. It is usually because minimum dimensions is not
* satisfied.
- * @see #shouldShowSideBySide(Rect, SplitRule, Pair)
+ * @see #shouldShowSplit(SplitAttributes)
*/
static final int RESULT_EXPANDED = 1;
/**
@@ -101,6 +127,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
})
private @interface ResultCode {}
+ @VisibleForTesting
+ static final SplitAttributes EXPAND_CONTAINERS_ATTRIBUTES =
+ new SplitAttributes.Builder()
+ .setSplitType(new ExpandContainersSplitType())
+ .build();
+
private final SplitController mController;
SplitPresenter(@NonNull Executor executor, @NonNull SplitController controller) {
@@ -129,14 +161,17 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* @return The newly created secondary container.
*/
@NonNull
+ @GuardedBy("mController.mLock")
TaskFragmentContainer createNewSplitWithEmptySideContainer(
@NonNull WindowContainerTransaction wct, @NonNull Activity primaryActivity,
@NonNull Intent secondaryIntent, @NonNull SplitPairRule rule) {
- final Rect parentBounds = getParentContainerBounds(primaryActivity);
+ final TaskProperties taskProperties = getTaskProperties(primaryActivity);
final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(
primaryActivity, secondaryIntent);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
- primaryActivity, minDimensionsPair);
+ final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
+ minDimensionsPair);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes);
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
primaryActivity, primaryRectBounds, null);
@@ -144,8 +179,8 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final int taskId = primaryContainer.getTaskId();
final TaskFragmentContainer secondaryContainer = mController.newContainer(
secondaryIntent, primaryActivity, taskId);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds,
- rule, primaryActivity, minDimensionsPair);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties,
+ splitAttributes);
final int windowingMode = mController.getTaskContainer(taskId)
.getWindowingModeForSplitTaskFragment(secondaryRectBounds);
createTaskFragment(wct, secondaryContainer.getTaskFragmentToken(),
@@ -154,9 +189,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// Set adjacent to each other so that the containers below will be invisible.
setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule,
- minDimensionsPair);
+ splitAttributes);
- mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
+ mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule,
+ splitAttributes);
return secondaryContainer;
}
@@ -176,16 +212,18 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
void createNewSplitContainer(@NonNull WindowContainerTransaction wct,
@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity,
@NonNull SplitPairRule rule) {
- final Rect parentBounds = getParentContainerBounds(primaryActivity);
+ final TaskProperties taskProperties = getTaskProperties(primaryActivity);
final Pair<Size, Size> minDimensionsPair = getActivitiesMinDimensionsPair(primaryActivity,
secondaryActivity);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
- primaryActivity, minDimensionsPair);
+ final SplitAttributes splitAttributes = computeSplitAttributes(taskProperties, rule,
+ minDimensionsPair);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes);
final TaskFragmentContainer primaryContainer = prepareContainerForActivity(wct,
primaryActivity, primaryRectBounds, null);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
- primaryActivity, minDimensionsPair);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties,
+ splitAttributes);
final TaskFragmentContainer curSecondaryContainer = mController.getContainerWithActivity(
secondaryActivity);
TaskFragmentContainer containerToAvoid = primaryContainer;
@@ -200,9 +238,10 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
// Set adjacent to each other so that the containers below will be invisible.
setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule,
- minDimensionsPair);
+ splitAttributes);
- mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule);
+ mController.registerSplit(wct, primaryContainer, primaryActivity, secondaryContainer, rule,
+ splitAttributes);
}
/**
@@ -244,16 +283,16 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* @param rule The split rule to be applied to the container.
* @param isPlaceholder Whether the launch is a placeholder.
*/
+ @GuardedBy("mController.mLock")
void startActivityToSide(@NonNull WindowContainerTransaction wct,
@NonNull Activity launchingActivity, @NonNull Intent activityIntent,
- @Nullable Bundle activityOptions, @NonNull SplitRule rule, boolean isPlaceholder) {
- final Rect parentBounds = getParentContainerBounds(launchingActivity);
- final Pair<Size, Size> minDimensionsPair = getActivityIntentMinDimensionsPair(
- launchingActivity, activityIntent);
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
- launchingActivity, minDimensionsPair);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
- launchingActivity, minDimensionsPair);
+ @Nullable Bundle activityOptions, @NonNull SplitRule rule,
+ @NonNull SplitAttributes splitAttributes, boolean isPlaceholder) {
+ final TaskProperties taskProperties = getTaskProperties(launchingActivity);
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties,
+ splitAttributes);
TaskFragmentContainer primaryContainer = mController.getContainerWithActivity(
launchingActivity);
@@ -268,7 +307,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final int windowingMode = mController.getTaskContainer(taskId)
.getWindowingModeForSplitTaskFragment(primaryRectBounds);
mController.registerSplit(wct, primaryContainer, launchingActivity, secondaryContainer,
- rule);
+ rule, splitAttributes);
startActivityToSide(wct, primaryContainer.getTaskFragmentToken(), primaryRectBounds,
launchingActivity, secondaryContainer.getTaskFragmentToken(), secondaryRectBounds,
activityIntent, activityOptions, rule, windowingMode);
@@ -284,22 +323,24 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* @param updatedContainer The task fragment that was updated and caused this split update.
* @param wct WindowContainerTransaction that this update should be performed with.
*/
+ @GuardedBy("mController.mLock")
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);
+ // Getting the parent configuration using the updated container - it will have the recent
+ // value.
final SplitRule rule = splitContainer.getSplitRule();
final TaskFragmentContainer primaryContainer = splitContainer.getPrimaryContainer();
final Activity activity = primaryContainer.getTopNonFinishingActivity();
if (activity == null) {
return;
}
- final Pair<Size, Size> minDimensionsPair = splitContainer.getMinDimensionsPair();
- final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, parentBounds, rule,
- activity, minDimensionsPair);
- final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, parentBounds, rule,
- activity, minDimensionsPair);
+ final TaskProperties taskProperties = getTaskProperties(updatedContainer);
+ final SplitAttributes splitAttributes = splitContainer.getSplitAttributes();
+ final Rect primaryRectBounds = getBoundsForPosition(POSITION_START, taskProperties,
+ splitAttributes);
+ final Rect secondaryRectBounds = getBoundsForPosition(POSITION_END, taskProperties,
+ splitAttributes);
final TaskFragmentContainer secondaryContainer = splitContainer.getSecondaryContainer();
// Whether the placeholder is becoming side-by-side with the primary from fullscreen.
final boolean isPlaceholderBecomingSplit = splitContainer.isPlaceholderContainer()
@@ -311,7 +352,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
resizeTaskFragmentIfRegistered(wct, primaryContainer, primaryRectBounds);
resizeTaskFragmentIfRegistered(wct, secondaryContainer, secondaryRectBounds);
setAdjacentTaskFragments(wct, primaryContainer, secondaryContainer, rule,
- minDimensionsPair);
+ splitAttributes);
if (isPlaceholderBecomingSplit) {
// When placeholder is shown in split, we should keep the focus on the primary.
wct.requestFocusOnTaskFragment(primaryContainer.getTaskFragmentToken());
@@ -323,14 +364,14 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
updateTaskFragmentWindowingModeIfRegistered(wct, secondaryContainer, windowingMode);
}
+ @GuardedBy("mController.mLock")
private void setAdjacentTaskFragments(@NonNull WindowContainerTransaction wct,
@NonNull TaskFragmentContainer primaryContainer,
@NonNull TaskFragmentContainer secondaryContainer, @NonNull SplitRule splitRule,
- @NonNull Pair<Size, Size> minDimensionsPair) {
- final Rect parentBounds = getParentContainerBounds(primaryContainer);
+ @NonNull SplitAttributes splitAttributes) {
// Clear adjacent TaskFragments if the container is shown in fullscreen, or the
// secondaryContainer could not be finished.
- if (!shouldShowSideBySide(parentBounds, splitRule, minDimensionsPair)) {
+ if (!shouldShowSplit(splitAttributes)) {
setAdjacentTaskFragments(wct, primaryContainer.getTaskFragmentToken(),
null /* secondary */, null /* splitRule */);
} else {
@@ -416,8 +457,9 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
* Expands the split container if the current split bounds are smaller than the Activity or
* Intent that is added to the container.
*
- * @return the {@link ResultCode} based on {@link #shouldShowSideBySide(Rect, SplitRule, Pair)}
- * and if {@link android.window.TaskFragmentInfo} has reported to the client side.
+ * @return the {@link ResultCode} based on
+ * {@link #shouldShowSplit(SplitAttributes)} and if
+ * {@link android.window.TaskFragmentInfo} has reported to the client side.
*/
@ResultCode
int expandSplitContainerIfNeeded(@NonNull WindowContainerTransaction wct,
@@ -427,7 +469,6 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
throw new IllegalArgumentException("Either secondaryActivity or secondaryIntent must be"
+ " non-null.");
}
- final Rect taskBounds = getParentContainerBounds(primaryActivity);
final Pair<Size, Size> minDimensionsPair;
if (secondaryActivity != null) {
minDimensionsPair = getActivitiesMinDimensionsPair(primaryActivity, secondaryActivity);
@@ -436,7 +477,12 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
secondaryIntent);
}
// Expand the splitContainer if minimum dimensions are not satisfied.
- if (!shouldShowSideBySide(taskBounds, splitContainer.getSplitRule(), minDimensionsPair)) {
+ final TaskContainer taskContainer = splitContainer.getTaskContainer();
+ final SplitAttributes splitAttributes = sanitizeSplitAttributes(
+ taskContainer.getTaskProperties(), splitContainer.getSplitAttributes(),
+ minDimensionsPair);
+ splitContainer.setSplitAttributes(splitAttributes);
+ if (!shouldShowSplit(splitAttributes)) {
// If the client side hasn't received TaskFragmentInfo yet, we can't change TaskFragment
// bounds. Return failure to create a new SplitContainer which fills task bounds.
if (splitContainer.getPrimaryContainer().getInfo() == null
@@ -450,36 +496,63 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
return RESULT_NOT_EXPANDED;
}
- static boolean shouldShowSideBySide(@NonNull Rect parentBounds, @NonNull SplitRule rule) {
- return shouldShowSideBySide(parentBounds, rule, null /* minimumDimensionPair */);
+ static boolean shouldShowSplit(@NonNull SplitContainer splitContainer) {
+ return shouldShowSplit(splitContainer.getSplitAttributes());
}
- static boolean shouldShowSideBySide(@NonNull SplitContainer splitContainer) {
- final Rect parentBounds = getParentContainerBounds(splitContainer.getPrimaryContainer());
+ static boolean shouldShowSplit(@NonNull SplitAttributes splitAttributes) {
+ return !(splitAttributes.getSplitType() instanceof ExpandContainersSplitType);
+ }
- return shouldShowSideBySide(parentBounds, splitContainer.getSplitRule(),
- splitContainer.getMinDimensionsPair());
+ @GuardedBy("mController.mLock")
+ @NonNull
+ SplitAttributes computeSplitAttributes(@NonNull TaskProperties taskProperties,
+ @NonNull SplitRule rule, @Nullable Pair<Size, Size> minDimensionsPair) {
+ final Configuration taskConfiguration = taskProperties.getConfiguration();
+ final WindowMetrics taskWindowMetrics = getTaskWindowMetrics(taskConfiguration);
+ final SplitAttributesCalculator calculator = mController.getSplitAttributesCalculator();
+ final SplitAttributes defaultSplitAttributes = rule.getDefaultSplitAttributes();
+ final boolean isDefaultMinSizeSatisfied = rule.checkParentMetrics(taskWindowMetrics);
+ if (calculator == null) {
+ if (!isDefaultMinSizeSatisfied) {
+ return EXPAND_CONTAINERS_ATTRIBUTES;
+ }
+ return sanitizeSplitAttributes(taskProperties, defaultSplitAttributes,
+ minDimensionsPair);
+ }
+ final WindowLayoutInfo windowLayoutInfo = mController.mWindowLayoutComponent
+ .getCurrentWindowLayoutInfo(taskProperties.getDisplayId(),
+ taskConfiguration.windowConfiguration);
+ final SplitAttributesCalculatorParams params = new SplitAttributesCalculatorParams(
+ taskWindowMetrics, taskConfiguration, defaultSplitAttributes,
+ isDefaultMinSizeSatisfied, windowLayoutInfo, rule.getTag());
+ final SplitAttributes splitAttributes = calculator.computeSplitAttributesForParams(params);
+ return sanitizeSplitAttributes(taskProperties, splitAttributes, minDimensionsPair);
}
- static boolean shouldShowSideBySide(@NonNull Rect parentBounds, @NonNull SplitRule rule,
+ /**
+ * Returns {@link #EXPAND_CONTAINERS_ATTRIBUTES} if the passed {@link SplitAttributes} doesn't
+ * meet the minimum dimensions set in {@link ActivityInfo.WindowLayout}. Otherwise, returns
+ * the passed {@link SplitAttributes}.
+ */
+ @NonNull
+ private SplitAttributes sanitizeSplitAttributes(@NonNull TaskProperties taskProperties,
+ @NonNull SplitAttributes splitAttributes,
@Nullable Pair<Size, Size> minDimensionsPair) {
- // TODO(b/190433398): Supply correct insets.
- final WindowMetrics parentMetrics = new WindowMetrics(parentBounds,
- new WindowInsets(new Rect()));
- // Don't show side by side if bounds is not qualified.
- if (!rule.checkParentMetrics(parentMetrics)) {
- return false;
- }
- final float splitRatio = rule.getSplitRatio();
- // We only care the size of the bounds regardless of its position.
- final Rect primaryBounds = getPrimaryBounds(parentBounds, splitRatio, true /* isLtr */);
- final Rect secondaryBounds = getSecondaryBounds(parentBounds, splitRatio, true /* isLtr */);
-
if (minDimensionsPair == null) {
- return true;
- }
- return !boundsSmallerThanMinDimensions(primaryBounds, minDimensionsPair.first)
- && !boundsSmallerThanMinDimensions(secondaryBounds, minDimensionsPair.second);
+ return splitAttributes;
+ }
+ final FoldingFeature foldingFeature = getFoldingFeature(taskProperties);
+ final Configuration taskConfiguration = taskProperties.getConfiguration();
+ final Rect primaryBounds = getPrimaryBounds(taskConfiguration, splitAttributes,
+ foldingFeature);
+ final Rect secondaryBounds = getSecondaryBounds(taskConfiguration, splitAttributes,
+ foldingFeature);
+ if (boundsSmallerThanMinDimensions(primaryBounds, minDimensionsPair.first)
+ || boundsSmallerThanMinDimensions(secondaryBounds, minDimensionsPair.second)) {
+ return EXPAND_CONTAINERS_ATTRIBUTES;
+ }
+ return splitAttributes;
}
@NonNull
@@ -541,20 +614,25 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
@VisibleForTesting
@NonNull
- static Rect getBoundsForPosition(@Position int position, @NonNull Rect parentBounds,
- @NonNull SplitRule rule, @NonNull Activity primaryActivity,
- @Nullable Pair<Size, Size> minDimensionsPair) {
- if (!shouldShowSideBySide(parentBounds, rule, minDimensionsPair)) {
+ Rect getBoundsForPosition(@Position int position, @NonNull TaskProperties taskProperties,
+ @NonNull SplitAttributes splitAttributes) {
+ final Configuration taskConfiguration = taskProperties.getConfiguration();
+ final FoldingFeature foldingFeature = getFoldingFeature(taskProperties);
+ final SplitType splitType = computeSplitType(splitAttributes, taskConfiguration,
+ foldingFeature);
+ final SplitAttributes computedSplitAttributes = new SplitAttributes.Builder()
+ .setSplitType(splitType)
+ .setLayoutDirection(splitAttributes.getLayoutDirection())
+ .build();
+ if (!shouldShowSplit(computedSplitAttributes)) {
return new Rect();
}
- final boolean isLtr = isLtr(primaryActivity, rule);
- final float splitRatio = rule.getSplitRatio();
-
switch (position) {
case POSITION_START:
- return getPrimaryBounds(parentBounds, splitRatio, isLtr);
+ return getPrimaryBounds(taskConfiguration, computedSplitAttributes, foldingFeature);
case POSITION_END:
- return getSecondaryBounds(parentBounds, splitRatio, isLtr);
+ return getSecondaryBounds(taskConfiguration, computedSplitAttributes,
+ foldingFeature);
case POSITION_FILL:
default:
return new Rect();
@@ -562,74 +640,303 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
}
@NonNull
- private static Rect getPrimaryBounds(@NonNull Rect parentBounds, float splitRatio,
- boolean isLtr) {
- return isLtr ? getLeftContainerBounds(parentBounds, splitRatio)
- : getRightContainerBounds(parentBounds, 1 - splitRatio);
+ private Rect getPrimaryBounds(@NonNull Configuration taskConfiguration,
+ @NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) {
+ if (!shouldShowSplit(splitAttributes)) {
+ return new Rect();
+ }
+ switch (splitAttributes.getLayoutDirection()) {
+ case SplitAttributes.LayoutDirection.LEFT_TO_RIGHT: {
+ return getLeftContainerBounds(taskConfiguration, splitAttributes, foldingFeature);
+ }
+ case SplitAttributes.LayoutDirection.RIGHT_TO_LEFT: {
+ return getRightContainerBounds(taskConfiguration, splitAttributes, foldingFeature);
+ }
+ case SplitAttributes.LayoutDirection.LOCALE: {
+ final boolean isLtr = taskConfiguration.getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
+ return isLtr
+ ? getLeftContainerBounds(taskConfiguration, splitAttributes, foldingFeature)
+ : getRightContainerBounds(taskConfiguration, splitAttributes,
+ foldingFeature);
+ }
+ case SplitAttributes.LayoutDirection.TOP_TO_BOTTOM: {
+ return getTopContainerBounds(taskConfiguration, splitAttributes, foldingFeature);
+ }
+ case SplitAttributes.LayoutDirection.BOTTOM_TO_TOP: {
+ return getBottomContainerBounds(taskConfiguration, splitAttributes, foldingFeature);
+ }
+ default:
+ throw new IllegalArgumentException("Unknown layout direction:"
+ + splitAttributes.getLayoutDirection());
+ }
}
@NonNull
- private static Rect getSecondaryBounds(@NonNull Rect parentBounds, float splitRatio,
- boolean isLtr) {
- return isLtr ? getRightContainerBounds(parentBounds, splitRatio)
- : getLeftContainerBounds(parentBounds, 1 - splitRatio);
+ private Rect getSecondaryBounds(@NonNull Configuration taskConfiguration,
+ @NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) {
+ if (!shouldShowSplit(splitAttributes)) {
+ return new Rect();
+ }
+ switch (splitAttributes.getLayoutDirection()) {
+ case SplitAttributes.LayoutDirection.LEFT_TO_RIGHT: {
+ return getRightContainerBounds(taskConfiguration, splitAttributes, foldingFeature);
+ }
+ case SplitAttributes.LayoutDirection.RIGHT_TO_LEFT: {
+ return getLeftContainerBounds(taskConfiguration, splitAttributes, foldingFeature);
+ }
+ case SplitAttributes.LayoutDirection.LOCALE: {
+ final boolean isLtr = taskConfiguration.getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
+ return isLtr
+ ? getRightContainerBounds(taskConfiguration, splitAttributes,
+ foldingFeature)
+ : getLeftContainerBounds(taskConfiguration, splitAttributes,
+ foldingFeature);
+ }
+ case SplitAttributes.LayoutDirection.TOP_TO_BOTTOM: {
+ return getBottomContainerBounds(taskConfiguration, splitAttributes, foldingFeature);
+ }
+ case SplitAttributes.LayoutDirection.BOTTOM_TO_TOP: {
+ return getTopContainerBounds(taskConfiguration, splitAttributes, foldingFeature);
+ }
+ default:
+ throw new IllegalArgumentException("Unknown layout direction:"
+ + splitAttributes.getLayoutDirection());
+ }
}
- private static Rect getLeftContainerBounds(@NonNull Rect parentBounds, float splitRatio) {
- return new Rect(
- parentBounds.left,
- parentBounds.top,
- (int) (parentBounds.left + parentBounds.width() * splitRatio),
- parentBounds.bottom);
+ @NonNull
+ private Rect getLeftContainerBounds(@NonNull Configuration taskConfiguration,
+ @NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) {
+ final int right = computeBoundaryBetweenContainers(taskConfiguration, splitAttributes,
+ CONTAINER_POSITION_LEFT, foldingFeature);
+ final Rect taskBounds = taskConfiguration.windowConfiguration.getBounds();
+ return new Rect(taskBounds.left, taskBounds.top, right, taskBounds.bottom);
}
- private static Rect getRightContainerBounds(@NonNull Rect parentBounds, float splitRatio) {
- return new Rect(
- (int) (parentBounds.left + parentBounds.width() * splitRatio),
- parentBounds.top,
- parentBounds.right,
- parentBounds.bottom);
+ @NonNull
+ private Rect getRightContainerBounds(@NonNull Configuration taskConfiguration,
+ @NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) {
+ final int left = computeBoundaryBetweenContainers(taskConfiguration, splitAttributes,
+ CONTAINER_POSITION_RIGHT, foldingFeature);
+ final Rect parentBounds = taskConfiguration.windowConfiguration.getBounds();
+ return new Rect(left, parentBounds.top, parentBounds.right, parentBounds.bottom);
+ }
+
+ @NonNull
+ private Rect getTopContainerBounds(@NonNull Configuration taskConfiguration,
+ @NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) {
+ final int bottom = computeBoundaryBetweenContainers(taskConfiguration, splitAttributes,
+ CONTAINER_POSITION_TOP, foldingFeature);
+ final Rect parentBounds = taskConfiguration.windowConfiguration.getBounds();
+ return new Rect(parentBounds.left, parentBounds.top, parentBounds.right, bottom);
+ }
+
+ @NonNull
+ private Rect getBottomContainerBounds(@NonNull Configuration taskConfiguration,
+ @NonNull SplitAttributes splitAttributes, @Nullable FoldingFeature foldingFeature) {
+ final int top = computeBoundaryBetweenContainers(taskConfiguration, splitAttributes,
+ CONTAINER_POSITION_BOTTOM, foldingFeature);
+ final Rect parentBounds = taskConfiguration.windowConfiguration.getBounds();
+ return new Rect(parentBounds.left, top, parentBounds.right, parentBounds.bottom);
}
/**
- * Checks if a split with the provided rule should be displays in left-to-right layout
- * direction, either always or with the current configuration.
+ * Computes the boundary position between the primary and the secondary containers for the given
+ * {@link ContainerPosition} with {@link SplitAttributes}, current window and device states.
+ * <ol>
+ * <li>For {@link #CONTAINER_POSITION_TOP}, it computes the boundary with the bottom
+ * container, which is {@link Rect#bottom} of the top container bounds.</li>
+ * <li>For {@link #CONTAINER_POSITION_BOTTOM}, it computes the boundary with the top
+ * container, which is {@link Rect#top} of the bottom container bounds.</li>
+ * <li>For {@link #CONTAINER_POSITION_LEFT}, it computes the boundary with the right
+ * container, which is {@link Rect#right} of the left container bounds.</li>
+ * <li>For {@link #CONTAINER_POSITION_RIGHT}, it computes the boundary with the bottom
+ * container, which is {@link Rect#left} of the right container bounds.</li>
+ * </ol>
+ *
+ * @see #getTopContainerBounds(Configuration, SplitAttributes, FoldingFeature)
+ * @see #getBottomContainerBounds(Configuration, SplitAttributes, FoldingFeature)
+ * @see #getLeftContainerBounds(Configuration, SplitAttributes, FoldingFeature)
+ * @see #getRightContainerBounds(Configuration, SplitAttributes, FoldingFeature)
*/
- private static boolean isLtr(@NonNull Context context, @NonNull SplitRule rule) {
- switch (rule.getLayoutDirection()) {
- case LayoutDirection.LOCALE:
- return context.getResources().getConfiguration().getLayoutDirection()
- == View.LAYOUT_DIRECTION_LTR;
- case LayoutDirection.RTL:
- return false;
- case LayoutDirection.LTR:
+ private int computeBoundaryBetweenContainers(@NonNull Configuration taskConfiguration,
+ @NonNull SplitAttributes splitAttributes, @ContainerPosition int position,
+ @Nullable FoldingFeature foldingFeature) {
+ final Rect parentBounds = taskConfiguration.windowConfiguration.getBounds();
+ final int startPoint = shouldSplitHorizontally(splitAttributes)
+ ? parentBounds.top
+ : parentBounds.left;
+ final int dimen = shouldSplitHorizontally(splitAttributes)
+ ? parentBounds.height()
+ : parentBounds.width();
+ final SplitType splitType = splitAttributes.getSplitType();
+ if (splitType instanceof RatioSplitType) {
+ final RatioSplitType splitRatio = (RatioSplitType) splitType;
+ return (int) (startPoint + dimen * splitRatio.getRatio());
+ }
+ // At this point, SplitType must be a HingeSplitType and foldingFeature must be
+ // non-null. RatioSplitType and ExpandContainerSplitType have been handled earlier.
+ Objects.requireNonNull(foldingFeature);
+ if (!(splitType instanceof HingeSplitType)) {
+ throw new IllegalArgumentException("Unknown splitType:" + splitType);
+ }
+ final Rect hingeArea = foldingFeature.getBounds();
+ switch (position) {
+ case CONTAINER_POSITION_LEFT:
+ return hingeArea.left;
+ case CONTAINER_POSITION_TOP:
+ return hingeArea.top;
+ case CONTAINER_POSITION_RIGHT:
+ return hingeArea.right;
+ case CONTAINER_POSITION_BOTTOM:
+ return hingeArea.bottom;
default:
- return true;
+ throw new IllegalArgumentException("Unknown position:" + position);
}
}
- @NonNull
- static Rect getParentContainerBounds(@NonNull TaskFragmentContainer container) {
- return container.getTaskContainer().getTaskBounds();
+ @Nullable
+ private FoldingFeature getFoldingFeature(@NonNull TaskProperties taskProperties) {
+ final int displayId = taskProperties.getDisplayId();
+ final WindowConfiguration windowConfiguration = taskProperties.getConfiguration()
+ .windowConfiguration;
+ final WindowLayoutInfo info = mController.mWindowLayoutComponent
+ .getCurrentWindowLayoutInfo(displayId, windowConfiguration);
+ final List<DisplayFeature> displayFeatures = info.getDisplayFeatures();
+ if (displayFeatures.isEmpty()) {
+ return null;
+ }
+ final List<FoldingFeature> foldingFeatures = new ArrayList<>();
+ for (DisplayFeature displayFeature : displayFeatures) {
+ if (displayFeature instanceof FoldingFeature) {
+ foldingFeatures.add((FoldingFeature) displayFeature);
+ }
+ }
+ // TODO(b/240219484): Support device with multiple hinges.
+ if (foldingFeatures.size() != 1) {
+ return null;
+ }
+ return foldingFeatures.get(0);
}
- @NonNull
- Rect getParentContainerBounds(@NonNull Activity activity) {
- final TaskFragmentContainer container = mController.getContainerWithActivity(activity);
- if (container != null) {
- return getParentContainerBounds(container);
+ /**
+ * Indicates that this {@link SplitAttributes} splits the task horizontally. Returns
+ * {@code false} if this {@link SplitAttributes} splits the task vertically.
+ */
+ private static boolean shouldSplitHorizontally(SplitAttributes splitAttributes) {
+ switch (splitAttributes.getLayoutDirection()) {
+ case SplitAttributes.LayoutDirection.TOP_TO_BOTTOM:
+ case SplitAttributes.LayoutDirection.BOTTOM_TO_TOP:
+ return true;
+ default:
+ return false;
}
- // Obtain bounds from Activity instead because the Activity hasn't been embedded yet.
- return getNonEmbeddedActivityBounds(activity);
}
/**
- * Obtains the bounds from a non-embedded Activity.
- * <p>
- * Note that callers should use {@link #getParentContainerBounds(Activity)} instead for most
- * cases unless we want to obtain task bounds before
- * {@link TaskContainer#isTaskBoundsInitialized()}.
+ * Computes the {@link SplitType} with the {@link SplitAttributes} and the current device and
+ * window state.
+ * If passed {@link SplitAttributes#getSplitType} is a {@link RatioSplitType}. It reversed
+ * the ratio if the computed {@link SplitAttributes#getLayoutDirection} is
+ * {@link SplitAttributes.LayoutDirection.LEFT_TO_RIGHT} or
+ * {@link SplitAttributes.LayoutDirection.BOTTOM_TO_TOP} to make the bounds calculation easier.
+ * If passed {@link SplitAttributes#getSplitType} is a {@link HingeSplitType}, it checks
+ * the current device and window states to determine whether the split container should split
+ * by hinge or use {@link HingeSplitType#getFallbackSplitType}.
*/
+ private SplitType computeSplitType(@NonNull SplitAttributes splitAttributes,
+ @NonNull Configuration taskConfiguration, @Nullable FoldingFeature foldingFeature) {
+ final int layoutDirection = splitAttributes.getLayoutDirection();
+ final SplitType splitType = splitAttributes.getSplitType();
+ if (splitType instanceof ExpandContainersSplitType) {
+ return splitType;
+ } else if (splitType instanceof RatioSplitType) {
+ final RatioSplitType splitRatio = (RatioSplitType) splitType;
+ // Reverse the ratio for RIGHT_TO_LEFT and BOTTOM_TO_TOP to make the boundary
+ // computation have the same direction, which is from (top, left) to (bottom, right).
+ final SplitType reversedSplitType = new RatioSplitType(1 - splitRatio.getRatio());
+ switch (layoutDirection) {
+ case SplitAttributes.LayoutDirection.LEFT_TO_RIGHT:
+ case SplitAttributes.LayoutDirection.TOP_TO_BOTTOM:
+ return splitType;
+ case SplitAttributes.LayoutDirection.RIGHT_TO_LEFT:
+ case SplitAttributes.LayoutDirection.BOTTOM_TO_TOP:
+ return reversedSplitType;
+ case LayoutDirection.LOCALE: {
+ boolean isLtr = taskConfiguration.getLayoutDirection()
+ == View.LAYOUT_DIRECTION_LTR;
+ return isLtr ? splitType : reversedSplitType;
+ }
+ }
+ } else if (splitType instanceof HingeSplitType) {
+ final HingeSplitType hinge = (HingeSplitType) splitType;
+ @WindowingMode
+ final int windowingMode = taskConfiguration.windowConfiguration.getWindowingMode();
+ return shouldSplitByHinge(splitAttributes, foldingFeature, windowingMode)
+ ? hinge : hinge.getFallbackSplitType();
+ }
+ throw new IllegalArgumentException("Unknown SplitType:" + splitType);
+ }
+
+ private static boolean shouldSplitByHinge(@NonNull SplitAttributes splitAttributes,
+ @Nullable FoldingFeature foldingFeature, @WindowingMode int taskWindowingMode) {
+ // Only HingeSplitType may split the task bounds by hinge.
+ if (!(splitAttributes.getSplitType() instanceof HingeSplitType)) {
+ return false;
+ }
+ // Device is not foldable, so there's no hinge to match.
+ if (foldingFeature == null) {
+ return false;
+ }
+ // The task is in multi-window mode. Match hinge doesn't make sense because current task
+ // bounds may not fit display bounds.
+ if (WindowConfiguration.inMultiWindowMode(taskWindowingMode)) {
+ return false;
+ }
+ // Return true if how the split attributes split the task bounds matches the orientation of
+ // folding area orientation.
+ return shouldSplitHorizontally(splitAttributes) == isFoldingAreaHorizontal(foldingFeature);
+ }
+
+ private static boolean isFoldingAreaHorizontal(@NonNull FoldingFeature foldingFeature) {
+ final Rect bounds = foldingFeature.getBounds();
+ return bounds.width() > bounds.height();
+ }
+
+ @NonNull
+ static TaskProperties getTaskProperties(@NonNull TaskFragmentContainer container) {
+ return container.getTaskContainer().getTaskProperties();
+ }
+
+ @NonNull
+ TaskProperties getTaskProperties(@NonNull Activity activity) {
+ final TaskContainer taskContainer = mController.getTaskContainer(
+ mController.getTaskId(activity));
+ if (taskContainer != null) {
+ return taskContainer.getTaskProperties();
+ }
+ // Use a copy of configuration because activity's configuration may be updated later,
+ // or we may get unexpected TaskContainer's configuration if Activity's configuration is
+ // updated. An example is Activity is going to be in split.
+ return new TaskProperties(activity.getDisplayId(),
+ new Configuration(activity.getResources().getConfiguration()));
+ }
+
+ @NonNull
+ WindowMetrics getTaskWindowMetrics(@NonNull Activity activity) {
+ return getTaskWindowMetrics(getTaskProperties(activity).getConfiguration());
+ }
+
+ @NonNull
+ private static WindowMetrics getTaskWindowMetrics(@NonNull Configuration taskConfiguration) {
+ final Rect taskBounds = taskConfiguration.windowConfiguration.getBounds();
+ // TODO(b/190433398): Supply correct insets.
+ return new WindowMetrics(taskBounds, WindowInsets.CONSUMED);
+ }
+
+ /** Obtains the bounds from a non-embedded Activity. */
@NonNull
static Rect getNonEmbeddedActivityBounds(@NonNull Activity activity) {
final WindowConfiguration windowConfiguration =
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
index b5636777568e..91573ffef568 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java
@@ -24,10 +24,13 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import android.app.Activity;
import android.app.WindowConfiguration;
import android.app.WindowConfiguration.WindowingMode;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArraySet;
import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentParentInfo;
+import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -42,13 +45,10 @@ class TaskContainer {
/** The unique task id. */
private final int mTaskId;
+ // TODO(b/240219484): consolidate to mConfiguration
/** Available window bounds of this Task. */
private final Rect mTaskBounds = new Rect();
- /** Windowing mode of this Task. */
- @WindowingMode
- private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
-
/** Active TaskFragments in this Task. */
@NonNull
final List<TaskFragmentContainer> mContainers = new ArrayList<>();
@@ -57,24 +57,56 @@ class TaskContainer {
@NonNull
final List<SplitContainer> mSplitContainers = new ArrayList<>();
+ @NonNull
+ private final Configuration mConfiguration;
+
+ private int mDisplayId;
+
+ private boolean mIsVisible;
+
/**
* TaskFragments that the organizer has requested to be closed. They should be removed when
- * the organizer receives {@link SplitController#onTaskFragmentVanished(TaskFragmentInfo)} event
- * for them.
+ * the organizer receives
+ * {@link SplitController#onTaskFragmentVanished(WindowContainerTransaction, TaskFragmentInfo)}
+ * event for them.
*/
final Set<IBinder> mFinishedContainer = new ArraySet<>();
- TaskContainer(int taskId) {
+ /**
+ * The {@link TaskContainer} constructor
+ *
+ * @param taskId The ID of the Task, which must match {@link Activity#getTaskId()} with
+ * {@code activityInTask}.
+ * @param activityInTask The {@link Activity} in the Task with {@code taskId}. It is used to
+ * initialize the {@link TaskContainer} properties.
+ *
+ */
+ TaskContainer(int taskId, @NonNull Activity activityInTask) {
if (taskId == INVALID_TASK_ID) {
throw new IllegalArgumentException("Invalid Task id");
}
mTaskId = taskId;
+ // Make a copy in case the activity's config is updated, and updates the TaskContainer's
+ // config unexpectedly.
+ mConfiguration = new Configuration(activityInTask.getResources().getConfiguration());
+ mDisplayId = activityInTask.getDisplayId();
+ // Note that it is always called when there's a new Activity is started, which implies
+ // the host task is visible.
+ mIsVisible = true;
}
int getTaskId() {
return mTaskId;
}
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ boolean isVisible() {
+ return mIsVisible;
+ }
+
@NonNull
Rect getTaskBounds() {
return mTaskBounds;
@@ -94,13 +126,21 @@ class TaskContainer {
return !mTaskBounds.isEmpty();
}
- void setWindowingMode(int windowingMode) {
- mWindowingMode = windowingMode;
+ @NonNull
+ Configuration getConfiguration() {
+ // Make a copy in case the config is updated unexpectedly.
+ return new Configuration(mConfiguration);
+ }
+
+ @NonNull
+ TaskProperties getTaskProperties() {
+ return new TaskProperties(mDisplayId, mConfiguration);
}
- /** Whether the Task windowing mode has been initialized. */
- boolean isWindowingModeInitialized() {
- return mWindowingMode != WINDOWING_MODE_UNDEFINED;
+ void updateTaskFragmentParentInfo(@NonNull TaskFragmentParentInfo info) {
+ mConfiguration.setTo(info.getConfiguration());
+ mDisplayId = info.getDisplayId();
+ mIsVisible = info.isVisibleRequested();
}
/**
@@ -123,13 +163,20 @@ class TaskContainer {
// DecorCaptionView won't work correctly. As a result, have the TaskFragment to be in the
// Task windowing mode if the Task is in multi window.
// TODO we won't need this anymore after we migrate Freeform caption to WM Shell.
- return WindowConfiguration.inMultiWindowMode(mWindowingMode)
- ? mWindowingMode
- : WINDOWING_MODE_MULTI_WINDOW;
+ return isInMultiWindow() ? getWindowingMode() : WINDOWING_MODE_MULTI_WINDOW;
}
boolean isInPictureInPicture() {
- return mWindowingMode == WINDOWING_MODE_PINNED;
+ return getWindowingMode() == WINDOWING_MODE_PINNED;
+ }
+
+ boolean isInMultiWindow() {
+ return WindowConfiguration.inMultiWindowMode(getWindowingMode());
+ }
+
+ @WindowingMode
+ private int getWindowingMode() {
+ return getConfiguration().windowConfiguration.getWindowingMode();
}
/** Whether there is any {@link TaskFragmentContainer} below this Task. */
@@ -173,4 +220,28 @@ class TaskContainer {
int indexOf(@NonNull TaskFragmentContainer child) {
return mContainers.indexOf(child);
}
+
+ /**
+ * A wrapper class which contains the display ID and {@link Configuration} of a
+ * {@link TaskContainer}
+ */
+ static final class TaskProperties {
+ private final int mDisplayId;
+ @NonNull
+ private final Configuration mConfiguration;
+
+ TaskProperties(int displayId, @NonNull Configuration configuration) {
+ mDisplayId = displayId;
+ mConfiguration = configuration;
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ @NonNull
+ Configuration getConfiguration() {
+ return mConfiguration;
+ }
+ }
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
index cdee9e386b33..af5d8c561874 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationAdapter.java
@@ -16,7 +16,6 @@
package androidx.window.extensions.embedding;
-import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
@@ -41,30 +40,44 @@ class TaskFragmentAnimationAdapter {
*/
private static final int LAYER_NO_OVERRIDE = -1;
+ @NonNull
final Animation mAnimation;
+ @NonNull
final RemoteAnimationTarget mTarget;
+ @NonNull
final SurfaceControl mLeash;
+ /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
+ @NonNull
+ private final Rect mWholeAnimationBounds = new Rect();
+ @NonNull
final Transformation mTransformation = new Transformation();
+ @NonNull
final float[] mMatrix = new float[9];
+ @NonNull
final float[] mVecs = new float[4];
+ @NonNull
final Rect mRect = new Rect();
private boolean mIsFirstFrame = true;
private int mOverrideLayer = LAYER_NO_OVERRIDE;
TaskFragmentAnimationAdapter(@NonNull Animation animation,
@NonNull RemoteAnimationTarget target) {
- this(animation, target, target.leash);
+ this(animation, target, target.leash, target.screenSpaceBounds);
}
/**
* @param leash the surface to animate.
+ * @param wholeAnimationBounds area in absolute coordinate that the animation surface shouldn't
+ * go beyond.
*/
TaskFragmentAnimationAdapter(@NonNull Animation animation,
- @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash) {
+ @NonNull RemoteAnimationTarget target, @NonNull SurfaceControl leash,
+ @NonNull Rect wholeAnimationBounds) {
mAnimation = animation;
mTarget = target;
mLeash = leash;
+ mWholeAnimationBounds.set(wholeAnimationBounds);
}
/**
@@ -94,23 +107,32 @@ class TaskFragmentAnimationAdapter {
/** To be overridden by subclasses to adjust the animation surface change. */
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ // Update the surface position and alpha.
mTransformation.getMatrix().postTranslate(
mTarget.localBounds.left, mTarget.localBounds.top);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
- // Get current animation position.
+
+ // Get current surface bounds in absolute coordinate.
+ // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
final int positionX = Math.round(mMatrix[MTRANS_X]);
final int positionY = Math.round(mMatrix[MTRANS_Y]);
- // The exiting surface starts at position: mTarget.localBounds and moves with
- // positionX varying. Offset our crop region by the amount we have slided so crop
- // regions stays exactly on the original container in split.
- final int cropOffsetX = mTarget.localBounds.left - positionX;
- final int cropOffsetY = mTarget.localBounds.top - positionY;
- final Rect cropRect = new Rect();
- cropRect.set(mTarget.localBounds);
- // Because window crop uses absolute position.
- cropRect.offsetTo(0, 0);
- cropRect.offset(cropOffsetX, cropOffsetY);
+ final Rect cropRect = new Rect(mTarget.screenSpaceBounds);
+ final Rect localBounds = mTarget.localBounds;
+ cropRect.offset(positionX - localBounds.left, positionY - localBounds.top);
+
+ // Store the current offset of the surface top left from (0,0) in absolute coordinate.
+ final int offsetX = cropRect.left;
+ final int offsetY = cropRect.top;
+
+ // Intersect to make sure the animation happens within the whole animation bounds.
+ if (!cropRect.intersect(mWholeAnimationBounds)) {
+ // Hide the surface when it is outside of the animation area.
+ t.setAlpha(mLeash, 0);
+ }
+
+ // cropRect is in absolute coordinate, so we need to translate it to surface top left.
+ cropRect.offset(-offsetX, -offsetY);
t.setCrop(mLeash, cropRect);
}
@@ -124,52 +146,6 @@ class TaskFragmentAnimationAdapter {
}
/**
- * Should be used when the {@link RemoteAnimationTarget} is in split with others, and want to
- * animate together as one. This adapter will offset the animation leash to make the animate of
- * two windows look like a single window.
- */
- static class SplitAdapter extends TaskFragmentAnimationAdapter {
- private final boolean mIsLeftHalf;
- private final int mWholeAnimationWidth;
-
- /**
- * @param isLeftHalf whether this is the left half of the animation.
- * @param wholeAnimationWidth the whole animation windows width.
- */
- SplitAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target,
- boolean isLeftHalf, int wholeAnimationWidth) {
- super(animation, target);
- mIsLeftHalf = isLeftHalf;
- mWholeAnimationWidth = wholeAnimationWidth;
- if (wholeAnimationWidth == 0) {
- throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
- }
- }
-
- @Override
- void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
- float posX = mTarget.localBounds.left;
- final float posY = mTarget.localBounds.top;
- // This window is half of the whole animation window. Offset left/right to make it
- // look as one with the other half.
- mTransformation.getMatrix().getValues(mMatrix);
- final int targetWidth = mTarget.localBounds.width();
- final float scaleX = mMatrix[MSCALE_X];
- final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
- final float curOffset = targetWidth * (1 - scaleX) / 2;
- final float offsetDiff = totalOffset - curOffset;
- if (mIsLeftHalf) {
- posX += offsetDiff;
- } else {
- posX -= offsetDiff;
- }
- mTransformation.getMatrix().postTranslate(posX, posY);
- t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
- t.setAlpha(mLeash, mTransformation.getAlpha());
- }
- }
-
- /**
* Should be used for the animation of the snapshot of a {@link RemoteAnimationTarget} that has
* size change.
*/
@@ -177,7 +153,7 @@ class TaskFragmentAnimationAdapter {
SnapshotAdapter(@NonNull Animation animation, @NonNull RemoteAnimationTarget target) {
// Start leash is the snapshot of the starting surface.
- super(animation, target, target.startLeash);
+ super(animation, target, target.startLeash, target.screenSpaceBounds);
}
@Override
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
index 8af2d9c6810b..8c416e881059 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationRunner.java
@@ -213,10 +213,10 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
for (RemoteAnimationTarget target : targets) {
if (target.mode != MODE_CLOSING) {
openingTargets.add(target);
- openingWholeScreenBounds.union(target.localBounds);
+ openingWholeScreenBounds.union(target.screenSpaceBounds);
} else {
closingTargets.add(target);
- closingWholeScreenBounds.union(target.localBounds);
+ closingWholeScreenBounds.union(target.screenSpaceBounds);
}
}
@@ -249,20 +249,8 @@ class TaskFragmentAnimationRunner extends IRemoteAnimationRunner.Stub {
@NonNull BiFunction<RemoteAnimationTarget, Rect, Animation> animationProvider,
@NonNull Rect wholeAnimationBounds) {
final Animation animation = animationProvider.apply(target, wholeAnimationBounds);
- final Rect targetBounds = target.localBounds;
- if (targetBounds.left == wholeAnimationBounds.left
- && targetBounds.right != wholeAnimationBounds.right) {
- // This is the left split of the whole animation window.
- return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
- true /* isLeftHalf */, wholeAnimationBounds.width());
- } else if (targetBounds.left != wholeAnimationBounds.left
- && targetBounds.right == wholeAnimationBounds.right) {
- // This is the right split of the whole animation window.
- return new TaskFragmentAnimationAdapter.SplitAdapter(animation, target,
- false /* isLeftHalf */, wholeAnimationBounds.width());
- }
- // Open/close window that fills the whole animation.
- return new TaskFragmentAnimationAdapter(animation, target);
+ return new TaskFragmentAnimationAdapter(animation, target, target.leash,
+ wholeAnimationBounds);
}
@NonNull
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
index 97d42391b6c4..ef5ea563de12 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentAnimationSpec.java
@@ -195,7 +195,10 @@ class TaskFragmentAnimationSpec {
? com.android.internal.R.anim.task_fragment_open_enter
: com.android.internal.R.anim.task_fragment_open_exit);
}
- animation.initialize(target.localBounds.width(), target.localBounds.height(),
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are opening at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are launching together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation;
@@ -215,7 +218,10 @@ class TaskFragmentAnimationSpec {
? com.android.internal.R.anim.task_fragment_close_enter
: com.android.internal.R.anim.task_fragment_close_exit);
}
- animation.initialize(target.localBounds.width(), target.localBounds.height(),
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are closing at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are finishing together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation;
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
index 626e0d990a6d..18712aed1be6 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java
@@ -432,7 +432,7 @@ class TaskFragmentContainer {
// In case we have requested to reparent the activity to another container (as
// pendingAppeared), we don't want to finish it with this container.
&& mController.getContainerWithActivity(activity) == this) {
- activity.finish();
+ wct.finishActivity(activity.getActivityToken());
}
}
@@ -457,7 +457,7 @@ class TaskFragmentContainer {
|| controller.shouldRetainAssociatedActivity(this, activity)) {
continue;
}
- activity.finish();
+ wct.finishActivity(activity.getActivityToken());
}
mActivitiesToFinishOnExit.clear();
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index f24401f0cd53..c76f568e117f 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -47,6 +47,7 @@ import androidx.window.common.RawFoldingFeatureProducer;
import androidx.window.util.DataProducer;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -68,6 +69,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
+ private final List<CommonFoldingFeature> mLastReportedFoldingFeatures = new ArrayList<>();
+
private final Map<IBinder, WindowContextConfigListener> mWindowContextConfigListeners =
new ArrayMap<>();
@@ -80,6 +83,11 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
+ /** Registers to listen to {@link CommonFoldingFeature} changes */
+ public void addFoldingStateChangedCallback(Consumer<List<CommonFoldingFeature>> consumer) {
+ mFoldingFeatureProducer.addDataChangedCallback(consumer);
+ }
+
/**
* Adds a listener interested in receiving updates to {@link WindowLayoutInfo}
*
@@ -186,6 +194,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
private void onDisplayFeaturesChanged(List<CommonFoldingFeature> storedFeatures) {
+ mLastReportedFoldingFeatures.clear();
+ mLastReportedFoldingFeatures.addAll(storedFeatures);
for (Context context : getContextsListeningForLayoutChanges()) {
// Get the WindowLayoutInfo from the activity and pass the value to the layoutConsumer.
Consumer<WindowLayoutInfo> layoutConsumer = mWindowLayoutChangeListeners.get(context);
@@ -207,6 +217,27 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
/**
+ * Gets the current {@link WindowLayoutInfo} computed with passed {@link WindowConfiguration}.
+ *
+ * @return current {@link WindowLayoutInfo} on the default display. Returns
+ * empty {@link WindowLayoutInfo} on secondary displays.
+ */
+ @NonNull
+ public WindowLayoutInfo getCurrentWindowLayoutInfo(int displayId,
+ @NonNull WindowConfiguration windowConfiguration) {
+ return getWindowLayoutInfo(displayId, windowConfiguration, mLastReportedFoldingFeatures);
+ }
+
+ /** @see #getWindowLayoutInfo(Context, List) */
+ private WindowLayoutInfo getWindowLayoutInfo(int displayId,
+ @NonNull WindowConfiguration windowConfiguration,
+ List<CommonFoldingFeature> storedFeatures) {
+ List<DisplayFeature> displayFeatureList = getDisplayFeatures(displayId, windowConfiguration,
+ storedFeatures);
+ return new WindowLayoutInfo(displayFeatureList);
+ }
+
+ /**
* Translate from the {@link CommonFoldingFeature} to
* {@link DisplayFeature} for a given {@link Activity}. If a
* {@link CommonFoldingFeature} is not valid then it will be omitted.
@@ -225,12 +256,23 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
*/
private List<DisplayFeature> getDisplayFeatures(
@NonNull @UiContext Context context, List<CommonFoldingFeature> storedFeatures) {
- List<DisplayFeature> features = new ArrayList<>();
if (!shouldReportDisplayFeatures(context)) {
+ return Collections.emptyList();
+ }
+ return getDisplayFeatures(context.getDisplayId(),
+ context.getResources().getConfiguration().windowConfiguration,
+ storedFeatures);
+ }
+
+ /** @see #getDisplayFeatures(Context, List) */
+ private List<DisplayFeature> getDisplayFeatures(int displayId,
+ @NonNull WindowConfiguration windowConfiguration,
+ List<CommonFoldingFeature> storedFeatures) {
+ List<DisplayFeature> features = new ArrayList<>();
+ if (displayId != DEFAULT_DISPLAY) {
return features;
}
- int displayId = context.getDisplay().getDisplayId();
for (CommonFoldingFeature baseFeature : storedFeatures) {
Integer state = convertToExtensionState(baseFeature.getState());
if (state == null) {
@@ -238,7 +280,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
}
Rect featureRect = baseFeature.getRect();
rotateRectToDisplayRotation(displayId, featureRect);
- transformToWindowSpaceRect(context, featureRect);
+ transformToWindowSpaceRect(windowConfiguration, featureRect);
if (!isZero(featureRect)) {
// TODO(b/228641877): Remove guarding when fixed.
@@ -263,6 +305,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
windowingMode = ActivityClient.getInstance().getTaskWindowingMode(
context.getActivityToken());
} else {
+ // TODO(b/242674941): use task windowing mode for window context that associates with
+ // activity.
windowingMode = context.getResources().getConfiguration().windowConfiguration
.getWindowingMode();
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
index 31bf96313a95..9e2611f392a3 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
@@ -21,6 +21,7 @@ import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
@@ -89,13 +90,21 @@ public final class ExtensionHelper {
/** Transforms rectangle from absolute coordinate space to the window coordinate space. */
public static void transformToWindowSpaceRect(@NonNull @UiContext Context context,
Rect inOutRect) {
- Rect windowRect = getWindowBounds(context);
- if (!Rect.intersects(inOutRect, windowRect)) {
+ transformToWindowSpaceRect(getWindowBounds(context), inOutRect);
+ }
+
+ /** @see ExtensionHelper#transformToWindowSpaceRect(Context, Rect) */
+ public static void transformToWindowSpaceRect(@NonNull WindowConfiguration windowConfiguration,
+ Rect inOutRect) {
+ transformToWindowSpaceRect(windowConfiguration.getBounds(), inOutRect);
+ }
+
+ private static void transformToWindowSpaceRect(@NonNull Rect bounds, @NonNull Rect inOutRect) {
+ if (!inOutRect.intersect(bounds)) {
inOutRect.setEmpty();
return;
}
- inOutRect.intersect(windowRect);
- inOutRect.offset(-windowRect.left, -windowRect.top);
+ inOutRect.offset(-bounds.left, -bounds.top);
}
/**
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
index effc1a3ef3ea..40f7a273980a 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/EmbeddingTestUtils.java
@@ -16,9 +16,12 @@
package androidx.window.extensions.embedding;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
import static androidx.window.extensions.embedding.SplitRule.FINISH_NEVER;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import android.annotation.NonNull;
@@ -26,32 +29,68 @@ import android.app.Activity;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
+import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Pair;
import android.window.TaskFragmentInfo;
import android.window.WindowContainerToken;
+import androidx.window.extensions.embedding.SplitAttributes.SplitType;
+import androidx.window.extensions.layout.DisplayFeature;
+import androidx.window.extensions.layout.FoldingFeature;
+import androidx.window.extensions.layout.WindowLayoutInfo;
+
+import java.util.ArrayList;
import java.util.Collections;
+import java.util.List;
public class EmbeddingTestUtils {
static final Rect TASK_BOUNDS = new Rect(0, 0, 600, 1200);
static final int TASK_ID = 10;
- static final float SPLIT_RATIO = 0.5f;
+ static final SplitType SPLIT_TYPE = SplitType.RatioSplitType.splitEqually();
+ static final SplitAttributes SPLIT_ATTRIBUTES = new SplitAttributes.Builder().build();
+ static final String TEST_TAG = "test";
/** Default finish behavior in Jetpack. */
static final int DEFAULT_FINISH_PRIMARY_WITH_SECONDARY = FINISH_NEVER;
static final int DEFAULT_FINISH_SECONDARY_WITH_PRIMARY = FINISH_ALWAYS;
+ private static final float SPLIT_RATIO = 0.5f;
private EmbeddingTestUtils() {}
/** Gets the bounds of a TaskFragment that is in split. */
static Rect getSplitBounds(boolean isPrimary) {
- final int width = (int) (TASK_BOUNDS.width() * SPLIT_RATIO);
+ return getSplitBounds(isPrimary, false /* shouldSplitHorizontally */);
+ }
+
+ /** Gets the bounds of a TaskFragment that is in split. */
+ static Rect getSplitBounds(boolean isPrimary, boolean shouldSplitHorizontally) {
+ final int dimension = (int) (
+ (shouldSplitHorizontally ? TASK_BOUNDS.height() : TASK_BOUNDS.width())
+ * SPLIT_RATIO);
+ if (shouldSplitHorizontally) {
+ return isPrimary
+ ? new Rect(
+ TASK_BOUNDS.left,
+ TASK_BOUNDS.top,
+ TASK_BOUNDS.right,
+ TASK_BOUNDS.top + dimension)
+ : new Rect(
+ TASK_BOUNDS.left,
+ TASK_BOUNDS.top + dimension,
+ TASK_BOUNDS.right,
+ TASK_BOUNDS.bottom);
+ }
return isPrimary
- ? new Rect(TASK_BOUNDS.left, TASK_BOUNDS.top, TASK_BOUNDS.left + width,
- TASK_BOUNDS.bottom)
+ ? new Rect(
+ TASK_BOUNDS.left,
+ TASK_BOUNDS.top,
+ TASK_BOUNDS.left + dimension,
+ TASK_BOUNDS.bottom)
: new Rect(
- TASK_BOUNDS.left + width, TASK_BOUNDS.top, TASK_BOUNDS.right,
+ TASK_BOUNDS.left + dimension,
+ TASK_BOUNDS.top,
+ TASK_BOUNDS.right,
TASK_BOUNDS.bottom);
}
@@ -69,10 +108,15 @@ public class EmbeddingTestUtils {
activityPair -> false,
targetPair::equals,
w -> true)
- .setSplitRatio(SPLIT_RATIO)
+ .setDefaultSplitAttributes(
+ new SplitAttributes.Builder()
+ .setSplitType(SPLIT_TYPE)
+ .build()
+ )
.setShouldClearTop(clearTop)
.setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
.setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY)
+ .setTag(TEST_TAG)
.build();
}
@@ -101,10 +145,15 @@ public class EmbeddingTestUtils {
targetPair::equals,
activityIntentPair -> false,
w -> true)
- .setSplitRatio(SPLIT_RATIO)
+ .setDefaultSplitAttributes(
+ new SplitAttributes.Builder()
+ .setSplitType(SPLIT_TYPE)
+ .build()
+ )
.setFinishPrimaryWithSecondary(finishPrimaryWithSecondary)
.setFinishSecondaryWithPrimary(finishSecondaryWithPrimary)
.setShouldClearTop(clearTop)
+ .setTag(TEST_TAG)
.build();
}
@@ -130,4 +179,29 @@ public class EmbeddingTestUtils {
primaryBounds.width() + 1, primaryBounds.height() + 1);
return aInfo;
}
+
+ static TaskContainer createTestTaskContainer() {
+ Resources resources = mock(Resources.class);
+ doReturn(new Configuration()).when(resources).getConfiguration();
+ Activity activity = mock(Activity.class);
+ doReturn(resources).when(activity).getResources();
+ doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId();
+
+ return new TaskContainer(TASK_ID, activity);
+ }
+
+ static WindowLayoutInfo createWindowLayoutInfo() {
+ final FoldingFeature foldingFeature = new FoldingFeature(
+ new Rect(
+ TASK_BOUNDS.left,
+ TASK_BOUNDS.top + TASK_BOUNDS.height() / 2 - 5,
+ TASK_BOUNDS.right,
+ TASK_BOUNDS.top + TASK_BOUNDS.height() / 2 + 5
+ ),
+ FoldingFeature.TYPE_HINGE,
+ FoldingFeature.STATE_HALF_OPENED);
+ final List<DisplayFeature> displayFeatures = new ArrayList<>();
+ displayFeatures.add(foldingFeature);
+ return new WindowLayoutInfo(displayFeatures);
+ }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
index 58a627bafa16..957a24873998 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/JetpackTaskFragmentOrganizerTest.java
@@ -19,6 +19,7 @@ package androidx.window.extensions.embedding;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -26,12 +27,14 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.os.Handler;
import android.platform.test.annotations.Presubmit;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentTransaction;
@@ -65,7 +68,10 @@ public class JetpackTaskFragmentOrganizerTest {
private WindowContainerTransaction mTransaction;
@Mock
private JetpackTaskFragmentOrganizer.TaskFragmentCallback mCallback;
+ @Mock
private SplitController mSplitController;
+ @Mock
+ private Handler mHandler;
private JetpackTaskFragmentOrganizer mOrganizer;
@Before
@@ -73,9 +79,8 @@ public class JetpackTaskFragmentOrganizerTest {
MockitoAnnotations.initMocks(this);
mOrganizer = new JetpackTaskFragmentOrganizer(Runnable::run, mCallback);
mOrganizer.registerOrganizer();
- mSplitController = new SplitController();
spyOn(mOrganizer);
- spyOn(mSplitController);
+ doReturn(mHandler).when(mSplitController).getHandler();
}
@Test
@@ -113,7 +118,7 @@ public class JetpackTaskFragmentOrganizerTest {
@Test
public void testExpandTaskFragment() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
new Intent(), taskContainer, mSplitController);
final TaskFragmentInfo info = createMockInfo(container);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
index 58870a66feea..25d034756265 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitControllerTest.java
@@ -19,6 +19,7 @@ package androidx.window.extensions.embedding;
import static android.app.ActivityManager.START_CANCELED;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentTransaction.TYPE_ACTIVITY_REPARENTED_TO_TASK;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_APPEARED;
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_ERROR;
@@ -27,15 +28,20 @@ import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_PARENT_I
import static android.window.TaskFragmentTransaction.TYPE_TASK_FRAGMENT_VANISHED;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT;
-import static androidx.window.extensions.embedding.EmbeddingTestUtils.SPLIT_RATIO;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_PRIMARY_WITH_SECONDARY;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_SECONDARY_WITH_PRIMARY;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.SPLIT_ATTRIBUTES;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.TEST_TAG;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
import static androidx.window.extensions.embedding.SplitRule.FINISH_ALWAYS;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -73,14 +79,19 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.platform.test.annotations.Presubmit;
+import android.view.WindowInsets;
+import android.view.WindowMetrics;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
+import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.window.extensions.layout.WindowLayoutComponentImpl;
+import androidx.window.extensions.layout.WindowLayoutInfo;
import org.junit.Before;
import org.junit.Test;
@@ -116,6 +127,8 @@ public class SplitControllerTest {
private WindowContainerTransaction mTransaction;
@Mock
private Handler mHandler;
+ @Mock
+ private WindowLayoutComponentImpl mWindowLayoutComponent;
private SplitController mSplitController;
private SplitPresenter mSplitPresenter;
@@ -123,7 +136,9 @@ public class SplitControllerTest {
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mSplitController = new SplitController();
+ doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
+ .getCurrentWindowLayoutInfo(anyInt(), any());
+ mSplitController = new SplitController(mWindowLayoutComponent);
mSplitPresenter = mSplitController.mPresenter;
spyOn(mSplitController);
spyOn(mSplitPresenter);
@@ -138,7 +153,7 @@ public class SplitControllerTest {
@Test
public void testGetTopActiveContainer() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
// tf1 has no running activity so is not active.
final TaskFragmentContainer tf1 = new TaskFragmentContainer(null /* activity */,
new Intent(), taskContainer, mSplitController);
@@ -192,12 +207,13 @@ public class SplitControllerTest {
verify(mSplitPresenter, never()).deleteTaskFragment(any(), any());
verify(mSplitController).removeContainer(tf);
- verify(mActivity, never()).finish();
+ verify(mTransaction, never()).finishActivity(any());
}
@Test
public void testOnTaskFragmentAppearEmptyTimeout() {
final TaskFragmentContainer tf = mSplitController.newContainer(mActivity, TASK_ID);
+ doCallRealMethod().when(mSplitController).onTaskFragmentAppearEmptyTimeout(any(), any());
mSplitController.onTaskFragmentAppearEmptyTimeout(mTransaction, tf);
verify(mSplitPresenter).cleanupContainer(mTransaction, tf,
@@ -268,6 +284,8 @@ public class SplitControllerTest {
final SplitContainer splitContainer = mock(SplitContainer.class);
doReturn(tf).when(splitContainer).getPrimaryContainer();
doReturn(tf).when(splitContainer).getSecondaryContainer();
+ doReturn(createTestTaskContainer()).when(splitContainer).getTaskContainer();
+ doReturn(createSplitRule(mActivity, mActivity)).when(splitContainer).getSplitRule();
final List<SplitContainer> splitContainers =
mSplitController.getTaskContainer(TASK_ID).mSplitContainers;
splitContainers.add(splitContainer);
@@ -298,7 +316,7 @@ public class SplitControllerTest {
// Verify if the top active split is updated if both of its containers are not finished.
doReturn(false).when(mSplitController)
- .dismissPlaceholderIfNecessary(mTransaction, splitContainer);
+ .dismissPlaceholderIfNecessary(mTransaction, splitContainer);
mSplitController.updateContainer(mTransaction, tf);
@@ -308,7 +326,7 @@ public class SplitControllerTest {
@Test
public void testOnStartActivityResultError() {
final Intent intent = new Intent();
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
intent, taskContainer, mSplitController);
final SplitController.ActivityStartMonitor monitor =
@@ -608,7 +626,7 @@ public class SplitControllerTest {
assertTrue(result);
verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT,
mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
- placeholderRule, true /* isPlaceholder */);
+ placeholderRule, SPLIT_ATTRIBUTES, true /* isPlaceholder */);
}
@Test
@@ -624,7 +642,7 @@ public class SplitControllerTest {
assertFalse(result);
verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(), any(),
- anyBoolean());
+ any(), anyBoolean());
}
@Test
@@ -641,7 +659,7 @@ public class SplitControllerTest {
assertTrue(result);
verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT,
mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
- placeholderRule, true /* isPlaceholder */);
+ placeholderRule, SPLIT_ATTRIBUTES, true /* isPlaceholder */);
}
@Test
@@ -656,7 +674,7 @@ public class SplitControllerTest {
assertFalse(result);
verify(mSplitPresenter, never()).startActivityToSide(any(), any(), any(), any(), any(),
- anyBoolean());
+ any(), anyBoolean());
}
@Test
@@ -674,7 +692,7 @@ public class SplitControllerTest {
assertTrue(result);
verify(mSplitPresenter).startActivityToSide(mTransaction, mActivity, PLACEHOLDER_INTENT,
mSplitController.getPlaceholderOptions(mActivity, true /* isOnCreated */),
- placeholderRule, true /* isPlaceholder */);
+ placeholderRule, SPLIT_ATTRIBUTES, true /* isPlaceholder */);
}
@Test
@@ -693,14 +711,15 @@ public class SplitControllerTest {
primaryContainer,
mActivity,
secondaryContainer,
- splitRule);
+ splitRule,
+ SPLIT_ATTRIBUTES);
clearInvocations(mSplitController);
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
- verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any());
+ verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
@Test
@@ -720,7 +739,8 @@ public class SplitControllerTest {
primaryContainer,
mActivity,
secondaryContainer,
- splitRule);
+ splitRule,
+ SPLIT_ATTRIBUTES);
final Activity launchedActivity = createMockActivity();
primaryContainer.addPendingAppearedActivity(launchedActivity);
@@ -741,7 +761,7 @@ public class SplitControllerTest {
assertTrue(result);
verify(mSplitController, never()).newContainer(any(), any(), any(), anyInt());
- verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any());
+ verify(mSplitController, never()).registerSplit(any(), any(), any(), any(), any(), any());
}
@Test
@@ -778,7 +798,8 @@ public class SplitControllerTest {
primaryContainer,
mActivity,
secondaryContainer,
- placeholderRule);
+ placeholderRule,
+ SPLIT_ATTRIBUTES);
final boolean result = mSplitController.resolveActivityToContainer(mTransaction, mActivity,
false /* isOnReparent */);
@@ -983,9 +1004,9 @@ public class SplitControllerTest {
assertTrue(primaryContainer.isFinished());
assertTrue(secondaryContainer0.isFinished());
assertTrue(secondaryContainer1.isFinished());
- verify(mActivity).finish();
- verify(secondaryActivity0).finish();
- verify(secondaryActivity1).finish();
+ verify(mTransaction).finishActivity(mActivity.getActivityToken());
+ verify(mTransaction).finishActivity(secondaryActivity0.getActivityToken());
+ verify(mTransaction).finishActivity(secondaryActivity1.getActivityToken());
assertTrue(taskContainer.mContainers.isEmpty());
assertTrue(taskContainer.mSplitContainers.isEmpty());
}
@@ -1038,15 +1059,16 @@ public class SplitControllerTest {
@Test
public void testOnTransactionReady_taskFragmentParentInfoChanged() {
final TaskFragmentTransaction transaction = new TaskFragmentTransaction();
- final Configuration taskConfig = new Configuration();
+ final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo(Configuration.EMPTY,
+ DEFAULT_DISPLAY, true);
transaction.addChange(new TaskFragmentTransaction.Change(
TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
.setTaskId(TASK_ID)
- .setTaskConfiguration(taskConfig));
+ .setTaskFragmentParentInfo(parentInfo));
mSplitController.onTransactionReady(transaction);
verify(mSplitController).onTaskFragmentParentInfoChanged(any(), eq(TASK_ID),
- eq(taskConfig));
+ eq(parentInfo));
verify(mSplitPresenter).onTransactionHandled(eq(transaction.getTransactionToken()), any(),
anyInt(), anyBoolean());
}
@@ -1088,6 +1110,47 @@ public class SplitControllerTest {
anyInt(), anyBoolean());
}
+ @Test
+ public void testHasSamePresentation() {
+ SplitPairRule splitRule1 = new SplitPairRule.Builder(
+ activityPair -> true,
+ activityIntentPair -> true,
+ windowMetrics -> true)
+ .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY)
+ .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
+ .setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
+ .build();
+ SplitPairRule splitRule2 = new SplitPairRule.Builder(
+ activityPair -> true,
+ activityIntentPair -> true,
+ windowMetrics -> true)
+ .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY)
+ .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
+ .setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
+ .build();
+
+ assertTrue("Rules must have same presentation if tags are null and has same properties.",
+ SplitController.haveSamePresentation(splitRule1, splitRule2,
+ new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED)));
+
+ splitRule2 = new SplitPairRule.Builder(
+ activityPair -> true,
+ activityIntentPair -> true,
+ windowMetrics -> true)
+ .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY)
+ .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
+ .setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
+ .setTag(TEST_TAG)
+ .build();
+
+ assertFalse("Rules must have different presentations if tags are not equal regardless"
+ + "of other properties",
+ SplitController.haveSamePresentation(splitRule1, splitRule2,
+ new WindowMetrics(TASK_BOUNDS, WindowInsets.CONSUMED)));
+
+
+ }
+
/** Creates a mock activity in the organizer process. */
private Activity createMockActivity() {
final Activity activity = mock(Activity.class);
@@ -1097,6 +1160,7 @@ public class SplitControllerTest {
doReturn(activity).when(mSplitController).getActivity(activityToken);
doReturn(TASK_ID).when(activity).getTaskId();
doReturn(new ActivityInfo()).when(activity).getActivityInfo();
+ doReturn(DEFAULT_DISPLAY).when(activity).getDisplayId();
return activity;
}
@@ -1135,7 +1199,7 @@ public class SplitControllerTest {
private void setupPlaceholderRule(@NonNull Activity primaryActivity) {
final SplitRule placeholderRule = new SplitPlaceholderRule.Builder(PLACEHOLDER_INTENT,
primaryActivity::equals, i -> false, w -> true)
- .setSplitRatio(SPLIT_RATIO)
+ .setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
.build();
mSplitController.setEmbeddingRules(Collections.singleton(placeholderRule));
}
@@ -1188,7 +1252,8 @@ public class SplitControllerTest {
primaryContainer,
primaryContainer.getTopNonFinishingActivity(),
secondaryContainer,
- rule);
+ rule,
+ SPLIT_ATTRIBUTES);
// We need to set those in case we are not respecting clear top.
// TODO(b/231845476) we should always respect clearTop.
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
index 25f0e25eec75..6dae0a1086b3 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/SplitPresenterTest.java
@@ -16,23 +16,28 @@
package androidx.window.extensions.embedding;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_PRIMARY_WITH_SECONDARY;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.DEFAULT_FINISH_SECONDARY_WITH_PRIMARY;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.SPLIT_ATTRIBUTES;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createActivityInfoWithMinDimensions;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createSplitRule;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createWindowLayoutInfo;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.getSplitBounds;
+import static androidx.window.extensions.embedding.SplitPresenter.EXPAND_CONTAINERS_ATTRIBUTES;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_END;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_FILL;
import static androidx.window.extensions.embedding.SplitPresenter.POSITION_START;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPANDED;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_EXPAND_FAILED_NO_TF_INFO;
import static androidx.window.extensions.embedding.SplitPresenter.RESULT_NOT_EXPANDED;
-import static androidx.window.extensions.embedding.SplitPresenter.getBoundsForPosition;
import static androidx.window.extensions.embedding.SplitPresenter.getMinDimensions;
-import static androidx.window.extensions.embedding.SplitPresenter.shouldShowSideBySide;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
@@ -66,6 +71,8 @@ import android.window.WindowContainerTransaction;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import androidx.window.extensions.layout.WindowLayoutComponentImpl;
+import androidx.window.extensions.layout.WindowLayoutInfo;
import org.junit.Before;
import org.junit.Test;
@@ -73,6 +80,8 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+
/**
* Test class for {@link SplitPresenter}.
*
@@ -94,13 +103,17 @@ public class SplitPresenterTest {
private TaskFragmentInfo mTaskFragmentInfo;
@Mock
private WindowContainerTransaction mTransaction;
+ @Mock
+ private WindowLayoutComponentImpl mWindowLayoutComponent;
private SplitController mController;
private SplitPresenter mPresenter;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mController = new SplitController();
+ doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
+ .getCurrentWindowLayoutInfo(anyInt(), any());
+ mController = new SplitController(mWindowLayoutComponent);
mPresenter = mController.mPresenter;
spyOn(mController);
spyOn(mPresenter);
@@ -162,59 +175,263 @@ public class SplitPresenterTest {
@Test
public void testShouldShowSideBySide() {
- Activity secondaryActivity = createMockActivity();
- final SplitRule splitRule = createSplitRule(mActivity, secondaryActivity);
+ assertTrue(SplitPresenter.shouldShowSplit(SPLIT_ATTRIBUTES));
+
+ final SplitAttributes expandContainers = new SplitAttributes.Builder()
+ .setSplitType(new SplitAttributes.SplitType.ExpandContainersSplitType())
+ .build();
- assertTrue(shouldShowSideBySide(TASK_BOUNDS, splitRule));
+ assertFalse(SplitPresenter.shouldShowSplit(expandContainers));
+ }
- // Set minDimensions of primary container to larger than primary bounds.
- final Rect primaryBounds = getSplitBounds(true /* isPrimary */);
- Pair<Size, Size> minDimensionsPair = new Pair<>(
- new Size(primaryBounds.width() + 1, primaryBounds.height() + 1), null);
+ @Test
+ public void testGetBoundsForPosition_expandContainers() {
+ final TaskContainer.TaskProperties taskProperties = getTaskProperty();
+ final SplitAttributes splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(new SplitAttributes.SplitType.ExpandContainersSplitType())
+ .build();
- assertFalse(shouldShowSideBySide(TASK_BOUNDS, splitRule, minDimensionsPair));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
}
@Test
- public void testGetBoundsForPosition() {
- Activity secondaryActivity = createMockActivity();
- final SplitRule splitRule = createSplitRule(mActivity, secondaryActivity);
- final Rect primaryBounds = getSplitBounds(true /* isPrimary */);
- final Rect secondaryBounds = getSplitBounds(false /* isPrimary */);
+ public void testGetBoundsForPosition_splitVertically() {
+ final Rect primaryBounds = getSplitBounds(true /* isPrimary */,
+ false /* splitHorizontally */);
+ final Rect secondaryBounds = getSplitBounds(false /* isPrimary */,
+ false /* splitHorizontally */);
+ final TaskContainer.TaskProperties taskProperties = getTaskProperty();
+ SplitAttributes splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
+ .setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
+ .build();
+
+ assertEquals("Primary bounds must be reported.",
+ primaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+
+ assertEquals("Secondary bounds must be reported.",
+ secondaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+
+ splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
+ .setLayoutDirection(SplitAttributes.LayoutDirection.RIGHT_TO_LEFT)
+ .build();
+
+ assertEquals("Secondary bounds must be reported.",
+ secondaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
assertEquals("Primary bounds must be reported.",
primaryBounds,
- getBoundsForPosition(POSITION_START, TASK_BOUNDS, splitRule,
- mActivity, null /* miniDimensionsPair */));
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+
+ splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
+ .setLayoutDirection(SplitAttributes.LayoutDirection.LOCALE)
+ .build();
+ // Layout direction should follow screen layout for SplitAttributes.LayoutDirection.LOCALE.
+ taskProperties.getConfiguration().screenLayout |= Configuration.SCREENLAYOUT_LAYOUTDIR_RTL;
assertEquals("Secondary bounds must be reported.",
secondaryBounds,
- getBoundsForPosition(POSITION_END, TASK_BOUNDS, splitRule,
- mActivity, null /* miniDimensionsPair */));
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+
+ assertEquals("Primary bounds must be reported.",
+ primaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ }
+
+ @Test
+ public void testGetBoundsForPosition_splitHorizontally() {
+ final Rect primaryBounds = getSplitBounds(true /* isPrimary */,
+ true /* splitHorizontally */);
+ final Rect secondaryBounds = getSplitBounds(false /* isPrimary */,
+ true /* splitHorizontally */);
+ final TaskContainer.TaskProperties taskProperties = getTaskProperty();
+ SplitAttributes splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
+ .setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM)
+ .build();
+
+ assertEquals("Primary bounds must be reported.",
+ primaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+
+ assertEquals("Secondary bounds must be reported.",
+ secondaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+
+ splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(SplitAttributes.SplitType.RatioSplitType.splitEqually())
+ .setLayoutDirection(SplitAttributes.LayoutDirection.BOTTOM_TO_TOP)
+ .build();
+
+ assertEquals("Secondary bounds must be reported.",
+ secondaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+
+ assertEquals("Primary bounds must be reported.",
+ primaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ }
+
+ @Test
+ public void testGetBoundsForPosition_useHingeFallback() {
+ final Rect primaryBounds = getSplitBounds(true /* isPrimary */,
+ false /* splitHorizontally */);
+ final Rect secondaryBounds = getSplitBounds(false /* isPrimary */,
+ false /* splitHorizontally */);
+ final TaskContainer.TaskProperties taskProperties = getTaskProperty();
+ final SplitAttributes splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(new SplitAttributes.SplitType.HingeSplitType(
+ SplitAttributes.SplitType.RatioSplitType.splitEqually()
+ )).setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
+ .build();
+
+ // There's no hinge on the device. Use fallback SplitType.
+ doReturn(new WindowLayoutInfo(new ArrayList<>())).when(mWindowLayoutComponent)
+ .getCurrentWindowLayoutInfo(anyInt(), any());
+
+ assertEquals("PrimaryBounds must be reported.",
+ primaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+
+ assertEquals("SecondaryBounds must be reported.",
+ secondaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+
+ // Hinge is reported, but the host task is in multi-window mode. Still use fallback
+ // splitType.
+ doReturn(createWindowLayoutInfo()).when(mWindowLayoutComponent)
+ .getCurrentWindowLayoutInfo(anyInt(), any());
+ taskProperties.getConfiguration().windowConfiguration
+ .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+
+ assertEquals("PrimaryBounds must be reported.",
+ primaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+
+ assertEquals("SecondaryBounds must be reported.",
+ secondaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+
+ // Hinge is reported, and the host task is in fullscreen, but layout direction doesn't match
+ // folding area orientation. Still use fallback splitType.
+ doReturn(createWindowLayoutInfo()).when(mWindowLayoutComponent)
+ .getCurrentWindowLayoutInfo(anyInt(), any());
+ taskProperties.getConfiguration().windowConfiguration
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+
+ assertEquals("PrimaryBounds must be reported.",
+ primaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+
+ assertEquals("SecondaryBounds must be reported.",
+ secondaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ }
+
+ @Test
+ public void testGetBoundsForPosition_fallbackToExpandContainers() {
+ final TaskContainer.TaskProperties taskProperties = getTaskProperty();
+ final SplitAttributes splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(new SplitAttributes.SplitType.HingeSplitType(
+ new SplitAttributes.SplitType.ExpandContainersSplitType()
+ )).setLayoutDirection(SplitAttributes.LayoutDirection.LEFT_TO_RIGHT)
+ .build();
+
assertEquals("Task bounds must be reported.",
new Rect(),
- getBoundsForPosition(POSITION_FILL, TASK_BOUNDS, splitRule,
- mActivity, null /* miniDimensionsPair */));
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
- Pair<Size, Size> minDimensionsPair = new Pair<>(
- new Size(primaryBounds.width() + 1, primaryBounds.height() + 1), null);
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
+ new Rect(),
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
+ }
- assertEquals("Fullscreen bounds must be reported because of min dimensions.",
+ @Test
+ public void testGetBoundsForPosition_useHingeSplitType() {
+ final TaskContainer.TaskProperties taskProperties = getTaskProperty();
+ final SplitAttributes splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(new SplitAttributes.SplitType.HingeSplitType(
+ new SplitAttributes.SplitType.ExpandContainersSplitType()
+ )).setLayoutDirection(SplitAttributes.LayoutDirection.TOP_TO_BOTTOM)
+ .build();
+ final WindowLayoutInfo windowLayoutInfo = createWindowLayoutInfo();
+ doReturn(windowLayoutInfo).when(mWindowLayoutComponent)
+ .getCurrentWindowLayoutInfo(anyInt(), any());
+ final Rect hingeBounds = windowLayoutInfo.getDisplayFeatures().get(0).getBounds();
+ final Rect primaryBounds = new Rect(
+ TASK_BOUNDS.left,
+ TASK_BOUNDS.top,
+ TASK_BOUNDS.right,
+ hingeBounds.top
+ );
+ final Rect secondaryBounds = new Rect(
+ TASK_BOUNDS.left,
+ hingeBounds.bottom,
+ TASK_BOUNDS.right,
+ TASK_BOUNDS.bottom
+ );
+
+ assertEquals("PrimaryBounds must be reported.",
+ primaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_START, taskProperties, splitAttributes));
+
+ assertEquals("SecondaryBounds must be reported.",
+ secondaryBounds,
+ mPresenter.getBoundsForPosition(POSITION_END, taskProperties, splitAttributes));
+ assertEquals("Task bounds must be reported.",
new Rect(),
- getBoundsForPosition(POSITION_START, TASK_BOUNDS,
- splitRule, mActivity, minDimensionsPair));
+ mPresenter.getBoundsForPosition(POSITION_FILL, taskProperties, splitAttributes));
}
@Test
public void testExpandSplitContainerIfNeeded() {
- SplitContainer splitContainer = mock(SplitContainer.class);
Activity secondaryActivity = createMockActivity();
SplitRule splitRule = createSplitRule(mActivity, secondaryActivity);
TaskFragmentContainer primaryTf = mController.newContainer(mActivity, TASK_ID);
TaskFragmentContainer secondaryTf = mController.newContainer(secondaryActivity, TASK_ID);
- doReturn(splitRule).when(splitContainer).getSplitRule();
- doReturn(primaryTf).when(splitContainer).getPrimaryContainer();
- doReturn(secondaryTf).when(splitContainer).getSecondaryContainer();
+ SplitContainer splitContainer = new SplitContainer(primaryTf, secondaryActivity,
+ secondaryTf, splitRule, SPLIT_ATTRIBUTES);
assertThrows(IllegalArgumentException.class, () ->
mPresenter.expandSplitContainerIfNeeded(mTransaction, splitContainer, mActivity,
@@ -224,11 +441,13 @@ public class SplitPresenterTest {
splitContainer, mActivity, secondaryActivity, null /* secondaryIntent */));
verify(mPresenter, never()).expandTaskFragment(any(), any());
+ splitContainer.setSplitAttributes(SPLIT_ATTRIBUTES);
doReturn(createActivityInfoWithMinDimensions()).when(secondaryActivity).getActivityInfo();
assertEquals(RESULT_EXPAND_FAILED_NO_TF_INFO, mPresenter.expandSplitContainerIfNeeded(
mTransaction, splitContainer, mActivity, secondaryActivity,
null /* secondaryIntent */));
+ splitContainer.setSplitAttributes(SPLIT_ATTRIBUTES);
primaryTf.setInfo(mTransaction, createMockTaskFragmentInfo(primaryTf, mActivity));
secondaryTf.setInfo(mTransaction,
createMockTaskFragmentInfo(secondaryTf, secondaryActivity));
@@ -238,6 +457,7 @@ public class SplitPresenterTest {
verify(mPresenter).expandTaskFragment(mTransaction, primaryTf.getTaskFragmentToken());
verify(mPresenter).expandTaskFragment(mTransaction, secondaryTf.getTaskFragmentToken());
+ splitContainer.setSplitAttributes(SPLIT_ATTRIBUTES);
clearInvocations(mPresenter);
assertEquals(RESULT_EXPANDED, mPresenter.expandSplitContainerIfNeeded(mTransaction,
@@ -256,6 +476,7 @@ public class SplitPresenterTest {
final SplitPairRule rule = new SplitPairRule.Builder(pair ->
pair.first == mActivity && pair.second == secondaryActivity, pair -> false,
metrics -> true)
+ .setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
.setShouldClearTop(false)
.build();
@@ -268,6 +489,49 @@ public class SplitPresenterTest {
assertTrue(secondaryTf.isAbove(primaryTf));
}
+ @Test
+ public void testComputeSplitAttributes() {
+ final SplitPairRule splitPairRule = new SplitPairRule.Builder(
+ activityPair -> true,
+ activityIntentPair -> true,
+ windowMetrics -> windowMetrics.getBounds().equals(TASK_BOUNDS))
+ .setFinishSecondaryWithPrimary(DEFAULT_FINISH_SECONDARY_WITH_PRIMARY)
+ .setFinishPrimaryWithSecondary(DEFAULT_FINISH_PRIMARY_WITH_SECONDARY)
+ .setDefaultSplitAttributes(SPLIT_ATTRIBUTES)
+ .build();
+ final TaskContainer.TaskProperties taskProperties = getTaskProperty();
+
+ assertEquals(SPLIT_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties,
+ splitPairRule, null /* minDimensionsPair */));
+
+ final Pair<Size, Size> minDimensionsPair = new Pair<>(
+ new Size(TASK_BOUNDS.width(), TASK_BOUNDS.height()), null);
+
+ assertEquals(EXPAND_CONTAINERS_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties,
+ splitPairRule, minDimensionsPair));
+
+ taskProperties.getConfiguration().windowConfiguration.setBounds(new Rect(
+ TASK_BOUNDS.left + 1, TASK_BOUNDS.top + 1, TASK_BOUNDS.right + 1,
+ TASK_BOUNDS.bottom + 1));
+
+ assertEquals(EXPAND_CONTAINERS_ATTRIBUTES, mPresenter.computeSplitAttributes(taskProperties,
+ splitPairRule, null /* minDimensionsPair */));
+
+ final SplitAttributes splitAttributes = new SplitAttributes.Builder()
+ .setSplitType(
+ new SplitAttributes.SplitType.HingeSplitType(
+ SplitAttributes.SplitType.RatioSplitType.splitEqually()
+ )
+ ).build();
+
+ mController.setSplitAttributesCalculator(params -> {
+ return splitAttributes;
+ });
+
+ assertEquals(splitAttributes, mPresenter.computeSplitAttributes(taskProperties,
+ splitPairRule, null /* minDimensionsPair */));
+ }
+
private Activity createMockActivity() {
final Activity activity = mock(Activity.class);
final Configuration activityConfig = new Configuration();
@@ -279,4 +543,10 @@ public class SplitPresenterTest {
doReturn(mock(IBinder.class)).when(activity).getActivityToken();
return activity;
}
+
+ private static TaskContainer.TaskProperties getTaskProperty() {
+ final Configuration configuration = new Configuration();
+ configuration.windowConfiguration.setBounds(TASK_BOUNDS);
+ return new TaskContainer.TaskProperties(DEFAULT_DISPLAY, configuration);
+ }
}
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
index dd67e48ef353..af9c6ba5c162 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskContainerTest.java
@@ -21,9 +21,10 @@ 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_UNDEFINED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_BOUNDS;
-import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -34,8 +35,10 @@ import static org.mockito.Mockito.mock;
import android.app.Activity;
import android.content.Intent;
+import android.content.res.Configuration;
import android.graphics.Rect;
import android.platform.test.annotations.Presubmit;
+import android.window.TaskFragmentParentInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -66,7 +69,7 @@ public class TaskContainerTest {
@Test
public void testIsTaskBoundsInitialized() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
assertFalse(taskContainer.isTaskBoundsInitialized());
@@ -77,7 +80,7 @@ public class TaskContainerTest {
@Test
public void testSetTaskBounds() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
assertFalse(taskContainer.setTaskBounds(new Rect()));
@@ -87,30 +90,24 @@ public class TaskContainerTest {
}
@Test
- public void testIsWindowingModeInitialized() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
-
- assertFalse(taskContainer.isWindowingModeInitialized());
-
- taskContainer.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-
- assertTrue(taskContainer.isWindowingModeInitialized());
- }
-
- @Test
public void testGetWindowingModeForSplitTaskFragment() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final Rect splitBounds = new Rect(0, 0, 500, 1000);
+ final Configuration configuration = new Configuration();
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
- taskContainer.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
+ DEFAULT_DISPLAY, true /* visible */));
assertEquals(WINDOWING_MODE_MULTI_WINDOW,
taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
- taskContainer.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM);
+ taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
+ DEFAULT_DISPLAY, true /* visible */));
assertEquals(WINDOWING_MODE_FREEFORM,
taskContainer.getWindowingModeForSplitTaskFragment(splitBounds));
@@ -123,22 +120,27 @@ public class TaskContainerTest {
@Test
public void testIsInPictureInPicture() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
+ final Configuration configuration = new Configuration();
assertFalse(taskContainer.isInPictureInPicture());
- taskContainer.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
+ DEFAULT_DISPLAY, true /* visible */));
assertFalse(taskContainer.isInPictureInPicture());
- taskContainer.setWindowingMode(WINDOWING_MODE_PINNED);
+ configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
+ taskContainer.updateTaskFragmentParentInfo(new TaskFragmentParentInfo(configuration,
+ DEFAULT_DISPLAY, true /* visible */));
assertTrue(taskContainer.isInPictureInPicture());
}
@Test
public void testIsEmpty() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
assertTrue(taskContainer.isEmpty());
@@ -155,7 +157,7 @@ public class TaskContainerTest {
@Test
public void testGetTopTaskFragmentContainer() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
assertNull(taskContainer.getTopTaskFragmentContainer());
final TaskFragmentContainer tf0 = new TaskFragmentContainer(null /* activity */,
@@ -169,7 +171,7 @@ public class TaskContainerTest {
@Test
public void testGetTopNonFinishingActivity() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
assertNull(taskContainer.getTopNonFinishingActivity());
final TaskFragmentContainer tf0 = mock(TaskFragmentContainer.class);
diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
index 082774e048a9..35415d816d8b 100644
--- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
+++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/TaskFragmentContainerTest.java
@@ -16,8 +16,8 @@
package androidx.window.extensions.embedding;
-import static androidx.window.extensions.embedding.EmbeddingTestUtils.TASK_ID;
import static androidx.window.extensions.embedding.EmbeddingTestUtils.createMockTaskFragmentInfo;
+import static androidx.window.extensions.embedding.EmbeddingTestUtils.createTestTaskContainer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
@@ -90,7 +90,7 @@ public class TaskFragmentContainerTest {
@Test
public void testNewContainer() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
// One of the activity and the intent must be non-null
assertThrows(IllegalArgumentException.class,
@@ -103,40 +103,39 @@ public class TaskFragmentContainerTest {
@Test
public void testFinish() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(mActivity,
null /* pendingAppearedIntent */, taskContainer, mController);
doReturn(container).when(mController).getContainerWithActivity(mActivity);
- final WindowContainerTransaction wct = new WindowContainerTransaction();
// Only remove the activity, but not clear the reference until appeared.
- container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
+ container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController);
- verify(mActivity).finish();
+ verify(mTransaction).finishActivity(mActivity.getActivityToken());
verify(mPresenter, never()).deleteTaskFragment(any(), any());
verify(mController, never()).removeContainer(any());
// Calling twice should not finish activity again.
- clearInvocations(mActivity);
- container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
+ clearInvocations(mTransaction);
+ container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController);
- verify(mActivity, never()).finish();
+ verify(mTransaction, never()).finishActivity(any());
verify(mPresenter, never()).deleteTaskFragment(any(), any());
verify(mController, never()).removeContainer(any());
// Remove all references after the container has appeared in server.
doReturn(new ArrayList<>()).when(mInfo).getActivities();
container.setInfo(mTransaction, mInfo);
- container.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
+ container.finish(true /* shouldFinishDependent */, mPresenter, mTransaction, mController);
- verify(mActivity, never()).finish();
- verify(mPresenter).deleteTaskFragment(wct, container.getTaskFragmentToken());
+ verify(mTransaction, never()).finishActivity(any());
+ verify(mPresenter).deleteTaskFragment(mTransaction, container.getTaskFragmentToken());
verify(mController).removeContainer(container);
}
@Test
public void testFinish_notFinishActivityThatIsReparenting() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container0 = new TaskFragmentContainer(mActivity,
null /* pendingAppearedIntent */, taskContainer, mController);
final TaskFragmentInfo info = createMockTaskFragmentInfo(container0, mActivity);
@@ -150,14 +149,14 @@ public class TaskFragmentContainerTest {
// The activity is requested to be reparented, so don't finish it.
container0.finish(true /* shouldFinishDependent */, mPresenter, wct, mController);
- verify(mActivity, never()).finish();
+ verify(mTransaction, never()).finishActivity(any());
verify(mPresenter).deleteTaskFragment(wct, container0.getTaskFragmentToken());
verify(mController).removeContainer(container0);
}
@Test
public void testSetInfo() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
// Pending activity should be cleared when it has appeared on server side.
final TaskFragmentContainer pendingActivityContainer = new TaskFragmentContainer(mActivity,
null /* pendingAppearedIntent */, taskContainer, mController);
@@ -185,7 +184,7 @@ public class TaskFragmentContainerTest {
@Test
public void testIsWaitingActivityAppear() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
mIntent, taskContainer, mController);
@@ -207,7 +206,7 @@ public class TaskFragmentContainerTest {
@Test
public void testAppearEmptyTimeout() {
doNothing().when(mController).onTaskFragmentAppearEmptyTimeout(any(), any());
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
mIntent, taskContainer, mController);
@@ -247,7 +246,7 @@ public class TaskFragmentContainerTest {
@Test
public void testCollectNonFinishingActivities() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
mIntent, taskContainer, mController);
List<Activity> activities = container.collectNonFinishingActivities();
@@ -275,7 +274,7 @@ public class TaskFragmentContainerTest {
@Test
public void testAddPendingActivity() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
mIntent, taskContainer, mController);
container.addPendingAppearedActivity(mActivity);
@@ -289,7 +288,7 @@ public class TaskFragmentContainerTest {
@Test
public void testIsAbove() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container0 = new TaskFragmentContainer(null /* activity */,
mIntent, taskContainer, mController);
final TaskFragmentContainer container1 = new TaskFragmentContainer(null /* activity */,
@@ -301,7 +300,7 @@ public class TaskFragmentContainerTest {
@Test
public void testGetBottomMostActivity() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
mIntent, taskContainer, mController);
container.addPendingAppearedActivity(mActivity);
@@ -318,7 +317,7 @@ public class TaskFragmentContainerTest {
@Test
public void testOnActivityDestroyed() {
- final TaskContainer taskContainer = new TaskContainer(TASK_ID);
+ final TaskContainer taskContainer = createTestTaskContainer();
final TaskFragmentContainer container = new TaskFragmentContainer(null /* activity */,
mIntent, taskContainer, mController);
container.addPendingAppearedActivity(mActivity);
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
index e9a1721fba2a..2c766d81d611 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/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml
index 40c4c35773f8..36c24c1b6b08 100644
--- a/libs/WindowManager/Shell/res/values-af/strings.xml
+++ b/libs/WindowManager/Shell/res/values-af/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerakwessies?\nTik om aan te pas"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sien en doen meer"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Sleep ’n ander program in vir verdeelde skerm"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dubbeltik buite ’n program om dit te herposisioneer"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Het dit"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Vou uit vir meer inligting."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimeer"</string>
diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml
index a183a0bf7e85..dff8f3fb2677 100644
--- a/libs/WindowManager/Shell/res/values-am/strings.xml
+++ b/libs/WindowManager/Shell/res/values-am/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"የካሜራ ችግሮች አሉ?\nዳግም ለማበጀት መታ ያድርጉ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ተጨማሪ ይመልከቱ እና ያድርጉ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ለተከፈለ ማያ ገጽ ሌላ መተግበሪያ ይጎትቱ"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ቦታውን ለመቀየር ከመተግበሪያው ውጪ ሁለቴ መታ ያድርጉ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ገባኝ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ለተጨማሪ መረጃ ይዘርጉ።"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"አስፋ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ar/strings.xml b/libs/WindowManager/Shell/res/values-ar/strings.xml
index 98f11dd99957..fa9d2c24dd33 100644
--- a/libs/WindowManager/Shell/res/values-ar/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ar/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"هل هناك مشاكل في الكاميرا؟\nانقر لإعادة الضبط."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ألم يتم حل المشكلة؟\nانقر للعودة"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"أليس هناك مشاكل في الكاميرا؟ انقر للإغلاق."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"استخدام تطبيقات متعدّدة في وقت واحد"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"اسحب تطبيقًا آخر لاستخدام وضع تقسيم الشاشة."</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"انقر مرّتين خارج تطبيق لتغيير موضعه."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"حسنًا"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"التوسيع للحصول على مزيد من المعلومات"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"تكبير"</string>
diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml
index 18cb3ffc61ab..039b7e2dafcc 100644
--- a/libs/WindowManager/Shell/res/values-as/strings.xml
+++ b/libs/WindowManager/Shell/res/values-as/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"কেমেৰাৰ কোনো সমস্যা হৈছে নেকি?\nপুনৰ খাপ খোৱাবলৈ টিপক"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"চাওক আৰু অধিক কৰক"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"বিভাজিত স্ক্ৰীনৰ বাবে অন্য এটা এপ্‌ টানি আনি এৰক"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"এপ্‌টোৰ স্থান সলনি কৰিবলৈ ইয়াৰ বাহিৰত দুবাৰ টিপক"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"বুজি পালোঁ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"অধিক তথ্যৰ বাবে বিস্তাৰ কৰক।"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"সৰ্বাধিক মাত্ৰালৈ বঢ়াওক"</string>
diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml
index 2040288cd726..36229187fa8b 100644
--- a/libs/WindowManager/Shell/res/values-az/strings.xml
+++ b/libs/WindowManager/Shell/res/values-az/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera problemi var?\nBərpa etmək üçün toxunun"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ardını görün və edin"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Bölünmüş ekrandan istifadə etmək üçün başqa tətbiqi sürüşdürüb gətirin"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tətbiqin yerini dəyişmək üçün kənarına iki dəfə toxunun"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ətraflı məlumat üçün genişləndirin."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Böyüdün"</string>
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 b2b484e9d249..e65268a280b6 100644
--- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Imate problema sa kamerom?\nDodirnite da biste ponovo uklopili"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vidite i uradite više"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Prevucite drugu aplikaciju da biste koristili podeljeni ekran"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da biste promenili njenu poziciju"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Važi"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite za još informacija."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Uvećajte"</string>
diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml
index 59db9923f714..31fcc171060b 100644
--- a/libs/WindowManager/Shell/res/values-be/strings.xml
+++ b/libs/WindowManager/Shell/res/values-be/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Праблемы з камерай?\nНацісніце, каб пераабсталяваць"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Адначасова выконвайце розныя задачы"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Перацягніце іншую праграму, каб выкарыстоўваць падзелены экран"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Двойчы націсніце экран па-за праграмай, каб перамясціць яе"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Зразумела"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Разгарнуць для дадатковай інфармацыі"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Разгарнуць"</string>
diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml
index 8b5c4a95b1bc..0944d216dd0f 100644
--- a/libs/WindowManager/Shell/res/values-bg/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bg/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблеми с камерата?\nДокоснете за ремонтиране"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Преглеждайте и правете повече неща"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Преместете друго приложение с плъзгане, за да преминете в режим за разделен екран"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Докоснете два пъти извън дадено приложение, за да промените позицията му"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Разбрах"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Разгъване за още информация."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Увеличаване"</string>
diff --git a/libs/WindowManager/Shell/res/values-bn/strings.xml b/libs/WindowManager/Shell/res/values-bn/strings.xml
index d7ff018a2fdf..87eb9ff0e4f7 100644
--- a/libs/WindowManager/Shell/res/values-bn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bn/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ক্যামেরা সংক্রান্ত সমস্যা?\nরিফিট করতে ট্যাপ করুন"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এখনও সমাধান হয়নি?\nরিভার্ট করার জন্য ট্যাপ করুন"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ক্যামেরা সংক্রান্ত সমস্যা নেই? বাতিল করতে ট্যাপ করুন।"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"দেখুন ও আরও অনেক কিছু করুন"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"স্প্লিট স্ক্রিনের জন্য অন্য অ্যাপে টেনে আনুন"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"কোনও অ্যাপের স্থান পরিবর্তন করতে তার বাইরে ডবল ট্যাপ করুন"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"বুঝেছি"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"আরও তথ্যের জন্য বড় করুন।"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"বড় করুন"</string>
diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml
index f4c456083147..01463c242682 100644
--- a/libs/WindowManager/Shell/res/values-bs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-bs/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s kamerom?\nDodirnite da ponovo namjestite"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Pogledajte i učinite više"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Prevucite još jednu aplikaciju za podijeljeni ekran"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da promijenite njen položaj"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Razumijem"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite za više informacija."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimiziranje"</string>
diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml
index c4e6b0d5c80b..c8d0bcc1b2ef 100644
--- a/libs/WindowManager/Shell/res/values-ca/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ca/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tens problemes amb la càmera?\nToca per resoldre\'ls"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta i fes més coses"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arrossega una altra aplicació per utilitzar la pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Fes doble toc fora d\'una aplicació per canviar-ne la posició"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entesos"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Desplega per obtenir més informació."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximitza"</string>
diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml
index 8b0d1ff9abde..7012294c0a88 100644
--- a/libs/WindowManager/Shell/res/values-cs/strings.xml
+++ b/libs/WindowManager/Shell/res/values-cs/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lepší zobrazení a více možností"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Přetáhnutím druhé aplikace použijete rozdělenou obrazovku"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvojitým klepnutím mimo aplikaci změníte její umístění"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Rozbalením zobrazíte další informace."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximalizovat"</string>
diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml
index 94faf4124b7f..e3c99ae5e2f8 100644
--- a/libs/WindowManager/Shell/res/values-da/strings.xml
+++ b/libs/WindowManager/Shell/res/values-da/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du problemer med dit kamera?\nTryk for at gendanne det oprindelige format"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gør mere"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Træk en anden app hertil for at bruge opdelt skærm"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tryk to gange uden for en app for at justere dens placering"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Udvid for at få flere oplysninger."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimér"</string>
diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml
index 397144594bb9..d231b63109ce 100644
--- a/libs/WindowManager/Shell/res/values-de/strings.xml
+++ b/libs/WindowManager/Shell/res/values-de/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Probleme mit der Kamera?\nZum Anpassen tippen."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Mehr sehen und erledigen"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Weitere App hineinziehen, um den Bildschirm zu teilen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Außerhalb einer App doppeltippen, um die Position zu ändern"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ok"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Für weitere Informationen maximieren."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximieren"</string>
diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml
index 023c2d68023b..0a4f88a951c4 100644
--- a/libs/WindowManager/Shell/res/values-el/strings.xml
+++ b/libs/WindowManager/Shell/res/values-el/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Προβλήματα με την κάμερα;\nΠατήστε για επιδιόρθωση."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Δείτε και κάντε περισσότερα"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Σύρετε σε μια άλλη εφαρμογή για διαχωρισμό οθόνης"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Πατήστε δύο φορές έξω από μια εφαρμογή για να αλλάξετε τη θέση της"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Το κατάλαβα"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ανάπτυξη για περισσότερες πληροφορίες."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Μεγιστοποίηση"</string>
diff --git a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
index b5b6670d0990..042bc8a5bd4d 100644
--- a/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es-rUS/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Tienes problemas con la cámara?\nPresiona para reajustarla"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se resolvió?\nPresiona para revertir los cambios"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No tienes problemas con la cámara? Presionar para descartar."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Aprovecha más"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arrastra otra app para el modo de pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Presiona dos veces fuera de una app para cambiar su ubicación"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expande para obtener más información."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-es/strings.xml b/libs/WindowManager/Shell/res/values-es/strings.xml
index e38fc09d74d4..9234ad224f1b 100644
--- a/libs/WindowManager/Shell/res/values-es/strings.xml
+++ b/libs/WindowManager/Shell/res/values-es/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"¿Problemas con la cámara?\nToca para reajustar"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"¿No se ha solucionado?\nToca para revertir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"¿No hay problemas con la cámara? Toca para cerrar."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta más información y haz más"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arrastra otra aplicación para activar la pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toca dos veces fuera de una aplicación para cambiarla de posición"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Mostrar más información"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml
index f46845265388..ea5005d655b5 100644
--- a/libs/WindowManager/Shell/res/values-et/strings.xml
+++ b/libs/WindowManager/Shell/res/values-et/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kas teil on kaameraprobleeme?\nPuudutage ümberpaigutamiseks."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vaadake ja tehke rohkem"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Lohistage muusse rakendusse, et jagatud ekraanikuva kasutada"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Topeltpuudutage rakendusest väljaspool, et selle asendit muuta"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Selge"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Laiendage lisateabe saamiseks."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimeeri"</string>
diff --git a/libs/WindowManager/Shell/res/values-eu/strings.xml b/libs/WindowManager/Shell/res/values-eu/strings.xml
index 2c1ee82f34b2..1e5e48558493 100644
--- a/libs/WindowManager/Shell/res/values-eu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-eu/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Arazoak dauzkazu kamerarekin?\nBerriro doitzeko, sakatu hau."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ez al da konpondu?\nLeheneratzeko, sakatu hau."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ez daukazu arazorik kamerarekin? Baztertzeko, sakatu hau."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ikusi eta egin gauza gehiago"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Pantaila zatituta ikusteko, arrastatu beste aplikazio bat"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Aplikazioaren posizioa aldatzeko, sakatu birritan haren kanpoaldea"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ados"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Informazio gehiago lortzeko, zabaldu hau."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizatu"</string>
diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml
index 6b7bf4d9048b..df43d55745a7 100644
--- a/libs/WindowManager/Shell/res/values-fa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fa/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"دوربین مشکل دارد؟\nبرای تنظیم مجدد اندازه ضربه بزنید"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن ضربه بزنید"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن ضربه بزنید."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"از چندین برنامه به‌طور هم‌زمان استفاده کنید"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"برای حالت صفحهٔ دونیمه، در برنامه‌ای دیگر بکشید"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"برای جابه‌جا کردن برنامه، بیرون از آن دوضربه بزنید"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"متوجه‌ام"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"برای اطلاعات بیشتر، گسترده کنید."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"بزرگ کردن"</string>
diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml
index b82a7cc4e8f7..a4acec4b6701 100644
--- a/libs/WindowManager/Shell/res/values-fi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fi/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Onko kameran kanssa ongelmia?\nKorjaa napauttamalla"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Näe ja tee enemmän"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Käytä jaettua näyttöä vetämällä tähän toinen sovellus"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Kaksoisnapauta sovelluksen ulkopuolella, jos haluat siirtää sitä"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Katso lisätietoja laajentamalla."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Suurenna"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
index 449dc27f0d70..acc97f88358e 100644
--- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo?\nTouchez pour réajuster"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et en faire plus"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Faites glisser une autre application pour utiliser l\'écran partagé"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Touchez deux fois à côté d\'une application pour la repositionner"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développer pour en savoir plus."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string>
diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml
index 15148b71e79d..d063f71347c7 100644
--- a/libs/WindowManager/Shell/res/values-fr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-fr/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problèmes d\'appareil photo ?\nAppuyez pour réajuster"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et interagir plus"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Appuyez deux fois en dehors d\'une appli pour la repositionner"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Développez pour obtenir plus d\'informations"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Agrandir"</string>
diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml
index b848fd0539e9..2cd8a4a060f2 100644
--- a/libs/WindowManager/Shell/res/values-gl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gl/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Tes problemas coa cámara?\nToca para reaxustala"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ver e facer máis"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arrastra outra aplicación para usar a pantalla dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toca dúas veces fóra da aplicación para cambiala de posición"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendido"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Despregar para obter máis información."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml
index 79d27a24d758..2ade063ecd3c 100644
--- a/libs/WindowManager/Shell/res/values-gu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-gu/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"કૅમેરામાં સમસ્યાઓ છે?\nફરીથી ફિટ કરવા માટે ટૅપ કરો"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"જુઓ અને બીજું ઘણું કરો"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"સ્ક્રીન વિભાજન માટે કોઈ અન્ય ઍપમાં ખેંચો"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"કોઈ ઍપની જગ્યા બદલવા માટે, તેની બહાર બે વાર ટૅપ કરો"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"સમજાઈ ગયું"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"વધુ માહિતી માટે મોટું કરો."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"મોટું કરો"</string>
diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml
index e803abe08511..0fd83d3065aa 100644
--- a/libs/WindowManager/Shell/res/values-hi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hi/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्या कैमरे से जुड़ी कोई समस्या है?\nफिर से फ़िट करने के लिए टैप करें"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पूरी जानकारी लेकर, बेहतर तरीके से काम करें"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"स्प्लिट स्क्रीन के लिए, दूसरे ऐप्लिकेशन को खींचें और छोड़ें"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"किसी ऐप्लिकेशन की जगह बदलने के लिए, उसके बाहर दो बार टैप करें"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ठीक है"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ज़्यादा जानकारी के लिए बड़ा करें."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"बड़ा करें"</string>
diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml
index 07b946d4bd3a..6ea911dc127c 100644
--- a/libs/WindowManager/Shell/res/values-hr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hr/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi s fotoaparatom?\nDodirnite za popravak"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Gledajte i učinite više"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Povucite drugu aplikaciju unutra da biste podijelili zaslon"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvaput dodirnite izvan aplikacije da biste je premjestili"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Shvaćam"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Proširite da biste saznali više."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimiziraj"</string>
diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml
index 08b35bfc4a59..e149f5c73e43 100644
--- a/libs/WindowManager/Shell/res/values-hu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hu/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamerával kapcsolatos problémába ütközött?\nKoppintson a megoldáshoz."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Több mindent láthat és tehet"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Húzzon ide egy másik alkalmazást az osztott képernyő használatához"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Koppintson duplán az alkalmazáson kívül az áthelyezéséhez"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Értem"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Kibontással további információkhoz juthat."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Teljes méret"</string>
diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml
index 9d81e8cb544e..070fb9470db5 100644
--- a/libs/WindowManager/Shell/res/values-hy/strings.xml
+++ b/libs/WindowManager/Shell/res/values-hy/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Տեսախցիկի հետ կապված խնդիրնե՞ր կան։\nՀպեք՝ վերակարգավորելու համար։"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Միաժամանակ կատարեք մի քանի առաջադրանք"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Քաշեք մյուս հավելվածի մեջ՝ էկրանի տրոհումն օգտագործելու համար"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Կրկնակի հպեք հավելվածի կողքին՝ այն տեղափոխելու համար"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Եղավ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Ծավալեք՝ ավելին իմանալու համար։"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Ծավալել"</string>
diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml
index f74e83f702c9..b5a1de166e82 100644
--- a/libs/WindowManager/Shell/res/values-in/strings.xml
+++ b/libs/WindowManager/Shell/res/values-in/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Masalah kamera?\nKetuk untuk memperbaiki"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih banyak hal"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Tarik aplikasi lain untuk menggunakan layar terpisah"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketuk dua kali di luar aplikasi untuk mengubah posisinya"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Oke"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Luaskan untuk melihat informasi selengkapnya."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimalkan"</string>
diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml
index 71f6ec77ccd2..4e935a2bc5cd 100644
--- a/libs/WindowManager/Shell/res/values-is/strings.xml
+++ b/libs/WindowManager/Shell/res/values-is/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Myndavélavesen?\nÝttu til að breyta stærð"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sjáðu og gerðu meira"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Dragðu annað forrit inn til að nota skjáskiptingu"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ýttu tvisvar utan við forrit til að færa það"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ég skil"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Stækka til að sjá frekari upplýsingar."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Stækka"</string>
diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml
index 458313567df3..c4b572185618 100644
--- a/libs/WindowManager/Shell/res/values-it/strings.xml
+++ b/libs/WindowManager/Shell/res/values-it/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemi con la fotocamera?\nTocca per risolverli"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Visualizza più contenuti e fai di più"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Trascina in un\'altra app per usare lo schermo diviso"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tocca due volte fuori da un\'app per riposizionarla"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Espandi per avere ulteriori informazioni."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Ingrandisci"</string>
diff --git a/libs/WindowManager/Shell/res/values-iw/strings.xml b/libs/WindowManager/Shell/res/values-iw/strings.xml
index bd52cd827e83..edd2cb6411de 100644
--- a/libs/WindowManager/Shell/res/values-iw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-iw/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"בעיות במצלמה?\nאפשר להקיש כדי לבצע התאמה מחדש"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"הבעיה לא נפתרה?\nאפשר להקיש כדי לחזור לגרסה הקודמת"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"אין בעיות במצלמה? אפשר להקיש כדי לסגור."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"רוצה לראות ולעשות יותר?"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"צריך לגרור אפליקציה אחרת כדי להשתמש במסך מפוצל"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"צריך להקיש הקשה כפולה מחוץ לאפליקציה כדי למקם אותה מחדש"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"הבנתי"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"מרחיבים כדי לקבל מידע נוסף."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"הגדלה"</string>
diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml
index 98b3ba61dd7f..721ef6c66a86 100644
--- a/libs/WindowManager/Shell/res/values-ja/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ja/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"カメラに関する問題の場合は、\nタップすると修正できます"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"表示を拡大して機能を強化"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"分割画面にするにはもう 1 つのアプリをドラッグしてください"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"位置を変えるにはアプリの外側をダブルタップしてください"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"開くと詳細が表示されます。"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml
index fbf0b5ec41c4..d4aaba0ca05a 100644
--- a/libs/WindowManager/Shell/res/values-ka/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ka/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"კამერად პრობლემები აქვს?\nშეეხეთ გამოსასწორებლად"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"მეტის ნახვა და გაკეთება"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ეკრანის გასაყოფად ჩავლებით გადაიტანეთ სხვა აპში"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ორმაგად შეეხეთ აპის გარშემო სივრცეს, რათა ის სხვაგან გადაიტანოთ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"გასაგებია"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"დამატებითი ინფორმაციისთვის გააფართოეთ."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"მაქსიმალურად გაშლა"</string>
diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml
index a75dc3c26a20..a4ff2a93f00b 100644
--- a/libs/WindowManager/Shell/res/values-kk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kk/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада қателер шықты ма?\nЖөндеу үшін түртіңіз."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Қосымша ақпаратты қарап, әрекеттер жасау"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Экранды бөлу үшін басқа қолданбаға сүйреңіз."</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Қолданбаның орнын өзгерту үшін одан тыс жерді екі рет түртіңіз."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Түсінікті"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Толығырақ ақпарат алу үшін терезені жайыңыз."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Жаю"</string>
diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml
index 6913c0663f9b..47367f5ea526 100644
--- a/libs/WindowManager/Shell/res/values-km/strings.xml
+++ b/libs/WindowManager/Shell/res/values-km/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"មានបញ្ហា​ពាក់ព័ន្ធនឹង​កាមេរ៉ាឬ?\nចុចដើម្បី​ដោះស្រាយ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបាន​ដោះស្រាយ​បញ្ហានេះទេឬ?\nចុចដើម្បី​ត្រឡប់"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមាន​បញ្ហាពាក់ព័ន្ធនឹង​កាមេរ៉ាទេឬ? ចុចដើម្បី​ច្រានចោល។"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"មើលឃើញ និងធ្វើបានកាន់តែច្រើន"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"អូស​កម្មវិធី​មួយ​ទៀត​ចូល ដើម្បី​ប្រើ​មុខងារ​បំបែកអេក្រង់"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ចុចពីរដង​នៅ​ក្រៅ​កម្មវិធី ដើម្បី​ប្ដូរ​ទីតាំង​កម្មវិធី​នោះ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"យល់ហើយ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ពង្រីកដើម្បីទទួលបានព័ត៌មានបន្ថែម។"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ពង្រីក"</string>
diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml
index 1f246d5189fb..001e12298cdf 100644
--- a/libs/WindowManager/Shell/res/values-kn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-kn/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿವೆಯೇ?\nಮರುಹೊಂದಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ನೋಡಿ ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ಮಾಡಿ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್‌ಗಾಗಿ ಮತ್ತೊಂದು ಆ್ಯಪ್‌ನಲ್ಲಿ ಎಳೆಯಿರಿ"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ಆ್ಯಪ್ ಒಂದರ ಸ್ಥಾನವನ್ನು ಬದಲಾಯಿಸಲು ಅದರ ಹೊರಗೆ ಡಬಲ್-ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ಸರಿ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ಇನ್ನಷ್ಟು ಮಾಹಿತಿಗಾಗಿ ವಿಸ್ತೃತಗೊಳಿಸಿ."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ಹಿಗ್ಗಿಸಿ"</string>
diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml
index e878cef5e75a..27e294ebd5d6 100644
--- a/libs/WindowManager/Shell/res/values-ko/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ko/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"카메라 문제가 있나요?\n해결하려면 탭하세요."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"더 많은 정보를 보고 더 많은 작업을 처리하세요"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"화면 분할을 사용하려면 다른 앱을 드래그해 가져옵니다."</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"앱 위치를 조정하려면 앱 외부를 두 번 탭합니다."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"확인"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"추가 정보는 펼쳐서 확인하세요."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"최대화"</string>
diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml
index 20c462e6ad05..d46fb66cff54 100644
--- a/libs/WindowManager/Shell/res/values-ky/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ky/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерада маселелер келип чыктыбы?\nОңдоо үчүн таптаңыз"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Көрүп, көбүрөөк нерселерди жасаңыз"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Экранды бөлүү үчүн башка колдонмону сүйрөңүз"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Колдонмону жылдыруу үчүн сырт жагын эки жолу таптаңыз"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Түшүндүм"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Толук маалымат алуу үчүн жайып көрүңүз."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Чоңойтуу"</string>
diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml
index 3f4a881c042b..d7d34d703431 100644
--- a/libs/WindowManager/Shell/res/values-lo/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lo/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ?\nແຕະເພື່ອປັບໃໝ່"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອ​ປິດ​ໄວ້."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ເບິ່ງ ແລະ ເຮັດຫຼາຍຂຶ້ນ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ລາກແອັບອື່ນເຂົ້າມາເພື່ອແບ່ງໜ້າຈໍ"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ແຕະສອງເທື່ອໃສ່ນອກແອັບໃດໜຶ່ງເພື່ອຈັດຕຳແໜ່ງຂອງມັນຄືນໃໝ່"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ຂະຫຍາຍເພື່ອເບິ່ງຂໍ້ມູນເພີ່ມເຕີມ."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ຂະຫຍາຍໃຫຍ່ສຸດ"</string>
diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml
index 515a263a099f..4b16f63666d0 100644
--- a/libs/WindowManager/Shell/res/values-lt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lt/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Iškilo problemų dėl kameros?\nPalieskite, kad pritaikytumėte iš naujo"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daugiau turinio ir funkcijų"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Vilkite kitoje programoje, kad galėtumėte naudoti išskaidyto ekrano režimą"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dukart palieskite už programos ribų, kad pakeistumėte jos poziciją"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Supratau"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Išskleiskite, jei reikia daugiau informacijos."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Padidinti"</string>
diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml
index 080c6f4682ff..36743cfd171f 100644
--- a/libs/WindowManager/Shell/res/values-lv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-lv/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Vai ir problēmas ar kameru?\nPieskarieties, lai tās novērstu."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Uzziniet un paveiciet vairāk"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Lai izmantotu sadalītu ekrānu, ievelciet vēl vienu lietotni"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Lai pārvietotu lietotni, veiciet dubultskārienu ārpus lietotnes"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Labi"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Izvērsiet, lai iegūtu plašāku informāciju."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimizēt"</string>
diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml
index 47ed632fbffe..52a9377af22e 100644
--- a/libs/WindowManager/Shell/res/values-mk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mk/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми со камерата?\nДопрете за да се совпадне повторно"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Погледнете и направете повеќе"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Повлечете во друга апликација за поделен екран"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Допрете двапати надвор од некоја апликација за да ја преместите"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Сфатив"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Проширете за повеќе информации."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Зголеми"</string>
diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml
index faa6a30fdf78..343ccf13427a 100644
--- a/libs/WindowManager/Shell/res/values-ml/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ml/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ക്യാമറ പ്രശ്നങ്ങളുണ്ടോ?\nശരിയാക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"കൂടുതൽ കാണുക, ചെയ്യുക"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"സ്‌ക്രീൻ വിഭജന മോഡിന്, മറ്റൊരു ആപ്പ് വലിച്ചിടുക"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ആപ്പിന്റെ സ്ഥാനം മാറ്റാൻ അതിന് പുറത്ത് ഡബിൾ ടാപ്പ് ചെയ്യുക"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"മനസ്സിലായി"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"കൂടുതൽ വിവരങ്ങൾക്ക് വികസിപ്പിക്കുക."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"വലുതാക്കുക"</string>
diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml
index 8e8d06db0436..5370ef68dbd4 100644
--- a/libs/WindowManager/Shell/res/values-mn/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mn/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Камерын асуудал гарсан уу?\nДахин тааруулахын тулд товшино уу"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Харж илүү ихийг хий"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Дэлгэцийг хуваахын тулд өөр апп руу чирнэ үү"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Аппыг дахин байрлуулахын тулд гадна талд нь хоёр товшино"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ойлголоо"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Нэмэлт мэдээлэл авах бол дэлгэнэ үү."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Томруулах"</string>
diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml
index 4fc03b276889..1433ce4b0ab2 100644
--- a/libs/WindowManager/Shell/res/values-mr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-mr/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"कॅमेराशी संबंधित काही समस्या आहेत का?\nपुन्हा फिट करण्यासाठी टॅप करा"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्‍यासाठी टॅप करा."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पहा आणि आणखी बरेच काही करा"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"स्प्लिट-स्क्रीन वापरण्यासाठी दुसऱ्या ॲपमध्ये ड्रॅग करा"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ॲपची स्थिती पुन्हा बदलण्यासाठी, त्याच्या बाहेर दोनदा टॅप करा"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"समजले"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"अधिक माहितीसाठी विस्तार करा."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"मोठे करा"</string>
diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml
index 2db225aba9f4..04805dac59fe 100644
--- a/libs/WindowManager/Shell/res/values-ms/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ms/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Isu kamera?\nKetik untuk memuatkan semula"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Seret apl lain untuk skrin pisah"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Ketik dua kali di luar apl untuk menempatkan semula apl itu"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Kembangkan untuk mendapatkan maklumat lanjut."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimumkan"</string>
diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml
index e8ab4b04b4dc..092cea20fb08 100644
--- a/libs/WindowManager/Shell/res/values-my/strings.xml
+++ b/libs/WindowManager/Shell/res/values-my/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ကင်မရာပြဿနာလား။\nပြင်ဆင်ရန် တို့ပါ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ကြည့်ပြီး ပိုမိုလုပ်ဆောင်ပါ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"မျက်နှာပြင် ခွဲ၍ပြသနိုင်ရန် နောက်အက်ပ်တစ်ခုကို ဖိဆွဲပါ"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"နေရာပြန်ချရန် အက်ပ်အပြင်ဘက်ကို နှစ်ချက်တို့ပါ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ရပြီ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"နောက်ထပ်အချက်အလက်များအတွက် ချဲ့နိုင်သည်။"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ချဲ့ရန်"</string>
diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml
index 278de2d8f1b1..22fa7f2efafc 100644
--- a/libs/WindowManager/Shell/res/values-nb/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nb/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gjør mer"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Dra inn en annen app for å bruke delt skjerm"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dobbelttrykk utenfor en app for å flytte den"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Greit"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Vis for å få mer informasjon."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimer"</string>
diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml
index 529f4011b565..9502421e46b6 100644
--- a/libs/WindowManager/Shell/res/values-ne/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ne/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"क्यामेरासम्बन्धी समस्या देखियो?\nसमस्या हल गर्न ट्याप गर्नुहोस्"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"थप कुरा हेर्नुहोस् र गर्नुहोस्"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"स्प्लिट स्क्रिन मोड प्रयोग गर्न अर्को एप ड्रयाग एन्ड ड्रप गर्नुहोस्"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"तपाईं जुन एपको स्थिति मिलाउन चाहनुहुन्छ सोही एपको बाहिर डबल ट्याप गर्नुहोस्"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"बुझेँ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"थप जानकारी प्राप्त गर्न चाहनुहुन्छ भने एक्स्पान्ड गर्नुहोस्।"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ठुलो बनाउनुहोस्"</string>
diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml
index 88c220cd7dfe..37fe1fd2b3ed 100644
--- a/libs/WindowManager/Shell/res/values-nl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-nl/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Cameraproblemen?\nTik om opnieuw passend te maken."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zie en doe meer"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Sleep een andere app hier naartoe om het scherm te splitsen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dubbeltik naast een app om deze opnieuw te positioneren"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Uitvouwen voor meer informatie."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximaliseren"</string>
diff --git a/libs/WindowManager/Shell/res/values-or/strings.xml b/libs/WindowManager/Shell/res/values-or/strings.xml
index 2c82fbcf9ff7..ca31f3cf84a7 100644
--- a/libs/WindowManager/Shell/res/values-or/strings.xml
+++ b/libs/WindowManager/Shell/res/values-or/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"କ୍ୟାମେରାରେ ସମସ୍ୟା ଅଛି?\nପୁଣି ଫିଟ କରିବାକୁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ଏହାର ସମାଧାନ ହୋଇନାହିଁ?\nଫେରିଯିବା ପାଇଁ ଟାପ କରନ୍ତୁ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"କ୍ୟାମେରାରେ କିଛି ସମସ୍ୟା ନାହିଁ? ଖାରଜ କରିବାକୁ ଟାପ କରନ୍ତୁ।"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ଦେଖନ୍ତୁ ଏବଂ ଆହୁରି ଅନେକ କିଛି କରନ୍ତୁ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ସ୍ପ୍ଲିଟ-ସ୍କ୍ରିନ ପାଇଁ ଅନ୍ୟ ଏକ ଆପକୁ ଡ୍ରାଗ କରନ୍ତୁ"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ଏକ ଆପକୁ ରିପୋଜିସନ କରିବା ପାଇଁ ଏହାର ବାହାରେ ଦୁଇଥର-ଟାପ କରନ୍ତୁ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ବୁଝିଗଲି"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ଅଧିକ ସୂଚନା ପାଇଁ ବିସ୍ତାର କରନ୍ତୁ।"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ବଡ଼ କରନ୍ତୁ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml
index 8c7229f58730..1f118c901243 100644
--- a/libs/WindowManager/Shell/res/values-pa/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pa/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਸਮੱਸਿਆਵਾਂ ਹਨ?\nਮੁੜ-ਫਿੱਟ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ਦੇਖੋ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਕਰੋ"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੇ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਵਿੱਚ ਘਸੀਟੋ"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ਕਿਸੇ ਐਪ ਦੀ ਜਗ੍ਹਾ ਬਦਲਣ ਲਈ ਉਸ ਦੇ ਬਾਹਰ ਡਬਲ ਟੈਪ ਕਰੋ"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ਸਮਝ ਲਿਆ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ ਵਿਸਤਾਰ ਕਰੋ।"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ਵੱਡਾ ਕਰੋ"</string>
diff --git a/libs/WindowManager/Shell/res/values-pl/strings.xml b/libs/WindowManager/Shell/res/values-pl/strings.xml
index 315b95e88265..4171aebcf99a 100644
--- a/libs/WindowManager/Shell/res/values-pl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pl/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemy z aparatem?\nKliknij, aby dopasować"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Naprawa się nie udała?\nKliknij, aby cofnąć"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Brak problemów z aparatem? Kliknij, aby zamknąć"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobacz i zrób więcej"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Przeciągnij drugą aplikację, aby podzielić ekran"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Kliknij dwukrotnie poza aplikacją, aby ją przenieść"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Rozwiń, aby wyświetlić więcej informacji."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksymalizuj"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
index 48781ad437bd..7a62410ad125 100644
--- a/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rBR/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arraste outro app para a tela dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de um app para reposicionar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Abra para ver mais informações."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
index e2be183e0ada..00549026483f 100644
--- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmara?\nToque aqui para reajustar"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arraste outra app para usar o ecrã dividido"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de uma app para a reposicionar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Expandir para obter mais informações"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-pt/strings.xml b/libs/WindowManager/Shell/res/values-pt/strings.xml
index 48781ad437bd..7a62410ad125 100644
--- a/libs/WindowManager/Shell/res/values-pt/strings.xml
+++ b/libs/WindowManager/Shell/res/values-pt/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problemas com a câmera?\nToque para ajustar o enquadramento"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"O problema não foi corrigido?\nToque para reverter"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Não tem problemas com a câmera? Toque para dispensar."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Arraste outro app para a tela dividida"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Toque duas vezes fora de um app para reposicionar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Entendi"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Abra para ver mais informações."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizar"</string>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml
index 65b0472805cf..352f81b7b6f0 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings.xml
@@ -17,21 +17,21 @@
<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">"Închideți"</string>
- <string name="pip_phone_expand" msgid="2579292903468287504">"Extindeți"</string>
+ <string name="pip_phone_close" msgid="5783752637260411309">"Închide"</string>
+ <string name="pip_phone_expand" msgid="2579292903468287504">"Extinde"</string>
<string name="pip_phone_settings" msgid="5468987116750491918">"Setări"</string>
- <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accesați ecranul împărțit"</string>
+ <string name="pip_phone_enter_split" msgid="7042877263880641911">"Accesează ecranul împărțit"</string>
<string name="pip_menu_title" msgid="5393619322111827096">"Meniu"</string>
<string name="pip_menu_accessibility_title" msgid="8129016817688656249">"Meniu picture-in-picture"</string>
<string name="pip_notification_title" msgid="1347104727641353453">"<xliff:g id="NAME">%s</xliff:g> este în modul picture-in-picture"</string>
<string name="pip_notification_message" msgid="8854051911700302620">"Dacă nu doriți ca <xliff:g id="NAME">%s</xliff:g> să utilizeze această funcție, atingeți pentru a deschide setările și dezactivați-o."</string>
- <string name="pip_play" msgid="3496151081459417097">"Redați"</string>
- <string name="pip_pause" msgid="690688849510295232">"Întrerupeți"</string>
+ <string name="pip_play" msgid="3496151081459417097">"Redă"</string>
+ <string name="pip_pause" msgid="690688849510295232">"Întrerupe"</string>
<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="accessibility_action_pip_unstash" msgid="7467499339610437646">"Anulează 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>
@@ -50,19 +50,19 @@
<string name="accessibility_action_divider_bottom_full" msgid="2831868345092314060">"Partea de jos pe ecran complet"</string>
<string name="one_handed_tutorial_title" msgid="4583241688067426350">"Folosirea modului cu o mână"</string>
<string name="one_handed_tutorial_description" msgid="3486582858591353067">"Pentru a ieși, glisați în sus din partea de jos a ecranului sau atingeți oriunde deasupra ferestrei aplicației"</string>
- <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Activați modul cu o mână"</string>
+ <string name="accessibility_action_start_one_handed" msgid="5070337354072861426">"Activează modul cu o mână"</string>
<string name="accessibility_action_stop_one_handed" msgid="1369940261782179442">"Părăsiți modul cu o mână"</string>
<string name="bubbles_settings_button_description" msgid="1301286017420516912">"Setări pentru baloanele <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="bubble_overflow_button_content_description" msgid="8160974472718594382">"Suplimentar"</string>
- <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Adăugați înapoi în stivă"</string>
+ <string name="bubble_accessibility_action_add_back" msgid="1830101076853540953">"Adaugă înapoi în stivă"</string>
<string name="bubble_content_description_single" msgid="8495748092720065813">"<xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g> de la <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> de la <xliff:g id="APP_NAME">%2$s</xliff:g> și încă <xliff:g id="BUBBLE_COUNT">%3$d</xliff:g>"</string>
- <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mutați în stânga sus"</string>
- <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mutați în dreapta sus"</string>
- <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mutați în stânga jos"</string>
- <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mutați în dreapta jos"</string>
+ <string name="bubble_accessibility_action_move_top_left" msgid="2644118920500782758">"Mută în stânga sus"</string>
+ <string name="bubble_accessibility_action_move_top_right" msgid="5864594920870245525">"Mută în dreapta sus"</string>
+ <string name="bubble_accessibility_action_move_bottom_left" msgid="850271002773745634">"Mută în stânga jos"</string>
+ <string name="bubble_accessibility_action_move_bottom_right" msgid="2107626346109206352">"Mută în dreapta jos"</string>
<string name="bubbles_app_settings" msgid="3617224938701566416">"Setări <xliff:g id="NOTIFICATION_TITLE">%1$s</xliff:g>"</string>
- <string name="bubble_dismiss_text" msgid="8816558050659478158">"Închideți balonul"</string>
+ <string name="bubble_dismiss_text" msgid="8816558050659478158">"Închide balonul"</string>
<string name="bubbles_dont_bubble_conversation" msgid="310000317885712693">"Nu afișați conversația în balon"</string>
<string name="bubbles_user_education_title" msgid="2112319053732691899">"Chat cu baloane"</string>
<string name="bubbles_user_education_description" msgid="4215862563054175407">"Conversațiile noi apar ca pictograme flotante sau baloane. Atingeți pentru a deschide balonul. Trageți pentru a-l muta."</string>
@@ -72,21 +72,18 @@
<string name="bubble_overflow_empty_title" msgid="2397251267073294968">"Nu există baloane recente"</string>
<string name="bubble_overflow_empty_subtitle" msgid="2627417924958633713">"Baloanele recente și baloanele respinse vor apărea aici"</string>
<string name="notification_bubble_title" msgid="6082910224488253378">"Balon"</string>
- <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionați"</string>
+ <string name="manage_bubbles_text" msgid="7730624269650594419">"Gestionează"</string>
<string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Balonul a fost respins."</string>
<string name="restart_button_description" msgid="6712141648865547958">"Atingeți ca să reporniți aplicația pentru o vizualizare mai bună."</string>
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Aveți probleme cu camera foto?\nAtingeți pentru a reîncadra"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ați remediat problema?\nAtingeți pentru a reveni"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu aveți probleme cu camera foto? Atingeți pentru a închide."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vezi și fă mai multe"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Trage în altă aplicație pentru a folosi ecranul împărțit"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Atinge de două ori lângă o aplicație pentru a o repoziționa"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
- <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Extindeți pentru mai multe informații"</string>
+ <string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Extinde pentru mai multe informații"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximizați"</string>
<string name="minimize_button_text" msgid="271592547935841753">"Minimizează"</string>
- <string name="close_button_text" msgid="2913281996024033299">"Închideți"</string>
+ <string name="close_button_text" msgid="2913281996024033299">"Închide"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
index 56dadb2e5e65..36df2864a752 100644
--- a/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values-ro/strings_tv.xml
@@ -19,16 +19,16 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="notification_channel_tv_pip" msgid="2576686079160402435">"Picture-in-picture"</string>
<string name="pip_notification_unknown_title" msgid="2729870284350772311">"(Program fără titlu)"</string>
- <string name="pip_close" msgid="2955969519031223530">"Închideți"</string>
+ <string name="pip_close" msgid="2955969519031223530">"Închide"</string>
<string name="pip_fullscreen" msgid="7278047353591302554">"Ecran complet"</string>
- <string name="pip_move" msgid="158770205886688553">"Mutați"</string>
- <string name="pip_expand" msgid="1051966011679297308">"Extindeți"</string>
- <string name="pip_collapse" msgid="3903295106641385962">"Restrângeți"</string>
- <string name="pip_edu_text" msgid="3672999496647508701">" Apăsați de două ori "<annotation icon="home_icon">"butonul ecran de pornire"</annotation>" pentru comenzi"</string>
+ <string name="pip_move" msgid="158770205886688553">"Mută"</string>
+ <string name="pip_expand" msgid="1051966011679297308">"Extinde"</string>
+ <string name="pip_collapse" msgid="3903295106641385962">"Restrânge"</string>
+ <string name="pip_edu_text" msgid="3672999496647508701">" Apasă de două ori "<annotation icon="home_icon">"butonul ecran de pornire"</annotation>" pentru comenzi"</string>
<string name="a11y_pip_menu_entered" msgid="5106343214776801614">"Meniu picture-in-picture."</string>
- <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mutați spre stânga"</string>
- <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mutați spre dreapta"</string>
- <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mutați în sus"</string>
- <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mutați în jos"</string>
+ <string name="a11y_action_pip_move_left" msgid="6612980937817141583">"Mută spre stânga"</string>
+ <string name="a11y_action_pip_move_right" msgid="1119409122645529936">"Mută spre dreapta"</string>
+ <string name="a11y_action_pip_move_up" msgid="98502616918621959">"Mută în sus"</string>
+ <string name="a11y_action_pip_move_down" msgid="3858802832725159740">"Mută în jos"</string>
<string name="a11y_action_pip_move_done" msgid="1486845365134416210">"Gata"</string>
</resources>
diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml
index 8affb9add8b9..1a77e428ccf3 100644
--- a/libs/WindowManager/Shell/res/values-ru/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ru/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблемы с камерой?\nНажмите, чтобы исправить."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Выполняйте несколько задач одновременно"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Перетащите сюда другое приложение, чтобы использовать разделение экрана."</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Чтобы переместить приложение, дважды нажмите рядом с ним."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ОК"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Развернуть, чтобы узнать больше."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Развернуть"</string>
diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml
index c816065b3585..dc89ec3a836c 100644
--- a/libs/WindowManager/Shell/res/values-si/strings.xml
+++ b/libs/WindowManager/Shell/res/values-si/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"කැමරා ගැටලුද?\nයළි සවි කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්‍රතිවර්තනය කිරීමට තට්ටු කරන්න"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"බලන්න සහ තවත් දේ කරන්න"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"බෙදුම් තිරය සඳහා වෙනත් යෙදුමකට අදින්න"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"යෙදුමක් නැවත ස්ථානගත කිරීමට පිටතින් දෙවරක් තට්ටු කරන්න"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"තේරුණා"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"වැඩිදුර තොරතුරු සඳහා දිග හරින්න"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"විහිදන්න"</string>
diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml
index 9dfbd54942e3..aec8501fca23 100644
--- a/libs/WindowManager/Shell/res/values-sk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sk/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s kamerou?\nKlepnutím znova upravte."</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobrazte si a zvládnite toho viac"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Rozdelenú obrazovku aktivujete presunutím ďalšie aplikácie"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvojitým klepnutím mimo aplikácie zmeníte jej pozíciu"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Dobre"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Po rozbalení sa dozviete viac."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maximalizovať"</string>
diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml
index 5bf943b96383..44462b6edb04 100644
--- a/libs/WindowManager/Shell/res/values-sl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sl/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Težave s fotoaparatom?\nDotaknite se za vnovično prilagoditev"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Oglejte si in naredite več"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Za razdeljeni zaslon povlecite sem še eno aplikacijo."</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Dvakrat se dotaknite zunaj aplikacije, če jo želite prestaviti."</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"V redu"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Razširitev za več informacij"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimiraj"</string>
diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml
index 09559990dc37..6e26ec6214ef 100644
--- a/libs/WindowManager/Shell/res/values-sq/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sq/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Ka probleme me kamerën?\nTrokit për ta ripërshtatur"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Shiko dhe bëj më shumë"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Zvarrite në një aplikacion tjetër për ekranin e ndarë"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Trokit dy herë jashtë një aplikacioni për ta ripozicionuar"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"E kuptova"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Zgjeroje për më shumë informacion."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Maksimizo"</string>
diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml
index b42d98e422ff..94725cbdf367 100644
--- a/libs/WindowManager/Shell/res/values-sr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sr/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Имате проблема са камером?\nДодирните да бисте поново уклопили"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Видите и урадите више"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Превуците другу апликацију да бисте користили подељени екран"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Двапут додирните изван апликације да бисте променили њену позицију"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Важи"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Проширите за још информација."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Увећајте"</string>
diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml
index 162b57dbd224..6b6ba2b2d298 100644
--- a/libs/WindowManager/Shell/res/values-sv/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sv/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problem med kameran?\nTryck för att anpassa på nytt"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se och gör mer"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Dra till en annan app för läget Delad skärm"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Tryck snabbt två gånger utanför en app för att flytta den"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Utöka för mer information."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Utöka"</string>
diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml
index 3844d01f5221..102e9cfae2e4 100644
--- a/libs/WindowManager/Shell/res/values-sw/strings.xml
+++ b/libs/WindowManager/Shell/res/values-sw/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Je, kuna hitilafu za kamera?\nGusa ili urekebishe"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Angalia na ufanye zaidi"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Buruta ndani programu nyingine ili utumie hali ya skrini iliyogawanywa"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Gusa mara mbili nje ya programu ili uihamishe"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Nimeelewa"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Panua ili upate maelezo zaidi."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Panua"</string>
diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml
index c45409c3713c..c2166fdb136a 100644
--- a/libs/WindowManager/Shell/res/values-ta/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ta/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"கேமரா தொடர்பான சிக்கல்களா?\nமீண்டும் பொருத்த தட்டவும்"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"பலவற்றைப் பார்த்தல் மற்றும் செய்தல்"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"திரைப் பிரிப்புக்கு மற்றொரு ஆப்ஸை இழுக்கலாம்"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"ஆப்ஸை இடம் மாற்ற அதன் வெளியில் இருமுறை தட்டலாம்"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"சரி"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"கூடுதல் தகவல்களுக்கு விரிவாக்கலாம்."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"பெரிதாக்கும்"</string>
diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml
index 0a61f937e3b6..ef0f9e7a6cff 100644
--- a/libs/WindowManager/Shell/res/values-te/strings.xml
+++ b/libs/WindowManager/Shell/res/values-te/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"కెమెరా సమస్యలు ఉన్నాయా?\nరీఫిట్ చేయడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"చూసి, మరిన్ని చేయండి"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"స్ప్లిట్-స్క్రీన్ కోసం మరొక యాప్‌లోకి లాగండి"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"యాప్ స్థానాన్ని మార్చడానికి దాని వెలుపల డబుల్-ట్యాప్ చేయండి"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"అర్థమైంది"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"మరింత సమాచారం కోసం విస్తరించండి."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"గరిష్టీకరించండి"</string>
diff --git a/libs/WindowManager/Shell/res/values-th/strings.xml b/libs/WindowManager/Shell/res/values-th/strings.xml
index 0a41d450a0b5..7a7575d53035 100644
--- a/libs/WindowManager/Shell/res/values-th/strings.xml
+++ b/libs/WindowManager/Shell/res/values-th/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"หากพบปัญหากับกล้อง\nแตะเพื่อแก้ไข"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"หากไม่ได้แก้ไข\nแตะเพื่อเปลี่ยนกลับ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"หากไม่พบปัญหากับกล้อง แตะเพื่อปิด"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"รับชมและทำสิ่งต่างๆ ได้มากขึ้น"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"ลากไปไว้ในแอปอื่นเพื่อแยกหน้าจอ"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"แตะสองครั้งด้านนอกแอปเพื่อเปลี่ยนตำแหน่ง"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"รับทราบ"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"ขยายเพื่อดูข้อมูลเพิ่มเติม"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"ขยายใหญ่สุด"</string>
diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml
index e2727992777f..1c8d94f24dfa 100644
--- a/libs/WindowManager/Shell/res/values-tl/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tl/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"May mga isyu sa camera?\nI-tap para i-refit"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Tumingin at gumawa ng higit pa"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Mag-drag ng ibang app para sa split screen"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Mag-double tap sa labas ng app para baguhin ang posisyon nito"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"I-expand para sa higit pang impormasyon."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"I-maximize"</string>
diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml
index 050fa5fee690..82e3f5847df8 100644
--- a/libs/WindowManager/Shell/res/values-tr/strings.xml
+++ b/libs/WindowManager/Shell/res/values-tr/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kameranızda sorun mu var?\nDüzeltmek için dokunun"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daha fazlasını görün ve yapın"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Bölünmüş ekran için başka bir uygulamayı sürükleyin"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Yeniden konumlandırmak için uygulamanın dışına iki kez dokunun"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Anladım"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Daha fazla bilgi için genişletin."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Ekranı Kapla"</string>
diff --git a/libs/WindowManager/Shell/res/values-uk/strings.xml b/libs/WindowManager/Shell/res/values-uk/strings.xml
index d5f047fcaf1f..218d11eaba0a 100644
--- a/libs/WindowManager/Shell/res/values-uk/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uk/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Проблеми з камерою?\nНатисніть, щоб пристосувати"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблему не вирішено?\nНатисніть, щоб скасувати зміни"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немає проблем із камерою? Торкніться, щоб закрити."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Більше простору та можливостей"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Щоб перейти в режим розділення екрана, перетягніть сюди інший додаток"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Щоб перемістити додаток, двічі торкніться області поза ним"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"ОK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Розгорніть, щоб дізнатися більше."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Збільшити"</string>
diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml
index 700ecaa7cfda..4a9c079fc7d1 100644
--- a/libs/WindowManager/Shell/res/values-ur/strings.xml
+++ b/libs/WindowManager/Shell/res/values-ur/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"کیمرے کے مسائل؟\nدوبارہ فٹ کرنے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"دیکھیں اور بہت کچھ کریں"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"اسپلٹ اسکرین کے ليے دوسری ایپ میں گھسیٹیں"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"کسی ایپ کی پوزیشن تبدیل کرنے کے لیے اس ایپ کے باہر دو بار تھپتھپائیں"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"سمجھ آ گئی"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"مزید معلومات کے لیے پھیلائیں۔"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"بڑا کریں"</string>
diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml
index e843b0be5b1e..a063476e1aa8 100644
--- a/libs/WindowManager/Shell/res/values-uz/strings.xml
+++ b/libs/WindowManager/Shell/res/values-uz/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Kamera nosozmi?\nQayta moslash uchun bosing"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Yana boshqa amallar"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Ekranni ikkiga ajratish uchun boshqa ilovani bu yerga torting"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Qayta joylash uchun ilova tashqarisiga ikki marta bosing"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Batafsil axborot olish uchun kengaytiring."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Yoyish"</string>
diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml
index ec1eadbfa988..b47296590324 100644
--- a/libs/WindowManager/Shell/res/values-vi/strings.xml
+++ b/libs/WindowManager/Shell/res/values-vi/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Có vấn đề với máy ảnh?\nHãy nhấn để sửa lỗi"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Xem và làm được nhiều việc hơn"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Kéo vào một ứng dụng khác để chia đôi màn hình"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Nhấn đúp bên ngoài ứng dụng để đặt lại vị trí"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"OK"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Mở rộng để xem thêm thông tin."</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Phóng to"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
index 37d424402f0c..d7366952f155 100644
--- a/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rCN/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相机有问题?\n点按即可整修"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"没有解决此问题?\n点按即可恢复"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相机没有问题?点按即可忽略。"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"查看和处理更多任务"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"拖入另一个应用,即可使用分屏模式"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"在某个应用外连续点按两次,即可调整它的位置"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"展开即可了解详情。"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
index 170cd4cae753..8eda853fbf2b 100644
--- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題?\n輕按即可修正"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"拖入另一個應用程式即可分割螢幕"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"在應用程式外輕按兩下即可調整位置"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"知道了"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"展開即可查看詳情。"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
index 83f8520b2be4..71f4f2b96346 100644
--- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"相機有問題嗎?\n輕觸即可修正"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"拖進另一個應用程式即可使用分割畫面模式"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"在應用程式外輕觸兩下即可調整位置"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"我知道了"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"展開即可查看詳細資訊。"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"最大化"</string>
diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml
index 686310ac9881..f637912b8ed9 100644
--- a/libs/WindowManager/Shell/res/values-zu/strings.xml
+++ b/libs/WindowManager/Shell/res/values-zu/strings.xml
@@ -78,12 +78,9 @@
<string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Izinkinga zekhamera?\nThepha ukuze uyilinganise kabusha"</string>
<string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string>
<string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string>
- <!-- no translation found for letterbox_education_dialog_title (7739895354143295358) -->
- <skip />
- <!-- no translation found for letterbox_education_split_screen_text (6206339484068670830) -->
- <skip />
- <!-- no translation found for letterbox_education_reposition_text (4589957299813220661) -->
- <skip />
+ <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Bona futhi wenze okuningi"</string>
+ <string name="letterbox_education_split_screen_text" msgid="6206339484068670830">"Hudula kwenye i-app mayelana nokuhlukanisa isikrini"</string>
+ <string name="letterbox_education_reposition_text" msgid="4589957299813220661">"Thepha kabili ngaphandle kwe-app ukuze uyimise kabusha"</string>
<string name="letterbox_education_got_it" msgid="4057634570866051177">"Ngiyezwa"</string>
<string name="letterbox_education_expand_button_description" msgid="1729796567101129834">"Nweba ukuze uthole ulwazi olwengeziwe"</string>
<string name="maximize_button_text" msgid="1650859196290301963">"Khulisa"</string>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 5696b8dfa8e3..0bc70857a113 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -297,4 +297,28 @@
when the pinned stack size is overridden by app via minWidth/minHeight.
-->
<dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen>
+
+ <!-- The size of the drag handle / menu shown along with a floating task. -->
+ <dimen name="floating_task_menu_size">32dp</dimen>
+
+ <!-- The size of menu items in the floating task menu. -->
+ <dimen name="floating_task_menu_item_size">24dp</dimen>
+
+ <!-- The horizontal margin of menu items in the floating task menu. -->
+ <dimen name="floating_task_menu_item_padding">5dp</dimen>
+
+ <!-- The width of visible floating view region when stashed. -->
+ <dimen name="floating_task_stash_offset">32dp</dimen>
+
+ <!-- The amount of elevation for a floating task. -->
+ <dimen name="floating_task_elevation">8dp</dimen>
+
+ <!-- The amount of padding around the bottom and top of the task. -->
+ <dimen name="floating_task_vertical_padding">8dp</dimen>
+
+ <!-- The normal size of the dismiss target. -->
+ <dimen name="floating_task_dismiss_circle_size">150dp</dimen>
+
+ <!-- The smaller size of the dismiss target (shrinks when something is in the target). -->
+ <dimen name="floating_dismiss_circle_small">120dp</dimen>
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index 9230c22c5d95..ca977ed2cb94 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -179,6 +179,14 @@ public class RootTaskDisplayAreaOrganizer extends DisplayAreaOrganizer {
}
/**
+ * Returns the {@link DisplayAreaInfo} of the {@link DisplayAreaInfo#displayId}.
+ */
+ @Nullable
+ public DisplayAreaInfo getDisplayAreaInfo(int displayId) {
+ return mDisplayAreasInfo.get(displayId);
+ }
+
+ /**
* Applies the {@link DisplayAreaInfo} to the {@link DisplayAreaContext} specified by
* {@link DisplayAreaInfo#displayId}.
*/
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 43679364b443..dec1e38914e2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java
@@ -44,6 +44,7 @@ import android.util.Log;
import android.util.SparseArray;
import android.view.SurfaceControl;
import android.window.ITaskOrganizerController;
+import android.window.ScreenCapture;
import android.window.StartingWindowInfo;
import android.window.StartingWindowRemovalInfo;
import android.window.TaskAppearedInfo;
@@ -474,7 +475,7 @@ public class ShellTaskOrganizer extends TaskOrganizer implements
* Take a screenshot of a task.
*/
public void screenshotTask(RunningTaskInfo taskInfo, Rect crop,
- Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {
+ Consumer<ScreenCapture.ScreenshotHardwareBuffer> consumer) {
final TaskAppearedInfo info = mTasks.get(taskInfo.taskId);
if (info == null) {
return;
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 a8764e05c3e2..d76ad3d27c70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -517,7 +517,9 @@ public class TaskView extends SurfaceView implements SurfaceHolder.Callback,
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
}
- ActivityManager.RunningTaskInfo getTaskInfo() {
+ /** Returns the task info for the task in the TaskView. */
+ @Nullable
+ public ActivityManager.RunningTaskInfo getTaskInfo() {
return mTaskInfo;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
index cc4db933ec9f..591e3476ecd9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationAdapter.java
@@ -16,7 +16,6 @@
package com.android.wm.shell.activityembedding;
-import static android.graphics.Matrix.MSCALE_X;
import static android.graphics.Matrix.MTRANS_X;
import static android.graphics.Matrix.MTRANS_Y;
@@ -42,31 +41,45 @@ class ActivityEmbeddingAnimationAdapter {
*/
private static final int LAYER_NO_OVERRIDE = -1;
+ @NonNull
final Animation mAnimation;
+ @NonNull
final TransitionInfo.Change mChange;
+ @NonNull
final SurfaceControl mLeash;
+ /** Area in absolute coordinate that the animation surface shouldn't go beyond. */
+ @NonNull
+ private final Rect mWholeAnimationBounds = new Rect();
+ @NonNull
final Transformation mTransformation = new Transformation();
+ @NonNull
final float[] mMatrix = new float[9];
+ @NonNull
final float[] mVecs = new float[4];
+ @NonNull
final Rect mRect = new Rect();
private boolean mIsFirstFrame = true;
private int mOverrideLayer = LAYER_NO_OVERRIDE;
ActivityEmbeddingAnimationAdapter(@NonNull Animation animation,
@NonNull TransitionInfo.Change change) {
- this(animation, change, change.getLeash());
+ this(animation, change, change.getLeash(), change.getEndAbsBounds());
}
/**
* @param leash the surface to animate, which is not necessary the same as
- * {@link TransitionInfo.Change#getLeash()}, it can be a screenshot for example.
+ * {@link TransitionInfo.Change#getLeash()}, it can be a screenshot for example.
+ * @param wholeAnimationBounds area in absolute coordinate that the animation surface shouldn't
+ * go beyond.
*/
ActivityEmbeddingAnimationAdapter(@NonNull Animation animation,
- @NonNull TransitionInfo.Change change, @NonNull SurfaceControl leash) {
+ @NonNull TransitionInfo.Change change, @NonNull SurfaceControl leash,
+ @NonNull Rect wholeAnimationBounds) {
mAnimation = animation;
mChange = change;
mLeash = leash;
+ mWholeAnimationBounds.set(wholeAnimationBounds);
}
/**
@@ -96,23 +109,31 @@ class ActivityEmbeddingAnimationAdapter {
/** To be overridden by subclasses to adjust the animation surface change. */
void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
+ // Update the surface position and alpha.
final Point offset = mChange.getEndRelOffset();
mTransformation.getMatrix().postTranslate(offset.x, offset.y);
t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
t.setAlpha(mLeash, mTransformation.getAlpha());
- // Get current animation position.
+
+ // Get current surface bounds in absolute coordinate.
+ // positionX/Y are in local coordinate, so minus the local offset to get the slide amount.
final int positionX = Math.round(mMatrix[MTRANS_X]);
final int positionY = Math.round(mMatrix[MTRANS_Y]);
- // The exiting surface starts at position: Change#getEndRelOffset() and moves with
- // positionX varying. Offset our crop region by the amount we have slided so crop
- // regions stays exactly on the original container in split.
- final int cropOffsetX = offset.x - positionX;
- final int cropOffsetY = offset.y - positionY;
- final Rect cropRect = new Rect();
- cropRect.set(mChange.getEndAbsBounds());
- // Because window crop uses absolute position.
- cropRect.offsetTo(0, 0);
- cropRect.offset(cropOffsetX, cropOffsetY);
+ final Rect cropRect = new Rect(mChange.getEndAbsBounds());
+ cropRect.offset(positionX - offset.x, positionY - offset.y);
+
+ // Store the current offset of the surface top left from (0,0) in absolute coordinate.
+ final int offsetX = cropRect.left;
+ final int offsetY = cropRect.top;
+
+ // Intersect to make sure the animation happens within the whole animation bounds.
+ if (!cropRect.intersect(mWholeAnimationBounds)) {
+ // Hide the surface when it is outside of the animation area.
+ t.setAlpha(mLeash, 0);
+ }
+
+ // cropRect is in absolute coordinate, so we need to translate it to surface top left.
+ cropRect.offset(-offsetX, -offsetY);
t.setCrop(mLeash, cropRect);
}
@@ -127,53 +148,6 @@ class ActivityEmbeddingAnimationAdapter {
}
/**
- * Should be used when the {@link TransitionInfo.Change} is in split with others, and wants to
- * animate together as one. This adapter will offset the animation leash to make the animate of
- * two windows look like a single window.
- */
- static class SplitAdapter extends ActivityEmbeddingAnimationAdapter {
- private final boolean mIsLeftHalf;
- private final int mWholeAnimationWidth;
-
- /**
- * @param isLeftHalf whether this is the left half of the animation.
- * @param wholeAnimationWidth the whole animation windows width.
- */
- SplitAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change,
- boolean isLeftHalf, int wholeAnimationWidth) {
- super(animation, change);
- mIsLeftHalf = isLeftHalf;
- mWholeAnimationWidth = wholeAnimationWidth;
- if (wholeAnimationWidth == 0) {
- throw new IllegalArgumentException("SplitAdapter must provide wholeAnimationWidth");
- }
- }
-
- @Override
- void onAnimationUpdateInner(@NonNull SurfaceControl.Transaction t) {
- final Point offset = mChange.getEndRelOffset();
- float posX = offset.x;
- final float posY = offset.y;
- // This window is half of the whole animation window. Offset left/right to make it
- // look as one with the other half.
- mTransformation.getMatrix().getValues(mMatrix);
- final int changeWidth = mChange.getEndAbsBounds().width();
- final float scaleX = mMatrix[MSCALE_X];
- final float totalOffset = mWholeAnimationWidth * (1 - scaleX) / 2;
- final float curOffset = changeWidth * (1 - scaleX) / 2;
- final float offsetDiff = totalOffset - curOffset;
- if (mIsLeftHalf) {
- posX += offsetDiff;
- } else {
- posX -= offsetDiff;
- }
- mTransformation.getMatrix().postTranslate(posX, posY);
- t.setMatrix(mLeash, mTransformation.getMatrix(), mMatrix);
- t.setAlpha(mLeash, mTransformation.getAlpha());
- }
- }
-
- /**
* Should be used for the animation of the snapshot of a {@link TransitionInfo.Change} that has
* size change.
*/
@@ -181,7 +155,7 @@ class ActivityEmbeddingAnimationAdapter {
SnapshotAdapter(@NonNull Animation animation, @NonNull TransitionInfo.Change change,
@NonNull SurfaceControl snapshotLeash) {
- super(animation, change, snapshotLeash);
+ super(animation, change, snapshotLeash, change.getEndAbsBounds());
}
@Override
@@ -196,7 +170,9 @@ class ActivityEmbeddingAnimationAdapter {
void onAnimationEnd(@NonNull SurfaceControl.Transaction t) {
super.onAnimationEnd(t);
// Remove the screenshot leash after animation is finished.
- t.remove(mLeash);
+ if (mLeash.isValid()) {
+ t.remove(mLeash);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
index 7e0795d11153..1c0e6f726fbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunner.java
@@ -18,13 +18,14 @@ package com.android.wm.shell.activityembedding;
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManagerPolicyConstants.TYPE_LAYER_OFFSET;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
+import android.util.ArraySet;
import android.util.Log;
import android.view.SurfaceControl;
import android.view.animation.Animation;
@@ -40,6 +41,7 @@ import com.android.wm.shell.transition.Transitions;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import java.util.function.BiFunction;
/** To run the ActivityEmbedding animations. */
@@ -128,12 +130,20 @@ class ActivityEmbeddingAnimationRunner {
@NonNull
private List<ActivityEmbeddingAnimationAdapter> createAnimationAdapters(
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
+ boolean isChangeTransition = false;
for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getMode() == TRANSIT_CHANGE
+ if (change.hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW)) {
+ // Skip the animation if the windows are behind an app starting window.
+ return new ArrayList<>();
+ }
+ if (!isChangeTransition && change.getMode() == TRANSIT_CHANGE
&& !change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
- return createChangeAnimationAdapters(info, startTransaction);
+ isChangeTransition = true;
}
}
+ if (isChangeTransition) {
+ return createChangeAnimationAdapters(info, startTransaction);
+ }
if (Transitions.isClosingType(info.getType())) {
return createCloseAnimationAdapters(info);
}
@@ -169,15 +179,12 @@ class ActivityEmbeddingAnimationRunner {
final Rect openingWholeScreenBounds = new Rect();
final Rect closingWholeScreenBounds = new Rect();
for (TransitionInfo.Change change : info.getChanges()) {
- final Rect bounds = new Rect(change.getEndAbsBounds());
- final Point offset = change.getEndRelOffset();
- bounds.offsetTo(offset.x, offset.y);
if (Transitions.isOpeningType(change.getMode())) {
openingChanges.add(change);
- openingWholeScreenBounds.union(bounds);
+ openingWholeScreenBounds.union(change.getEndAbsBounds());
} else {
closingChanges.add(change);
- closingWholeScreenBounds.union(bounds);
+ closingWholeScreenBounds.union(change.getEndAbsBounds());
}
}
@@ -210,62 +217,76 @@ class ActivityEmbeddingAnimationRunner {
@NonNull BiFunction<TransitionInfo.Change, Rect, Animation> animationProvider,
@NonNull Rect wholeAnimationBounds) {
final Animation animation = animationProvider.apply(change, wholeAnimationBounds);
- final Rect bounds = new Rect(change.getEndAbsBounds());
- final Point offset = change.getEndRelOffset();
- bounds.offsetTo(offset.x, offset.y);
- if (bounds.left == wholeAnimationBounds.left
- && bounds.right != wholeAnimationBounds.right) {
- // This is the left split of the whole animation window.
- return new ActivityEmbeddingAnimationAdapter.SplitAdapter(animation, change,
- true /* isLeftHalf */, wholeAnimationBounds.width());
- } else if (bounds.left != wholeAnimationBounds.left
- && bounds.right == wholeAnimationBounds.right) {
- // This is the right split of the whole animation window.
- return new ActivityEmbeddingAnimationAdapter.SplitAdapter(animation, change,
- false /* isLeftHalf */, wholeAnimationBounds.width());
- }
- // Open/close window that fills the whole animation.
- return new ActivityEmbeddingAnimationAdapter(animation, change);
+ return new ActivityEmbeddingAnimationAdapter(animation, change, change.getLeash(),
+ wholeAnimationBounds);
}
@NonNull
private List<ActivityEmbeddingAnimationAdapter> createChangeAnimationAdapters(
@NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction) {
final List<ActivityEmbeddingAnimationAdapter> adapters = new ArrayList<>();
+ final Set<TransitionInfo.Change> handledChanges = new ArraySet<>();
+
+ // For the first iteration, we prepare the animation for the change type windows. This is
+ // needed because there may be window that is reparented while resizing. In such case, we
+ // will do the following:
+ // 1. Capture a screenshot from the Activity surface.
+ // 2. Attach the screenshot surface to the top of TaskFragment (Activity's parent) surface.
+ // 3. Animate the TaskFragment using Activity Change info (start/end bounds).
+ // This is because the TaskFragment surface/change won't contain the Activity's before its
+ // reparent.
for (TransitionInfo.Change change : info.getChanges()) {
- if (change.getMode() == TRANSIT_CHANGE
- && !change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
- // This is the window with bounds change.
- final WindowContainerToken parentToken = change.getParent();
- final Rect parentBounds;
- if (parentToken != null) {
- TransitionInfo.Change parentChange = info.getChange(parentToken);
- parentBounds = parentChange != null
- ? parentChange.getEndAbsBounds()
- : change.getEndAbsBounds();
- } else {
- parentBounds = change.getEndAbsBounds();
+ if (change.getMode() != TRANSIT_CHANGE
+ || change.getStartAbsBounds().equals(change.getEndAbsBounds())) {
+ continue;
+ }
+
+ // This is the window with bounds change.
+ handledChanges.add(change);
+ final WindowContainerToken parentToken = change.getParent();
+ TransitionInfo.Change boundsAnimationChange = change;
+ if (parentToken != null) {
+ // When the parent window is also included in the transition as an opening window,
+ // we would like to animate the parent window instead.
+ final TransitionInfo.Change parentChange = info.getChange(parentToken);
+ if (parentChange != null && Transitions.isOpeningType(parentChange.getMode())) {
+ // We won't create a separate animation for the parent, but to animate the
+ // parent for the child resizing.
+ handledChanges.add(parentChange);
+ boundsAnimationChange = parentChange;
}
- final Animation[] animations =
- mAnimationSpec.createChangeBoundsChangeAnimations(change, parentBounds);
+ }
+
+ final Animation[] animations = mAnimationSpec.createChangeBoundsChangeAnimations(change,
+ boundsAnimationChange.getEndAbsBounds());
+
+ // Create a screenshot based on change, but attach it to the top of the
+ // boundsAnimationChange.
+ final SurfaceControl screenshotLeash = getOrCreateScreenshot(change,
+ boundsAnimationChange, startTransaction);
+ if (screenshotLeash != null) {
// Adapter for the starting screenshot leash.
- final SurfaceControl screenshotLeash = createScreenshot(change, startTransaction);
- if (screenshotLeash != null) {
- // The screenshot leash will be removed in SnapshotAdapter#onAnimationEnd
- adapters.add(new ActivityEmbeddingAnimationAdapter.SnapshotAdapter(
- animations[0], change, screenshotLeash));
- } else {
- Log.e(TAG, "Failed to take screenshot for change=" + change);
- }
- // Adapter for the ending bounds changed leash.
- adapters.add(new ActivityEmbeddingAnimationAdapter.BoundsChangeAdapter(
- animations[1], change));
+ // The screenshot leash will be removed in SnapshotAdapter#onAnimationEnd
+ adapters.add(new ActivityEmbeddingAnimationAdapter.SnapshotAdapter(
+ animations[0], change, screenshotLeash));
+ } else {
+ Log.e(TAG, "Failed to take screenshot for change=" + change);
+ }
+ // Adapter for the ending bounds changed leash.
+ adapters.add(new ActivityEmbeddingAnimationAdapter.BoundsChangeAdapter(
+ animations[1], boundsAnimationChange));
+ }
+
+ // Handle the other windows that don't have bounds change in the same transition.
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if (handledChanges.contains(change)) {
+ // Skip windows that we have already handled in the previous iteration.
continue;
}
- // These are the other windows that don't have bounds change in the same transition.
final Animation animation;
- if (!TransitionInfo.isIndependent(change, info)) {
+ if (change.getParent() != null
+ && handledChanges.contains(info.getChange(change.getParent()))) {
// No-op if it will be covered by the changing parent window.
animation = ActivityEmbeddingAnimationSpec.createNoopAnimation(change);
} else if (Transitions.isClosingType(change.getMode())) {
@@ -278,13 +299,27 @@ class ActivityEmbeddingAnimationRunner {
return adapters;
}
- /** Takes a screenshot of the given {@link TransitionInfo.Change} surface. */
+ /**
+ * Takes a screenshot of the given {@code screenshotChange} surface if WM Core hasn't taken one.
+ * The screenshot leash should be attached to the {@code animationChange} surface which we will
+ * animate later.
+ */
@Nullable
- private SurfaceControl createScreenshot(@NonNull TransitionInfo.Change change,
- @NonNull SurfaceControl.Transaction startTransaction) {
- final Rect cropBounds = new Rect(change.getStartAbsBounds());
+ private SurfaceControl getOrCreateScreenshot(@NonNull TransitionInfo.Change screenshotChange,
+ @NonNull TransitionInfo.Change animationChange,
+ @NonNull SurfaceControl.Transaction t) {
+ final SurfaceControl screenshotLeash = screenshotChange.getSnapshot();
+ if (screenshotLeash != null) {
+ // If WM Core has already taken a screenshot, make sure it is reparented to the
+ // animation leash.
+ t.reparent(screenshotLeash, animationChange.getLeash());
+ return screenshotLeash;
+ }
+
+ // If WM Core hasn't taken a screenshot, take a screenshot now.
+ final Rect cropBounds = new Rect(screenshotChange.getStartAbsBounds());
cropBounds.offsetTo(0, 0);
- return ScreenshotUtils.takeScreenshot(startTransaction, change.getLeash(), cropBounds,
- Integer.MAX_VALUE);
+ return ScreenshotUtils.takeScreenshot(t, screenshotChange.getLeash(),
+ animationChange.getLeash(), cropBounds, Integer.MAX_VALUE);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
index 6f06f28caff2..ad0dddf77002 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationSpec.java
@@ -185,8 +185,10 @@ class ActivityEmbeddingAnimationSpec {
animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
? R.anim.task_fragment_open_enter
: R.anim.task_fragment_open_exit);
- final Rect bounds = change.getEndAbsBounds();
- animation.initialize(bounds.width(), bounds.height(),
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are opening at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are launching together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation;
@@ -203,8 +205,10 @@ class ActivityEmbeddingAnimationSpec {
animation = mTransitionAnimation.loadDefaultAnimationRes(isEnter
? R.anim.task_fragment_close_enter
: R.anim.task_fragment_close_exit);
- final Rect bounds = change.getEndAbsBounds();
- animation.initialize(bounds.width(), bounds.height(),
+ // Use the whole animation bounds instead of the change bounds, so that when multiple change
+ // targets are closing at the same time, the animation applied to each will be the same.
+ // Otherwise, we may see gap between the activities that are finishing together.
+ animation.initialize(wholeAnimationBounds.width(), wholeAnimationBounds.height(),
wholeAnimationBounds.width(), wholeAnimationBounds.height());
animation.scaleCurrentDuration(mTransitionAnimationScaleSetting);
return animation;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index e0004fcaa060..521a65cc4df6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -16,7 +16,8 @@
package com.android.wm.shell.activityembedding;
-import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
+import static android.window.TransitionInfo.FLAG_FILLS_TASK;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static java.util.Objects.requireNonNull;
@@ -84,12 +85,23 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- // TODO(b/207070762) Handle AE animation as a part of other transitions.
- // Only handle the transition if all containers are embedded.
+ boolean containsEmbeddingSplit = false;
for (TransitionInfo.Change change : info.getChanges()) {
- if (!isEmbedded(change)) {
+ if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+ // Only animate the transition if all changes are in a Task with ActivityEmbedding.
return false;
}
+ if (!containsEmbeddingSplit && !change.hasFlags(FLAG_FILLS_TASK)) {
+ // Whether the Task contains any ActivityEmbedding split before or after the
+ // transition.
+ containsEmbeddingSplit = true;
+ }
+ }
+ if (!containsEmbeddingSplit) {
+ // Let the system to play the default animation if there is no ActivityEmbedding split
+ // window. This allows to play the app customized animation when there is no embedding,
+ // such as the device is in a folded state.
+ return false;
}
// Start ActivityEmbedding animation.
@@ -119,8 +131,4 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle
}
callback.onTransitionFinished(null /* wct */, null /* wctCB */);
}
-
- private static boolean isEmbedded(@NonNull TransitionInfo.Change change) {
- return (change.getFlags() & FLAG_IS_EMBEDDED) != 0;
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
index b5a575499a3a..922472a26113 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java
@@ -59,6 +59,8 @@ import java.util.concurrent.Executor;
public class Bubble implements BubbleViewProvider {
private static final String TAG = "Bubble";
+ public static final String KEY_APP_BUBBLE = "key_app_bubble";
+
private final String mKey;
@Nullable
private final String mGroupKey;
@@ -164,6 +166,14 @@ public class Bubble implements BubbleViewProvider {
private PendingIntent mDeleteIntent;
/**
+ * Used only for a special bubble in the stack that has the key {@link #KEY_APP_BUBBLE}.
+ * There can only be one of these bubbles in the stack and this intent will be populated for
+ * that bubble.
+ */
+ @Nullable
+ private Intent mAppIntent;
+
+ /**
* Create a bubble with limited information based on given {@link ShortcutInfo}.
* Note: Currently this is only being used when the bubble is persisted to disk.
*/
@@ -192,6 +202,22 @@ public class Bubble implements BubbleViewProvider {
mBubbleMetadataFlagListener = listener;
}
+ public Bubble(Intent intent,
+ UserHandle user,
+ Executor mainExecutor) {
+ mKey = KEY_APP_BUBBLE;
+ mGroupKey = null;
+ mLocusId = null;
+ mFlags = 0;
+ mUser = user;
+ mShowBubbleUpdateDot = false;
+ mMainExecutor = mainExecutor;
+ mTaskId = INVALID_TASK_ID;
+ mAppIntent = intent;
+ mDesiredHeight = Integer.MAX_VALUE;
+ mPackageName = intent.getPackage();
+ }
+
@VisibleForTesting(visibility = PRIVATE)
public Bubble(@NonNull final BubbleEntry entry,
final Bubbles.BubbleMetadataFlagListener listener,
@@ -417,6 +443,9 @@ public class Bubble implements BubbleViewProvider {
mShortcutInfo = info.shortcutInfo;
mAppName = info.appName;
+ if (mTitle == null) {
+ mTitle = mAppName;
+ }
mFlyoutMessage = info.flyoutMessage;
mBadgeBitmap = info.badgeBitmap;
@@ -520,7 +549,7 @@ public class Bubble implements BubbleViewProvider {
* @return the last time this bubble was updated or accessed, whichever is most recent.
*/
long getLastActivity() {
- return Math.max(mLastUpdated, mLastAccessed);
+ return isAppBubble() ? Long.MAX_VALUE : Math.max(mLastUpdated, mLastAccessed);
}
/**
@@ -719,6 +748,15 @@ public class Bubble implements BubbleViewProvider {
return mDeleteIntent;
}
+ @Nullable
+ Intent getAppBubbleIntent() {
+ return mAppIntent;
+ }
+
+ boolean isAppBubble() {
+ return KEY_APP_BUBBLE.equals(mKey);
+ }
+
Intent getSettingsIntent(final Context context) {
final Intent intent = new Intent(Settings.ACTION_APP_NOTIFICATION_BUBBLE_SETTINGS);
intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName());
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 0dfba3464f23..93413dbe7e5f 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
@@ -1017,6 +1017,20 @@ public class BubbleController implements ConfigurationChangeListener {
}
/**
+ * Adds a bubble for a specific intent. These bubbles are <b>not</b> backed by a notification
+ * and remain until the user dismisses the bubble or bubble stack. Only one intent bubble
+ * is supported at a time.
+ *
+ * @param intent the intent to display in the bubble expanded view.
+ */
+ public void addAppBubble(Intent intent) {
+ if (intent == null || intent.getPackage() == null) return;
+ Bubble b = new Bubble(intent, UserHandle.of(mCurrentUserId), mMainExecutor);
+ b.setShouldAutoExpand(true);
+ inflateAndAdd(b, /* suppressFlyout= */ true, /* showInShade= */ false);
+ }
+
+ /**
* Fills the overflow bubbles by loading them from disk.
*/
void loadOverflowBubblesFromDisk() {
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 cfbe1b3caf7a..8121b206c93a 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
@@ -53,13 +53,13 @@ import android.util.IntProperty;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
-import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import android.window.ScreenCapture;
import androidx.annotation.Nullable;
@@ -224,15 +224,23 @@ public class BubbleExpandedView extends LinearLayout {
try {
options.setTaskAlwaysOnTop(true);
options.setLaunchedFromBubble(true);
- if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
+
+ Intent fillInIntent = new Intent();
+ // Apply flags to make behaviour match documentLaunchMode=always.
+ fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+
+ if (mBubble.isAppBubble()) {
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0,
+ mBubble.getAppBubbleIntent(),
+ PendingIntent.FLAG_MUTABLE,
+ null);
+ mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
+ } else if (!mIsOverflow && mBubble.hasMetadataShortcutId()) {
options.setApplyActivityFlagsForBubbles(true);
mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
options, launchBounds);
} else {
- Intent fillInIntent = new Intent();
- // Apply flags to make behaviour match documentLaunchMode=always.
- fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
- fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
if (mBubble != null) {
mBubble.setIntentActive();
}
@@ -508,7 +516,7 @@ public class BubbleExpandedView extends LinearLayout {
/** Return a GraphicBuffer with the contents of the task view surface. */
@Nullable
- SurfaceControl.ScreenshotHardwareBuffer snapshotActivitySurface() {
+ ScreenCapture.ScreenshotHardwareBuffer snapshotActivitySurface() {
if (mIsOverflow) {
// For now, just snapshot the view and return it as a hw buffer so that the animation
// code for both the tasks and overflow can be the same
@@ -517,7 +525,7 @@ public class BubbleExpandedView extends LinearLayout {
p.beginRecording(mOverflowView.getWidth(), mOverflowView.getHeight()));
p.endRecording();
Bitmap snapshot = Bitmap.createBitmap(p);
- return new SurfaceControl.ScreenshotHardwareBuffer(
+ return new ScreenCapture.ScreenshotHardwareBuffer(
snapshot.getHardwareBuffer(),
snapshot.getColorSpace(),
false /* containsSecureLayers */,
@@ -526,7 +534,7 @@ public class BubbleExpandedView extends LinearLayout {
if (mTaskView == null || mTaskView.getSurfaceControl() == null) {
return null;
}
- return SurfaceControl.captureLayers(
+ return ScreenCapture.captureLayers(
mTaskView.getSurfaceControl(),
new Rect(0, 0, mTaskView.getWidth(), mTaskView.getHeight()),
1 /* scale */);
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 aeaf6eda9809..2d9c2a9145ab 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
@@ -51,7 +51,6 @@ import android.util.Log;
import android.view.Choreographer;
import android.view.LayoutInflater;
import android.view.MotionEvent;
-import android.view.SurfaceControl;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
@@ -64,6 +63,7 @@ import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
+import android.window.ScreenCapture;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -244,7 +244,7 @@ public class BubbleStackView extends FrameLayout
* Buffer containing a screenshot of the animating-out bubble. This is drawn into the
* SurfaceView during animations.
*/
- private SurfaceControl.ScreenshotHardwareBuffer mAnimatingOutBubbleBuffer;
+ private ScreenCapture.ScreenshotHardwareBuffer mAnimatingOutBubbleBuffer;
private BubbleFlyoutView mFlyout;
/** Runnable that fades out the flyout and then sets it to GONE. */
@@ -1270,7 +1270,7 @@ public class BubbleStackView extends FrameLayout
}
final boolean seen = getPrefBoolean(ManageEducationViewKt.PREF_MANAGED_EDUCATION);
final boolean shouldShow = (!seen || BubbleDebugConfig.forceShowUserEducation(mContext))
- && mExpandedBubble != null;
+ && mExpandedBubble != null && mExpandedBubble.getExpandedView() != null;
if (BubbleDebugConfig.DEBUG_USER_EDUCATION) {
Log.d(TAG, "Show manage edu: " + shouldShow);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
index 063dac3d4109..ab194dfb3ce9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/DismissView.kt
@@ -24,8 +24,8 @@ import android.util.IntProperty
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
-import android.view.WindowManager
import android.view.WindowInsets
+import android.view.WindowManager
import android.widget.FrameLayout
import androidx.dynamicanimation.animation.DynamicAnimation
import androidx.dynamicanimation.animation.SpringForce.DAMPING_RATIO_LOW_BOUNCY
@@ -41,6 +41,7 @@ class DismissView(context: Context) : FrameLayout(context) {
var circle = DismissCircleView(context)
var isShowing = false
+ var targetSizeResId: Int
private val animator = PhysicsAnimator.getInstance(circle)
private val spring = PhysicsAnimator.SpringConfig(STIFFNESS_LOW, DAMPING_RATIO_LOW_BOUNCY)
@@ -70,7 +71,8 @@ class DismissView(context: Context) : FrameLayout(context) {
setVisibility(View.INVISIBLE)
setBackgroundDrawable(gradientDrawable)
- val targetSize: Int = resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ targetSizeResId = R.dimen.dismiss_circle_size
+ val targetSize: Int = resources.getDimensionPixelSize(targetSizeResId)
addView(circle, LayoutParams(targetSize, targetSize,
Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL))
// start with circle offscreen so it's animated up
@@ -126,7 +128,7 @@ class DismissView(context: Context) : FrameLayout(context) {
layoutParams.height = resources.getDimensionPixelSize(
R.dimen.floating_dismiss_gradient_height)
- val targetSize: Int = resources.getDimensionPixelSize(R.dimen.dismiss_circle_size)
+ val targetSize = resources.getDimensionPixelSize(targetSizeResId)
circle.layoutParams.width = targetSize
circle.layoutParams.height = targetSize
circle.requestLayout()
@@ -153,4 +155,4 @@ class DismissView(context: Context) : FrameLayout(context) {
setPadding(0, 0, 0, navInset.bottom +
resources.getDimensionPixelSize(R.dimen.floating_dismiss_bottom_margin))
}
-} \ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java
index 976fba52b9e2..e0c782d1675b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DismissCircleView.java
@@ -49,6 +49,8 @@ public class DismissCircleView extends FrameLayout {
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
+ final Resources res = getResources();
+ setBackground(res.getDrawable(R.drawable.dismiss_circle_background));
setViewSizes();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
index c4bd73ba1b4a..fad3dee1f927 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ScreenshotUtils.java
@@ -19,6 +19,7 @@ package com.android.wm.shell.common;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.view.SurfaceControl;
+import android.window.ScreenCapture;
import java.util.function.Consumer;
@@ -28,16 +29,16 @@ import java.util.function.Consumer;
public class ScreenshotUtils {
/**
- * Take a screenshot of the specified SurfaceControl.
+ * Takes a screenshot of the specified SurfaceControl.
*
* @param sc the SurfaceControl to take a screenshot of
* @param crop the crop to use when capturing the screenshot
* @param consumer Consumer for the captured buffer
*/
public static void captureLayer(SurfaceControl sc, Rect crop,
- Consumer<SurfaceControl.ScreenshotHardwareBuffer> consumer) {
- consumer.accept(SurfaceControl.captureLayers(
- new SurfaceControl.LayerCaptureArgs.Builder(sc)
+ Consumer<ScreenCapture.ScreenshotHardwareBuffer> consumer) {
+ consumer.accept(ScreenCapture.captureLayers(
+ new ScreenCapture.LayerCaptureArgs.Builder(sc)
.setSourceCrop(crop)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
@@ -45,20 +46,23 @@ public class ScreenshotUtils {
}
private static class BufferConsumer implements
- Consumer<SurfaceControl.ScreenshotHardwareBuffer> {
+ Consumer<ScreenCapture.ScreenshotHardwareBuffer> {
SurfaceControl mScreenshot = null;
SurfaceControl.Transaction mTransaction;
SurfaceControl mSurfaceControl;
+ SurfaceControl mParentSurfaceControl;
int mLayer;
- BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, int layer) {
+ BufferConsumer(SurfaceControl.Transaction t, SurfaceControl sc, SurfaceControl parentSc,
+ int layer) {
mTransaction = t;
mSurfaceControl = sc;
+ mParentSurfaceControl = parentSc;
mLayer = layer;
}
@Override
- public void accept(SurfaceControl.ScreenshotHardwareBuffer buffer) {
+ public void accept(ScreenCapture.ScreenshotHardwareBuffer buffer) {
if (buffer == null || buffer.getHardwareBuffer() == null) {
return;
}
@@ -72,7 +76,7 @@ public class ScreenshotUtils {
mTransaction.setBuffer(mScreenshot, buffer.getHardwareBuffer());
mTransaction.setColorSpace(mScreenshot, buffer.getColorSpace());
- mTransaction.reparent(mScreenshot, mSurfaceControl);
+ mTransaction.reparent(mScreenshot, mParentSurfaceControl);
mTransaction.setLayer(mScreenshot, mLayer);
mTransaction.show(mScreenshot);
mTransaction.apply();
@@ -80,7 +84,7 @@ public class ScreenshotUtils {
}
/**
- * Take a screenshot of the specified SurfaceControl.
+ * Takes a screenshot of the specified SurfaceControl.
*
* @param t the transaction used to set changes on the resulting screenshot.
* @param sc the SurfaceControl to take a screenshot of
@@ -91,7 +95,23 @@ public class ScreenshotUtils {
*/
public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
Rect crop, int layer) {
- BufferConsumer consumer = new BufferConsumer(t, sc, layer);
+ return takeScreenshot(t, sc, sc /* parentSc */, crop, layer);
+ }
+
+ /**
+ * Takes a screenshot of the specified SurfaceControl.
+ *
+ * @param t the transaction used to set changes on the resulting screenshot.
+ * @param sc the SurfaceControl to take a screenshot of
+ * @param parentSc the SurfaceControl to attach the screenshot to.
+ * @param crop the crop to use when capturing the screenshot
+ * @param layer the layer to place the screenshot
+ *
+ * @return A SurfaceControl where the screenshot will be attached, or null if failed.
+ */
+ public static SurfaceControl takeScreenshot(SurfaceControl.Transaction t, SurfaceControl sc,
+ SurfaceControl parentSc, Rect crop, int layer) {
+ BufferConsumer consumer = new BufferConsumer(t, sc, parentSc, layer);
captureLayer(sc, crop, consumer);
return consumer.mScreenshot;
}
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 4c85d2090ec5..419e62daf586 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
@@ -561,6 +561,10 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
if (from == to) {
// No animation run, still callback to stop resizing.
mSplitLayoutHandler.onLayoutSizeChanged(this);
+
+ if (flingFinishedCallback != null) {
+ flingFinishedCallback.run();
+ }
InteractionJankMonitorUtils.endTracing(
CUJ_SPLIT_SCREEN_RESIZE);
return;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index c39602032170..80cdd1f79cb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -24,6 +24,7 @@ import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.SystemProperties;
import android.view.IWindowManager;
+import android.view.WindowManager;
import com.android.internal.logging.UiEventLogger;
import com.android.launcher3.icons.IconProvider;
@@ -55,9 +56,15 @@ import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.common.annotations.ShellSplashscreenThread;
import com.android.wm.shell.compatui.CompatUIController;
+import com.android.wm.shell.desktopmode.DesktopMode;
+import com.android.wm.shell.desktopmode.DesktopModeController;
+import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
import com.android.wm.shell.displayareahelper.DisplayAreaHelperController;
import com.android.wm.shell.draganddrop.DragAndDropController;
+import com.android.wm.shell.floating.FloatingTasks;
+import com.android.wm.shell.floating.FloatingTasksController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.fullscreen.FullscreenTaskListener;
import com.android.wm.shell.hidedisplaycutout.HideDisplayCutoutController;
@@ -477,11 +484,14 @@ public abstract class WMShellBaseModule {
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ ActivityTaskManager activityTaskManager,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor
) {
return Optional.ofNullable(
RecentTasksController.create(context, shellInit, shellCommandHandler,
- taskStackListener, mainExecutor));
+ taskStackListener, activityTaskManager, desktopModeTaskRepository,
+ mainExecutor));
}
//
@@ -569,6 +579,47 @@ public abstract class WMShellBaseModule {
}
//
+ // Floating tasks
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<FloatingTasks> provideFloatingTasks(
+ Optional<FloatingTasksController> floatingTaskController) {
+ return floatingTaskController.map((controller) -> controller.asFloatingTasks());
+ }
+
+ @WMSingleton
+ @Provides
+ static Optional<FloatingTasksController> provideFloatingTasksController(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ ShellCommandHandler shellCommandHandler,
+ Optional<BubbleController> bubbleController,
+ WindowManager windowManager,
+ ShellTaskOrganizer organizer,
+ TaskViewTransitions taskViewTransitions,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellBackgroundThread ShellExecutor bgExecutor,
+ SyncTransactionQueue syncQueue) {
+ if (FloatingTasksController.FLOATING_TASKS_ENABLED) {
+ return Optional.of(new FloatingTasksController(context,
+ shellInit,
+ shellController,
+ shellCommandHandler,
+ bubbleController,
+ windowManager,
+ organizer,
+ taskViewTransitions,
+ mainExecutor,
+ bgExecutor,
+ syncQueue));
+ } else {
+ return Optional.empty();
+ }
+ }
+
+ //
// Starting window
//
@@ -666,6 +717,45 @@ public abstract class WMShellBaseModule {
}
//
+ // Desktop mode (optional feature)
+ //
+
+ @WMSingleton
+ @Provides
+ static Optional<DesktopMode> provideDesktopMode(
+ Optional<DesktopModeController> desktopModeController) {
+ return desktopModeController.map(DesktopModeController::asDesktopMode);
+ }
+
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract DesktopModeController optionalDesktopModeController();
+
+ @WMSingleton
+ @Provides
+ static Optional<DesktopModeController> providesDesktopModeController(
+ @DynamicOverride Optional<DesktopModeController> desktopModeController) {
+ if (DesktopModeStatus.IS_SUPPORTED) {
+ return desktopModeController;
+ }
+ return Optional.empty();
+ }
+
+ @BindsOptionalOf
+ @DynamicOverride
+ abstract DesktopModeTaskRepository optionalDesktopModeTaskRepository();
+
+ @WMSingleton
+ @Provides
+ static Optional<DesktopModeTaskRepository> providesDesktopTaskRepository(
+ @DynamicOverride Optional<DesktopModeTaskRepository> desktopModeTaskRepository) {
+ if (DesktopModeStatus.IS_SUPPORTED) {
+ return desktopModeTaskRepository;
+ }
+ return Optional.empty();
+ }
+
+ //
// Misc
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 18ce3642335d..37a50b611039 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -27,7 +27,6 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.RootDisplayAreaOrganizer;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.TaskViewTransitions;
@@ -49,8 +48,8 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ShellBackgroundThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.desktopmode.DesktopModeController;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
@@ -189,14 +188,16 @@ public abstract class WMShellModule {
@ShellMainThread Choreographer mainChoreographer,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
- SyncTransactionQueue syncQueue) {
+ SyncTransactionQueue syncQueue,
+ @DynamicOverride DesktopModeController desktopModeController) {
return new CaptionWindowDecorViewModel(
context,
mainHandler,
mainChoreographer,
taskOrganizer,
displayController,
- syncQueue);
+ syncQueue,
+ desktopModeController);
}
//
@@ -220,14 +221,14 @@ public abstract class WMShellModule {
Context context,
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<RecentTasksController> recentTasksController,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
WindowDecorViewModel<?> windowDecorViewModel) {
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
ShellInit init = FreeformComponents.isFreeformEnabled(context)
? shellInit
: null;
- return new FreeformTaskListener<>(init, shellTaskOrganizer, recentTasksController,
+ return new FreeformTaskListener<>(init, shellTaskOrganizer, desktopModeTaskRepository,
windowDecorViewModel);
}
@@ -319,6 +320,7 @@ public abstract class WMShellModule {
ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
+ PipAnimationController pipAnimationController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
PhonePipKeepClearAlgorithm pipKeepClearAlgorithm,
@@ -338,11 +340,12 @@ public abstract class WMShellModule {
@ShellMainThread ShellExecutor mainExecutor) {
return Optional.ofNullable(PipController.create(
context, shellInit, shellCommandHandler, shellController,
- displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
- pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
- pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
- windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
- displayInsetsController, oneHandedController, mainExecutor));
+ displayController, pipAnimationController, pipAppOpsListener, pipBoundsAlgorithm,
+ pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper, pipMediaController,
+ phonePipMenuController, pipTaskOrganizer, pipTransitionState, pipTouchHandler,
+ pipTransitionController, windowManagerShellWrapper, taskStackListener,
+ pipParamsChangedForwarder, displayInsetsController, oneHandedController,
+ mainExecutor));
}
@WMSingleton
@@ -595,19 +598,25 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
- static Optional<DesktopModeController> provideDesktopModeController(
- Context context, ShellInit shellInit,
+ @DynamicOverride
+ static DesktopModeController provideDesktopModeController(Context context, ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
- RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- @ShellMainThread Handler mainHandler
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ Transitions transitions,
+ @DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
+ @ShellMainThread Handler mainHandler,
+ @ShellMainThread ShellExecutor mainExecutor
) {
- if (DesktopMode.IS_SUPPORTED) {
- return Optional.of(new DesktopModeController(context, shellInit, shellTaskOrganizer,
- rootDisplayAreaOrganizer,
- mainHandler));
- } else {
- return Optional.empty();
- }
+ return new DesktopModeController(context, shellInit, shellTaskOrganizer,
+ rootTaskDisplayAreaOrganizer, transitions, desktopModeTaskRepository, mainHandler,
+ mainExecutor);
+ }
+
+ @WMSingleton
+ @Provides
+ @DynamicOverride
+ static DesktopModeTaskRepository provideDesktopModeTaskRepository() {
+ return new DesktopModeTaskRepository();
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
index 8993d549964c..ff3be38d09e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
@@ -16,43 +16,16 @@
package com.android.wm.shell.desktopmode;
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
-
-import android.content.Context;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.provider.Settings;
-
-import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.annotations.ExternalThread;
/**
- * Constants for desktop mode feature
+ * Interface to interact with desktop mode feature in shell.
*/
-public class DesktopMode {
-
- /**
- * Flag to indicate whether desktop mode is available on the device
- */
- public static final boolean IS_SUPPORTED = SystemProperties.getBoolean(
- "persist.wm.debug.desktop_mode", false);
+@ExternalThread
+public interface DesktopMode {
- /**
- * Check if desktop mode is active
- *
- * @return {@code true} if active
- */
- public static boolean isActive(Context context) {
- if (!IS_SUPPORTED) {
- return false;
- }
- try {
- int result = Settings.System.getIntForUser(context.getContentResolver(),
- Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
- ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result);
- return result != 0;
- } catch (Exception e) {
- ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
- return false;
- }
+ /** Returns a binder that can be passed to an external process to manipulate DesktopMode. */
+ default IDesktopMode createExternalInterface() {
+ return null;
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
index 7d34ea481de6..99739c457aa6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeController.java
@@ -18,43 +18,67 @@ package com.android.wm.shell.desktopmode;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.TRANSIT_CHANGE;
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.WindowConfiguration;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
+import android.util.ArraySet;
+import android.window.DisplayAreaInfo;
import android.window.WindowContainerTransaction;
+import androidx.annotation.BinderThread;
import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.wm.shell.RootDisplayAreaOrganizer;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.RemoteCallable;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.ArrayList;
+import java.util.Comparator;
/**
* Handles windowing changes when desktop mode system setting changes
*/
-public class DesktopModeController {
+public class DesktopModeController implements RemoteCallable<DesktopModeController> {
private final Context mContext;
private final ShellTaskOrganizer mShellTaskOrganizer;
- private final RootDisplayAreaOrganizer mRootDisplayAreaOrganizer;
+ private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
+ private final Transitions mTransitions;
+ private final DesktopModeTaskRepository mDesktopModeTaskRepository;
+ private final ShellExecutor mMainExecutor;
+ private final DesktopMode mDesktopModeImpl = new DesktopModeImpl();
private final SettingsObserver mSettingsObserver;
public DesktopModeController(Context context, ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
- RootDisplayAreaOrganizer rootDisplayAreaOrganizer,
- @ShellMainThread Handler mainHandler) {
+ RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
+ Transitions transitions,
+ DesktopModeTaskRepository desktopModeTaskRepository,
+ @ShellMainThread Handler mainHandler,
+ @ShellMainThread ShellExecutor mainExecutor) {
mContext = context;
mShellTaskOrganizer = shellTaskOrganizer;
- mRootDisplayAreaOrganizer = rootDisplayAreaOrganizer;
+ mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
+ mTransitions = transitions;
+ mDesktopModeTaskRepository = desktopModeTaskRepository;
+ mMainExecutor = mainExecutor;
mSettingsObserver = new SettingsObserver(mContext, mainHandler);
shellInit.addInitCallback(this::onInit, this);
}
@@ -62,11 +86,28 @@ public class DesktopModeController {
private void onInit() {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "Initialize DesktopModeController");
mSettingsObserver.observe();
- if (DesktopMode.isActive(mContext)) {
+ if (DesktopModeStatus.isActive(mContext)) {
updateDesktopModeActive(true);
}
}
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mMainExecutor;
+ }
+
+ /**
+ * Get connection interface between sysui and shell
+ */
+ public DesktopMode asDesktopMode() {
+ return mDesktopModeImpl;
+ }
+
@VisibleForTesting
void updateDesktopModeActive(boolean active) {
ProtoLog.d(WM_SHELL_DESKTOP_MODE, "updateDesktopModeActive: active=%s", active);
@@ -87,9 +128,71 @@ public class DesktopModeController {
wct.merge(mShellTaskOrganizer.prepareClearBoundsForStandardTasks(displayId),
true /* transfer */);
}
- wct.merge(mRootDisplayAreaOrganizer.prepareWindowingModeChange(displayId,
- targetWindowingMode), true /* transfer */);
- mRootDisplayAreaOrganizer.applyTransaction(wct);
+ prepareWindowingModeChange(wct, displayId, targetWindowingMode);
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mTransitions.startTransition(TRANSIT_CHANGE, wct, null);
+ } else {
+ mRootTaskDisplayAreaOrganizer.applyTransaction(wct);
+ }
+ }
+
+ private void prepareWindowingModeChange(WindowContainerTransaction wct,
+ int displayId, @WindowConfiguration.WindowingMode int windowingMode) {
+ DisplayAreaInfo displayAreaInfo = mRootTaskDisplayAreaOrganizer
+ .getDisplayAreaInfo(displayId);
+ if (displayAreaInfo == null) {
+ ProtoLog.e(WM_SHELL_DESKTOP_MODE,
+ "unable to update windowing mode for display %d display not found", displayId);
+ return;
+ }
+
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE,
+ "setWindowingMode: displayId=%d current wmMode=%d new wmMode=%d", displayId,
+ displayAreaInfo.configuration.windowConfiguration.getWindowingMode(),
+ windowingMode);
+
+ wct.setWindowingMode(displayAreaInfo.token, windowingMode);
+ }
+
+ /**
+ * Show apps on desktop
+ */
+ public void showDesktopApps() {
+ ArraySet<Integer> activeTasks = mDesktopModeTaskRepository.getActiveTasks();
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE, "bringDesktopAppsToFront: tasks=%s", activeTasks.size());
+ ArrayList<RunningTaskInfo> taskInfos = new ArrayList<>();
+ for (Integer taskId : activeTasks) {
+ RunningTaskInfo taskInfo = mShellTaskOrganizer.getRunningTaskInfo(taskId);
+ if (taskInfo != null) {
+ taskInfos.add(taskInfo);
+ }
+ }
+ // Order by lastActiveTime, descending
+ taskInfos.sort(Comparator.comparingLong(task -> -task.lastActiveTime));
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ for (RunningTaskInfo task : taskInfos) {
+ wct.reorder(task.token, true);
+ }
+ mShellTaskOrganizer.applyTransaction(wct);
+ }
+
+ /**
+ * Turn desktop mode on or off
+ * @param active the desired state for desktop mode setting
+ */
+ public void setDesktopModeActive(boolean active) {
+ int value = active ? 1 : 0;
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.DESKTOP_MODE, value);
+ }
+
+ /**
+ * Returns the windowing mode of the display area with the specified displayId.
+ * @param displayId
+ * @return
+ */
+ public int getDisplayAreaWindowingMode(int displayId) {
+ return mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(displayId)
+ .configuration.windowConfiguration.getWindowingMode();
}
/**
@@ -122,8 +225,51 @@ public class DesktopModeController {
}
private void desktopModeSettingChanged() {
- boolean enabled = DesktopMode.isActive(mContext);
+ boolean enabled = DesktopModeStatus.isActive(mContext);
updateDesktopModeActive(enabled);
}
}
+
+ /**
+ * The interface for calls from outside the shell, within the host process.
+ */
+ @ExternalThread
+ private final class DesktopModeImpl implements DesktopMode {
+
+ private IDesktopModeImpl mIDesktopMode;
+
+ @Override
+ public IDesktopMode createExternalInterface() {
+ if (mIDesktopMode != null) {
+ mIDesktopMode.invalidate();
+ }
+ mIDesktopMode = new IDesktopModeImpl(DesktopModeController.this);
+ return mIDesktopMode;
+ }
+ }
+
+ /**
+ * The interface for calls from outside the host process.
+ */
+ @BinderThread
+ private static class IDesktopModeImpl extends IDesktopMode.Stub {
+
+ private DesktopModeController mController;
+
+ IDesktopModeImpl(DesktopModeController controller) {
+ mController = controller;
+ }
+
+ /**
+ * Invalidates this instance, preventing future calls from updating the controller.
+ */
+ void invalidate() {
+ mController = null;
+ }
+
+ public void showDesktopApps() {
+ executeRemoteCallWithTaskPermission(mController, "showDesktopApps",
+ DesktopModeController::showDesktopApps);
+ }
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
new file mode 100644
index 000000000000..195ff502e7dc
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeStatus.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.desktopmode;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE;
+
+import android.content.Context;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+import com.android.internal.protolog.common.ProtoLog;
+
+/**
+ * Constants for desktop mode feature
+ */
+public class DesktopModeStatus {
+
+ /**
+ * Flag to indicate whether desktop mode is available on the device
+ */
+ public static final boolean IS_SUPPORTED = SystemProperties.getBoolean(
+ "persist.wm.debug.desktop_mode", false);
+
+ /**
+ * Check if desktop mode is active
+ *
+ * @return {@code true} if active
+ */
+ public static boolean isActive(Context context) {
+ if (!IS_SUPPORTED) {
+ return false;
+ }
+ try {
+ int result = Settings.System.getIntForUser(context.getContentResolver(),
+ Settings.System.DESKTOP_MODE, UserHandle.USER_CURRENT);
+ ProtoLog.d(WM_SHELL_DESKTOP_MODE, "isDesktopModeEnabled=%s", result);
+ return result != 0;
+ } catch (Exception e) {
+ ProtoLog.e(WM_SHELL_DESKTOP_MODE, "Failed to read DESKTOP_MODE setting %s", e);
+ return false;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
new file mode 100644
index 000000000000..988601c0e8a8
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.desktopmode
+
+import android.util.ArraySet
+
+/**
+ * Keeps track of task data related to desktop mode.
+ */
+class DesktopModeTaskRepository {
+
+ /**
+ * Set of task ids that are marked as active in desktop mode.
+ * Active tasks in desktop mode are freeform tasks that are visible or have been visible after
+ * desktop mode was activated.
+ * Task gets removed from this list when it vanishes. Or when desktop mode is turned off.
+ */
+ private val activeTasks = ArraySet<Int>()
+ private val listeners = ArraySet<Listener>()
+
+ /**
+ * Add a [Listener] to be notified of updates to the repository.
+ */
+ fun addListener(listener: Listener) {
+ listeners.add(listener)
+ }
+
+ /**
+ * Remove a previously registered [Listener]
+ */
+ fun removeListener(listener: Listener) {
+ listeners.remove(listener)
+ }
+
+ /**
+ * Mark a task with given [taskId] as active.
+ */
+ fun addActiveTask(taskId: Int) {
+ val added = activeTasks.add(taskId)
+ if (added) {
+ listeners.onEach { it.onActiveTasksChanged() }
+ }
+ }
+
+ /**
+ * Remove task with given [taskId] from active tasks.
+ */
+ fun removeActiveTask(taskId: Int) {
+ val removed = activeTasks.remove(taskId)
+ if (removed) {
+ listeners.onEach { it.onActiveTasksChanged() }
+ }
+ }
+
+ /**
+ * Check if a task with the given [taskId] was marked as an active task
+ */
+ fun isActiveTask(taskId: Int): Boolean {
+ return activeTasks.contains(taskId)
+ }
+
+ /**
+ * Get a set of the active tasks
+ */
+ fun getActiveTasks(): ArraySet<Int> {
+ return ArraySet(activeTasks)
+ }
+
+ /**
+ * Defines interface for classes that can listen to changes in repository state.
+ */
+ interface Listener {
+ fun onActiveTasksChanged()
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
new file mode 100644
index 000000000000..5042bd6f2d65
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopMode.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.desktopmode;
+
+/**
+ * Interface that is exposed to remote callers to manipulate desktop mode features.
+ */
+interface IDesktopMode {
+
+ /** Show apps on the desktop */
+ void showDesktopApps();
+} \ No newline at end of file
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 435d8eaa563e..62bf5172e106 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
@@ -17,6 +17,8 @@
package com.android.wm.shell.draganddrop;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -263,6 +265,9 @@ public class DragAndDropPolicy {
mStarter.startShortcut(packageName, id, position, opts, user);
} else {
final PendingIntent launchIntent = intent.getParcelableExtra(EXTRA_PENDING_INTENT);
+ // Put BAL flags to avoid activity start aborted.
+ opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
+ opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
mStarter.startIntent(launchIntent, null /* fillIntent */, position, opts);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java
new file mode 100644
index 000000000000..83a1734dc71a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingDismissController.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.floating;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.view.MotionEvent;
+import android.view.View;
+
+import androidx.annotation.NonNull;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+
+import com.android.wm.shell.R;
+import com.android.wm.shell.bubbles.DismissView;
+import com.android.wm.shell.common.magnetictarget.MagnetizedObject;
+import com.android.wm.shell.floating.views.FloatingTaskLayer;
+import com.android.wm.shell.floating.views.FloatingTaskView;
+
+import java.util.Objects;
+
+/**
+ * Controls a floating dismiss circle that has a 'magnetic' field around it, causing views moved
+ * close to the target to be stuck to it unless moved out again.
+ */
+public class FloatingDismissController {
+
+ /** Velocity required to dismiss the view without dragging it into the dismiss target. */
+ private static final float FLING_TO_DISMISS_MIN_VELOCITY = 4000f;
+ /**
+ * Max velocity that the view can be moving through the target with to stick (i.e. if it's
+ * more than this velocity, it will pass through the target.
+ */
+ private static final float STICK_TO_TARGET_MAX_X_VELOCITY = 2000f;
+ /**
+ * Percentage of the target width to use to determine if an object flung towards the target
+ * should dismiss (e.g. if target is 100px and this is set ot 2f, anything flung within a
+ * 200px-wide area around the target will be considered 'near' enough get dismissed).
+ */
+ private static final float FLING_TO_TARGET_WIDTH_PERCENT = 2f;
+ /** Minimum alpha to apply to the view being dismissed when it is in the target. */
+ private static final float DISMISS_VIEW_MIN_ALPHA = 0.6f;
+ /** Amount to scale down the view being dismissed when it is in the target. */
+ private static final float DISMISS_VIEW_SCALE_DOWN_PERCENT = 0.15f;
+
+ private Context mContext;
+ private FloatingTasksController mController;
+ private FloatingTaskLayer mParent;
+
+ private DismissView mDismissView;
+ private ValueAnimator mDismissAnimator;
+ private View mViewBeingDismissed;
+ private float mDismissSizePercent;
+ private float mDismissSize;
+
+ /**
+ * The currently magnetized object, which is being dragged and will be attracted to the magnetic
+ * dismiss target.
+ */
+ private MagnetizedObject<View> mMagnetizedObject;
+ /**
+ * The MagneticTarget instance for our circular dismiss view. This is added to the
+ * MagnetizedObject instances for the view being dragged.
+ */
+ private MagnetizedObject.MagneticTarget mMagneticTarget;
+ /** Magnet listener that handles animating and dismissing the view. */
+ private MagnetizedObject.MagnetListener mFloatingViewMagnetListener;
+
+ public FloatingDismissController(Context context, FloatingTasksController controller,
+ FloatingTaskLayer parent) {
+ mContext = context;
+ mController = controller;
+ mParent = parent;
+ updateSizes();
+ createAndAddDismissView();
+
+ mDismissAnimator = ValueAnimator.ofFloat(1f, 0f);
+ mDismissAnimator.addUpdateListener(animation -> {
+ final float value = (float) animation.getAnimatedValue();
+ if (mDismissView != null) {
+ mDismissView.setPivotX((mDismissView.getRight() - mDismissView.getLeft()) / 2f);
+ mDismissView.setPivotY((mDismissView.getBottom() - mDismissView.getTop()) / 2f);
+ final float scaleValue = Math.max(value, mDismissSizePercent);
+ mDismissView.getCircle().setScaleX(scaleValue);
+ mDismissView.getCircle().setScaleY(scaleValue);
+ }
+ if (mViewBeingDismissed != null) {
+ // TODO: alpha doesn't actually apply to taskView currently.
+ mViewBeingDismissed.setAlpha(Math.max(value, DISMISS_VIEW_MIN_ALPHA));
+ mViewBeingDismissed.setScaleX(Math.max(value, DISMISS_VIEW_SCALE_DOWN_PERCENT));
+ mViewBeingDismissed.setScaleY(Math.max(value, DISMISS_VIEW_SCALE_DOWN_PERCENT));
+ }
+ });
+
+ mFloatingViewMagnetListener = new MagnetizedObject.MagnetListener() {
+ @Override
+ public void onStuckToTarget(
+ @NonNull MagnetizedObject.MagneticTarget target) {
+ animateDismissing(/* dismissing= */ true);
+ }
+
+ @Override
+ public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target,
+ float velX, float velY, boolean wasFlungOut) {
+ animateDismissing(/* dismissing= */ false);
+ mParent.onUnstuckFromTarget((FloatingTaskView) mViewBeingDismissed, velX, velY,
+ wasFlungOut);
+ }
+
+ @Override
+ public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) {
+ doDismiss();
+ }
+ };
+ }
+
+ /** Updates all the sizes used and applies them to the {@link DismissView}. */
+ public void updateSizes() {
+ Resources res = mContext.getResources();
+ mDismissSize = res.getDimensionPixelSize(
+ R.dimen.floating_task_dismiss_circle_size);
+ final float minDismissSize = res.getDimensionPixelSize(
+ R.dimen.floating_dismiss_circle_small);
+ mDismissSizePercent = minDismissSize / mDismissSize;
+
+ if (mDismissView != null) {
+ mDismissView.updateResources();
+ }
+ }
+
+ /** Prepares the view being dragged to be magnetic. */
+ public void setUpMagneticObject(View viewBeingDragged) {
+ mViewBeingDismissed = viewBeingDragged;
+ mMagnetizedObject = getMagnetizedView(viewBeingDragged);
+ mMagnetizedObject.clearAllTargets();
+ mMagnetizedObject.addTarget(mMagneticTarget);
+ mMagnetizedObject.setMagnetListener(mFloatingViewMagnetListener);
+ }
+
+ /** Shows or hides the dismiss target. */
+ public void showDismiss(boolean show) {
+ if (show) {
+ mDismissView.show();
+ } else {
+ mDismissView.hide();
+ }
+ }
+
+ /** Passes the MotionEvent to the magnetized object and returns true if it was consumed. */
+ public boolean passEventToMagnetizedObject(MotionEvent event) {
+ return mMagnetizedObject != null && mMagnetizedObject.maybeConsumeMotionEvent(event);
+ }
+
+ private void createAndAddDismissView() {
+ if (mDismissView != null) {
+ mParent.removeView(mDismissView);
+ }
+ mDismissView = new DismissView(mContext);
+ mDismissView.setTargetSizeResId(R.dimen.floating_task_dismiss_circle_size);
+ mDismissView.updateResources();
+ mParent.addView(mDismissView);
+
+ final float dismissRadius = mDismissSize;
+ // Save the MagneticTarget instance for the newly set up view - we'll add this to the
+ // MagnetizedObjects when the dismiss view gets shown.
+ mMagneticTarget = new MagnetizedObject.MagneticTarget(
+ mDismissView.getCircle(), (int) dismissRadius);
+ }
+
+ private MagnetizedObject<View> getMagnetizedView(View v) {
+ if (mMagnetizedObject != null
+ && Objects.equals(mMagnetizedObject.getUnderlyingObject(), v)) {
+ // Same view being dragged, we can reuse the magnetic object.
+ return mMagnetizedObject;
+ }
+ MagnetizedObject<View> magnetizedView = new MagnetizedObject<View>(
+ mContext,
+ v,
+ DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y
+ ) {
+ @Override
+ public float getWidth(@NonNull View underlyingObject) {
+ return underlyingObject.getWidth();
+ }
+
+ @Override
+ public float getHeight(@NonNull View underlyingObject) {
+ return underlyingObject.getHeight();
+ }
+
+ @Override
+ public void getLocationOnScreen(@NonNull View underlyingObject,
+ @NonNull int[] loc) {
+ loc[0] = (int) underlyingObject.getTranslationX();
+ loc[1] = (int) underlyingObject.getTranslationY();
+ }
+ };
+ magnetizedView.setHapticsEnabled(true);
+ magnetizedView.setFlingToTargetMinVelocity(FLING_TO_DISMISS_MIN_VELOCITY);
+ magnetizedView.setStickToTargetMaxXVelocity(STICK_TO_TARGET_MAX_X_VELOCITY);
+ magnetizedView.setFlingToTargetWidthPercent(FLING_TO_TARGET_WIDTH_PERCENT);
+ return magnetizedView;
+ }
+
+ /** Animates the dismiss treatment on the view being dismissed. */
+ private void animateDismissing(boolean shouldDismiss) {
+ if (mViewBeingDismissed == null) {
+ return;
+ }
+ if (shouldDismiss) {
+ mDismissAnimator.removeAllListeners();
+ mDismissAnimator.start();
+ } else {
+ mDismissAnimator.removeAllListeners();
+ mDismissAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ resetDismissAnimator();
+ }
+ });
+ mDismissAnimator.reverse();
+ }
+ }
+
+ /** Actually dismisses the view. */
+ private void doDismiss() {
+ mDismissView.hide();
+ mController.removeTask();
+ resetDismissAnimator();
+ mViewBeingDismissed = null;
+ }
+
+ private void resetDismissAnimator() {
+ mDismissAnimator.removeAllListeners();
+ mDismissAnimator.cancel();
+ if (mDismissView != null) {
+ mDismissView.cancelAnimators();
+ mDismissView.getCircle().setScaleX(1f);
+ mDismissView.getCircle().setScaleY(1f);
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java
new file mode 100644
index 000000000000..935666026bf4
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasks.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.floating;
+
+import android.content.Intent;
+
+import com.android.wm.shell.common.annotations.ExternalThread;
+
+/**
+ * Interface to interact with floating tasks.
+ */
+@ExternalThread
+public interface FloatingTasks {
+
+ /**
+ * Shows, stashes, or un-stashes the floating task depending on state:
+ * - If there is no floating task for this intent, it shows the task for the provided intent.
+ * - If there is a floating task for this intent, but it's stashed, this un-stashes it.
+ * - If there is a floating task for this intent, and it's not stashed, this stashes it.
+ */
+ void showOrSetStashed(Intent intent);
+
+ /** Returns a binder that can be passed to an external process to manipulate FloatingTasks. */
+ default IFloatingTasks createExternalInterface() {
+ return null;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java
new file mode 100644
index 000000000000..67552991869b
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/FloatingTasksController.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.floating;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.os.SystemProperties;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+
+import androidx.annotation.BinderThread;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskViewTransitions;
+import com.android.wm.shell.bubbles.BubbleController;
+import com.android.wm.shell.common.RemoteCallable;
+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.ShellBackgroundThread;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.floating.views.FloatingTaskLayer;
+import com.android.wm.shell.floating.views.FloatingTaskView;
+import com.android.wm.shell.sysui.ConfigurationChangeListener;
+import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+
+import java.io.PrintWriter;
+import java.util.Objects;
+import java.util.Optional;
+
+/**
+ * Entry point for creating and managing floating tasks.
+ *
+ * A single window layer is added and the task(s) are displayed using a {@link FloatingTaskView}
+ * within that window.
+ *
+ * Currently optimized for a single task. Multiple tasks are not supported.
+ */
+public class FloatingTasksController implements RemoteCallable<FloatingTasksController>,
+ ConfigurationChangeListener {
+
+ private static final String TAG = FloatingTasksController.class.getSimpleName();
+
+ public static final boolean FLOATING_TASKS_ENABLED =
+ SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
+ public static final boolean SHOW_FLOATING_TASKS_AS_BUBBLES =
+ SystemProperties.getBoolean("persist.wm.debug.floating_tasks_as_bubbles", false);
+
+ @VisibleForTesting
+ static final int SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET = 600;
+
+ // Only used for testing
+ private Configuration mConfig;
+ private boolean mFloatingTasksEnabledForTests;
+
+ private FloatingTaskImpl mImpl = new FloatingTaskImpl();
+ private Context mContext;
+ private ShellController mShellController;
+ private ShellCommandHandler mShellCommandHandler;
+ private @Nullable BubbleController mBubbleController;
+ private WindowManager mWindowManager;
+ private ShellTaskOrganizer mTaskOrganizer;
+ private TaskViewTransitions mTaskViewTransitions;
+ private @ShellMainThread ShellExecutor mMainExecutor;
+ // TODO: mBackgroundThread is not used but we'll probs need it eventually?
+ private @ShellBackgroundThread ShellExecutor mBackgroundThread;
+ private SyncTransactionQueue mSyncQueue;
+
+ private boolean mIsFloatingLayerAdded;
+ private FloatingTaskLayer mFloatingTaskLayer;
+ private final Point mLastPosition = new Point(-1, -1);
+
+ private Task mTask;
+
+ // Simple class to hold onto info for intent or shortcut based tasks.
+ public static class Task {
+ public int taskId = INVALID_TASK_ID;
+ @Nullable
+ public Intent intent;
+ @Nullable
+ public ShortcutInfo info;
+ @Nullable
+ public FloatingTaskView floatingView;
+ }
+
+ public FloatingTasksController(Context context,
+ ShellInit shellInit,
+ ShellController shellController,
+ ShellCommandHandler shellCommandHandler,
+ Optional<BubbleController> bubbleController,
+ WindowManager windowManager,
+ ShellTaskOrganizer organizer,
+ TaskViewTransitions transitions,
+ @ShellMainThread ShellExecutor mainExecutor,
+ @ShellBackgroundThread ShellExecutor bgExceutor,
+ SyncTransactionQueue syncTransactionQueue) {
+ mContext = context;
+ mShellController = shellController;
+ mShellCommandHandler = shellCommandHandler;
+ mBubbleController = bubbleController.get();
+ mWindowManager = windowManager;
+ mTaskOrganizer = organizer;
+ mTaskViewTransitions = transitions;
+ mMainExecutor = mainExecutor;
+ mBackgroundThread = bgExceutor;
+ mSyncQueue = syncTransactionQueue;
+ if (isFloatingTasksEnabled()) {
+ shellInit.addInitCallback(this::onInit, this);
+ }
+ mShellCommandHandler.addDumpCallback(this::dump, this);
+ }
+
+ protected void onInit() {
+ mShellController.addConfigurationChangeListener(this);
+ }
+
+ /** Only used for testing. */
+ @VisibleForTesting
+ void setConfig(Configuration config) {
+ mConfig = config;
+ }
+
+ /** Only used for testing. */
+ @VisibleForTesting
+ void setFloatingTasksEnabled(boolean enabled) {
+ mFloatingTasksEnabledForTests = enabled;
+ }
+
+ /** Whether the floating layer is available. */
+ boolean isFloatingLayerAvailable() {
+ Configuration config = mConfig == null
+ ? mContext.getResources().getConfiguration()
+ : mConfig;
+ return config.smallestScreenWidthDp >= SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
+ }
+
+ /** Whether floating tasks are enabled. */
+ boolean isFloatingTasksEnabled() {
+ return FLOATING_TASKS_ENABLED || mFloatingTasksEnabledForTests;
+ }
+
+ @Override
+ public void onThemeChanged() {
+ if (mIsFloatingLayerAdded) {
+ mFloatingTaskLayer.updateSizes();
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ // TODO: probably other stuff here to do (e.g. handle rotation)
+ if (mIsFloatingLayerAdded) {
+ mFloatingTaskLayer.updateSizes();
+ }
+ }
+
+ /** Returns false if the task shouldn't be shown. */
+ private boolean canShowTask(Intent intent) {
+ ProtoLog.d(WM_SHELL_FLOATING_APPS, "canShowTask -- %s", intent);
+ if (!isFloatingTasksEnabled() || !isFloatingLayerAvailable()) return false;
+ if (intent == null) {
+ ProtoLog.e(WM_SHELL_FLOATING_APPS, "canShowTask given null intent, doing nothing");
+ return false;
+ }
+ return true;
+ }
+
+ /** Returns true if the task was or should be shown as a bubble. */
+ private boolean maybeShowTaskAsBubble(Intent intent) {
+ if (SHOW_FLOATING_TASKS_AS_BUBBLES && mBubbleController != null) {
+ removeFloatingLayer();
+ if (intent.getPackage() != null) {
+ mBubbleController.addAppBubble(intent);
+ ProtoLog.d(WM_SHELL_FLOATING_APPS, "showing floating task as bubble: %s", intent);
+ } else {
+ ProtoLog.d(WM_SHELL_FLOATING_APPS,
+ "failed to show floating task as bubble: %s; unknown package", intent);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Shows, stashes, or un-stashes the floating task depending on state:
+ * - If there is no floating task for this intent, it shows this the provided task.
+ * - If there is a floating task for this intent, but it's stashed, this un-stashes it.
+ * - If there is a floating task for this intent, and it's not stashed, this stashes it.
+ */
+ public void showOrSetStashed(Intent intent) {
+ if (!canShowTask(intent)) return;
+ if (maybeShowTaskAsBubble(intent)) return;
+
+ addFloatingLayer();
+
+ if (isTaskAttached(mTask) && intent.filterEquals(mTask.intent)) {
+ // The task is already added, toggle the stash state.
+ mFloatingTaskLayer.setStashed(mTask, !mTask.floatingView.isStashed());
+ return;
+ }
+
+ // If we're here it's either a new or different task
+ showNewTask(intent);
+ }
+
+ /**
+ * Shows a floating task with the provided intent.
+ * If the same task is present it will un-stash it or do nothing if it is already un-stashed.
+ * Removes any other floating tasks that might exist.
+ */
+ public void showTask(Intent intent) {
+ if (!canShowTask(intent)) return;
+ if (maybeShowTaskAsBubble(intent)) return;
+
+ addFloatingLayer();
+
+ if (isTaskAttached(mTask) && intent.filterEquals(mTask.intent)) {
+ // The task is already added, show it if it's stashed.
+ if (mTask.floatingView.isStashed()) {
+ mFloatingTaskLayer.setStashed(mTask, false);
+ }
+ return;
+ }
+ showNewTask(intent);
+ }
+
+ private void showNewTask(Intent intent) {
+ if (mTask != null && !intent.filterEquals(mTask.intent)) {
+ mFloatingTaskLayer.removeAllTaskViews();
+ mTask.floatingView.cleanUpTaskView();
+ mTask = null;
+ }
+
+ FloatingTaskView ftv = new FloatingTaskView(mContext, this);
+ ftv.createTaskView(mContext, mTaskOrganizer, mTaskViewTransitions, mSyncQueue);
+
+ mTask = new Task();
+ mTask.floatingView = ftv;
+ mTask.intent = intent;
+
+ // Add & start the task.
+ mFloatingTaskLayer.addTask(mTask);
+ ProtoLog.d(WM_SHELL_FLOATING_APPS, "showNewTask, startingIntent: %s", intent);
+ mTask.floatingView.startTask(mMainExecutor, mTask);
+ }
+
+ /**
+ * Removes the task and cleans up the view.
+ */
+ public void removeTask() {
+ if (mTask != null) {
+ ProtoLog.d(WM_SHELL_FLOATING_APPS, "Removing task with id=%d", mTask.taskId);
+
+ if (mTask.floatingView != null) {
+ // TODO: animate it
+ mFloatingTaskLayer.removeView(mTask.floatingView);
+ mTask.floatingView.cleanUpTaskView();
+ }
+ removeFloatingLayer();
+ }
+ }
+
+ /**
+ * Whether there is a floating task and if it is stashed.
+ */
+ public boolean isStashed() {
+ return isTaskAttached(mTask) && mTask.floatingView.isStashed();
+ }
+
+ /**
+ * If a floating task exists, this sets whether it is stashed and animates if needed.
+ */
+ public void setStashed(boolean shouldStash) {
+ if (mTask != null && mTask.floatingView != null && mIsFloatingLayerAdded) {
+ mFloatingTaskLayer.setStashed(mTask, shouldStash);
+ }
+ }
+
+ /**
+ * Saves the last position the floating task was in so that it can be put there again.
+ */
+ public void setLastPosition(int x, int y) {
+ mLastPosition.set(x, y);
+ }
+
+ /**
+ * Returns the last position the floating task was in.
+ */
+ public Point getLastPosition() {
+ return mLastPosition;
+ }
+
+ /**
+ * Whether the provided task has a view that's attached to the floating layer.
+ */
+ private boolean isTaskAttached(Task t) {
+ return t != null && t.floatingView != null
+ && mIsFloatingLayerAdded
+ && mFloatingTaskLayer.getTaskViewCount() > 0
+ && Objects.equals(mFloatingTaskLayer.getFirstTaskView(), t.floatingView);
+ }
+
+ // TODO: when this is added, if there are bubbles, they get hidden? Is only one layer of this
+ // type allowed? Bubbles & floating tasks should probably be in the same layer to reduce
+ // # of windows.
+ private void addFloatingLayer() {
+ if (mIsFloatingLayerAdded) {
+ return;
+ }
+
+ mFloatingTaskLayer = new FloatingTaskLayer(mContext, this, mWindowManager);
+
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
+ PixelFormat.TRANSLUCENT
+ );
+ params.setTrustedOverlay();
+ params.setFitInsetsTypes(0);
+ params.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+ params.setTitle("FloatingTaskLayer");
+ params.packageName = mContext.getPackageName();
+ params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+ params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+
+ try {
+ mIsFloatingLayerAdded = true;
+ mWindowManager.addView(mFloatingTaskLayer, params);
+ } catch (IllegalStateException e) {
+ // This means the floating layer has already been added which shouldn't happen.
+ e.printStackTrace();
+ }
+ }
+
+ private void removeFloatingLayer() {
+ if (!mIsFloatingLayerAdded) {
+ return;
+ }
+ try {
+ mIsFloatingLayerAdded = false;
+ if (mFloatingTaskLayer != null) {
+ mWindowManager.removeView(mFloatingTaskLayer);
+ }
+ } catch (IllegalArgumentException e) {
+ // This means the floating layer has already been removed which shouldn't happen.
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Description of current floating task state.
+ */
+ private void dump(PrintWriter pw, String prefix) {
+ pw.println("FloatingTaskController state:");
+ pw.print(" isFloatingLayerAvailable= "); pw.println(isFloatingLayerAvailable());
+ pw.print(" isFloatingTasksEnabled= "); pw.println(isFloatingTasksEnabled());
+ pw.print(" mIsFloatingLayerAdded= "); pw.println(mIsFloatingLayerAdded);
+ pw.print(" mLastPosition= "); pw.println(mLastPosition);
+ pw.println();
+ }
+
+ /** Returns the {@link FloatingTasks} implementation. */
+ public FloatingTasks asFloatingTasks() {
+ return mImpl;
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mMainExecutor;
+ }
+
+ /**
+ * The interface for calls from outside the shell, within the host process.
+ */
+ @ExternalThread
+ private class FloatingTaskImpl implements FloatingTasks {
+ private IFloatingTasksImpl mIFloatingTasks;
+
+ @Override
+ public void showOrSetStashed(Intent intent) {
+ mMainExecutor.execute(() -> FloatingTasksController.this.showOrSetStashed(intent));
+ }
+
+ @Override
+ public IFloatingTasks createExternalInterface() {
+ if (mIFloatingTasks != null) {
+ mIFloatingTasks.invalidate();
+ }
+ mIFloatingTasks = new IFloatingTasksImpl(FloatingTasksController.this);
+ return mIFloatingTasks;
+ }
+ }
+
+ /**
+ * The interface for calls from outside the host process.
+ */
+ @BinderThread
+ private static class IFloatingTasksImpl extends IFloatingTasks.Stub {
+ private FloatingTasksController mController;
+
+ IFloatingTasksImpl(FloatingTasksController controller) {
+ mController = controller;
+ }
+
+ /**
+ * Invalidates this instance, preventing future calls from updating the controller.
+ */
+ void invalidate() {
+ mController = null;
+ }
+
+ public void showTask(Intent intent) {
+ executeRemoteCallWithTaskPermission(mController, "showTask",
+ (controller) -> controller.showTask(intent));
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/NonResizeableActivity.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl
index 24275e002c7f..f79ca1039865 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/NonResizeableActivity.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/IFloatingTasks.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open 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,16 +14,15 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.testapp;
+package com.android.wm.shell.floating;
-import android.app.Activity;
-import android.os.Bundle;
+import android.content.Intent;
-public class NonResizeableActivity extends Activity {
+/**
+ * Interface that is exposed to remote callers to manipulate floating task features.
+ */
+interface IFloatingTasks {
+
+ void showTask(in Intent intent) = 1;
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.activity_non_resizeable);
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java
new file mode 100644
index 000000000000..c922109751ba
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingMenuView.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.floating.views;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import com.android.wm.shell.R;
+
+/**
+ * Displays the menu items for a floating task view (e.g. close).
+ */
+public class FloatingMenuView extends LinearLayout {
+
+ private int mItemSize;
+ private int mItemMargin;
+
+ public FloatingMenuView(Context context) {
+ super(context);
+ setOrientation(LinearLayout.HORIZONTAL);
+ setGravity(Gravity.CENTER);
+
+ mItemSize = context.getResources().getDimensionPixelSize(
+ R.dimen.floating_task_menu_item_size);
+ mItemMargin = context.getResources().getDimensionPixelSize(
+ R.dimen.floating_task_menu_item_padding);
+ }
+
+ /** Adds a clickable item to the menu bar. Items are ordered as added. */
+ public void addMenuItem(@Nullable Drawable drawable, View.OnClickListener listener) {
+ ImageView itemView = new ImageView(getContext());
+ itemView.setScaleType(ImageView.ScaleType.CENTER);
+ if (drawable != null) {
+ itemView.setImageDrawable(drawable);
+ }
+ LinearLayout.LayoutParams lp = new LayoutParams(mItemSize,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ lp.setMarginStart(mItemMargin);
+ lp.setMarginEnd(mItemMargin);
+ addView(itemView, lp);
+
+ itemView.setOnClickListener(listener);
+ }
+
+ /**
+ * The menu extends past the top of the TaskView because of the rounded corners. This means
+ * to center content in the menu we must subtract the radius (i.e. the amount of space covered
+ * by TaskView).
+ */
+ public void setCornerRadius(float radius) {
+ setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), (int) radius);
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java
new file mode 100644
index 000000000000..16dab2415bf2
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskLayer.java
@@ -0,0 +1,687 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.floating.views;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewPropertyAnimator;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.dynamicanimation.animation.DynamicAnimation;
+import androidx.dynamicanimation.animation.FlingAnimation;
+
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.R;
+import com.android.wm.shell.floating.FloatingDismissController;
+import com.android.wm.shell.floating.FloatingTasksController;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This is the layout that {@link FloatingTaskView}s are contained in. It handles input and
+ * movement of the task views.
+ */
+public class FloatingTaskLayer extends FrameLayout
+ implements ViewTreeObserver.OnComputeInternalInsetsListener {
+
+ private static final String TAG = FloatingTaskLayer.class.getSimpleName();
+
+ /** How big to make the task view based on screen width of the largest size. */
+ private static final float START_SIZE_WIDTH_PERCENT = 0.33f;
+ /** Min fling velocity required to move the view from one side of the screen to the other. */
+ private static final float ESCAPE_VELOCITY = 750f;
+ /** Amount of friction to apply to fling animations. */
+ private static final float FLING_FRICTION = 1.9f;
+
+ private final FloatingTasksController mController;
+ private final FloatingDismissController mDismissController;
+ private final WindowManager mWindowManager;
+ private final TouchHandlerImpl mTouchHandler;
+
+ private final Region mTouchableRegion = new Region();
+ private final Rect mPositionRect = new Rect();
+ private final Point mDefaultStartPosition = new Point();
+ private final Point mTaskViewSize = new Point();
+ private WindowInsets mWindowInsets;
+ private int mVerticalPadding;
+ private int mOverhangWhenStashed;
+
+ private final List<Rect> mSystemGestureExclusionRects = Collections.singletonList(new Rect());
+ private ViewTreeObserver.OnDrawListener mSystemGestureExclusionListener =
+ this::updateSystemGestureExclusion;
+
+ /** Interface allowing something to handle the touch events going to a task. */
+ interface FloatingTaskTouchHandler {
+ void onDown(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
+ float viewInitialX, float viewInitialY);
+
+ void onMove(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
+ float dx, float dy);
+
+ void onUp(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
+ float dx, float dy, float velX, float velY);
+
+ void onClick(@NonNull FloatingTaskView v);
+ }
+
+ public FloatingTaskLayer(Context context,
+ FloatingTasksController controller,
+ WindowManager windowManager) {
+ super(context);
+ // TODO: Why is this necessary? Without it FloatingTaskView does not render correctly.
+ setBackgroundColor(Color.argb(0, 0, 0, 0));
+
+ mController = controller;
+ mWindowManager = windowManager;
+ updateSizes();
+
+ // TODO: Might make sense to put dismiss controller in the touch handler since that's the
+ // main user of dismiss controller.
+ mDismissController = new FloatingDismissController(context, mController, this);
+ mTouchHandler = new TouchHandlerImpl();
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ getViewTreeObserver().addOnComputeInternalInsetsListener(this);
+ getViewTreeObserver().addOnDrawListener(mSystemGestureExclusionListener);
+ setOnApplyWindowInsetsListener((view, windowInsets) -> {
+ if (!windowInsets.equals(mWindowInsets)) {
+ mWindowInsets = windowInsets;
+ updateSizes();
+ }
+ return windowInsets;
+ });
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
+ getViewTreeObserver().removeOnDrawListener(mSystemGestureExclusionListener);
+ }
+
+ @Override
+ public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) {
+ inoutInfo.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
+ mTouchableRegion.setEmpty();
+ getTouchableRegion(mTouchableRegion);
+ inoutInfo.touchableRegion.set(mTouchableRegion);
+ }
+
+ /** Adds a floating task to the layout. */
+ public void addTask(FloatingTasksController.Task task) {
+ if (task.floatingView == null) return;
+
+ task.floatingView.setTouchHandler(mTouchHandler);
+ addView(task.floatingView, new LayoutParams(mTaskViewSize.x, mTaskViewSize.y));
+ updateTaskViewPosition(task.floatingView);
+ }
+
+ /** Animates the stashed state of the provided task, if it's part of the floating layer. */
+ public void setStashed(FloatingTasksController.Task task, boolean shouldStash) {
+ if (task.floatingView != null && task.floatingView.getParent() == this) {
+ mTouchHandler.stashTaskView(task.floatingView, shouldStash);
+ }
+ }
+
+ /** Removes all {@link FloatingTaskView} from the layout. */
+ public void removeAllTaskViews() {
+ int childCount = getChildCount();
+ ArrayList<View> viewsToRemove = new ArrayList<>();
+ for (int i = 0; i < childCount; i++) {
+ if (getChildAt(i) instanceof FloatingTaskView) {
+ viewsToRemove.add(getChildAt(i));
+ }
+ }
+ for (View v : viewsToRemove) {
+ removeView(v);
+ }
+ }
+
+ /** Returns the number of task views in the layout. */
+ public int getTaskViewCount() {
+ int taskViewCount = 0;
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (getChildAt(i) instanceof FloatingTaskView) {
+ taskViewCount++;
+ }
+ }
+ return taskViewCount;
+ }
+
+ /**
+ * Called when the task view is un-stuck from the dismiss target.
+ * @param v the task view being moved.
+ * @param velX the x velocity of the motion event.
+ * @param velY the y velocity of the motion event.
+ * @param wasFlungOut true if the user flung the task view out of the dismiss target (i.e. there
+ * was an 'up' event), otherwise the user is still dragging.
+ */
+ public void onUnstuckFromTarget(FloatingTaskView v, float velX, float velY,
+ boolean wasFlungOut) {
+ mTouchHandler.onUnstuckFromTarget(v, velX, velY, wasFlungOut);
+ }
+
+ /**
+ * Updates dimensions and applies them to any task views.
+ */
+ public void updateSizes() {
+ if (mDismissController != null) {
+ mDismissController.updateSizes();
+ }
+
+ mOverhangWhenStashed = getResources().getDimensionPixelSize(
+ R.dimen.floating_task_stash_offset);
+ mVerticalPadding = getResources().getDimensionPixelSize(
+ R.dimen.floating_task_vertical_padding);
+
+ WindowMetrics windowMetrics = mWindowManager.getCurrentWindowMetrics();
+ WindowInsets windowInsets = windowMetrics.getWindowInsets();
+ Insets insets = windowInsets.getInsetsIgnoringVisibility(WindowInsets.Type.navigationBars()
+ | WindowInsets.Type.statusBars()
+ | WindowInsets.Type.displayCutout());
+ Rect bounds = windowMetrics.getBounds();
+ mPositionRect.set(bounds.left + insets.left,
+ bounds.top + insets.top + mVerticalPadding,
+ bounds.right - insets.right,
+ bounds.bottom - insets.bottom - mVerticalPadding);
+
+ int taskViewWidth = Math.max(bounds.height(), bounds.width());
+ int taskViewHeight = Math.min(bounds.height(), bounds.width());
+ taskViewHeight = taskViewHeight - (insets.top + insets.bottom + (mVerticalPadding * 2));
+ mTaskViewSize.set((int) (taskViewWidth * START_SIZE_WIDTH_PERCENT), taskViewHeight);
+ mDefaultStartPosition.set(mPositionRect.left, mPositionRect.top);
+
+ // Update existing views
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ if (getChildAt(i) instanceof FloatingTaskView) {
+ FloatingTaskView child = (FloatingTaskView) getChildAt(i);
+ LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ lp.width = mTaskViewSize.x;
+ lp.height = mTaskViewSize.y;
+ child.setLayoutParams(lp);
+ updateTaskViewPosition(child);
+ }
+ }
+ }
+
+ /** Returns the first floating task view in the layout. (Currently only ever 1 view). */
+ @Nullable
+ public FloatingTaskView getFirstTaskView() {
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child instanceof FloatingTaskView) {
+ return (FloatingTaskView) child;
+ }
+ }
+ return null;
+ }
+
+ private void updateTaskViewPosition(FloatingTaskView floatingView) {
+ Point lastPosition = mController.getLastPosition();
+ if (lastPosition.x == -1 && lastPosition.y == -1) {
+ floatingView.setX(mDefaultStartPosition.x);
+ floatingView.setY(mDefaultStartPosition.y);
+ } else {
+ floatingView.setX(lastPosition.x);
+ floatingView.setY(lastPosition.y);
+ }
+ if (mTouchHandler.isStashedPosition(floatingView)) {
+ floatingView.setStashed(true);
+ }
+ floatingView.updateLocation();
+ }
+
+ /**
+ * Updates the area of the screen that shouldn't allow the back gesture due to the placement
+ * of task view (i.e. when task view is stashed on an edge, tapping or swiping that edge would
+ * un-stash the task view instead of performing the back gesture).
+ */
+ private void updateSystemGestureExclusion() {
+ Rect excludeZone = mSystemGestureExclusionRects.get(0);
+ FloatingTaskView floatingTaskView = getFirstTaskView();
+ if (floatingTaskView != null && floatingTaskView.isStashed()) {
+ excludeZone.set(floatingTaskView.getLeft(),
+ floatingTaskView.getTop(),
+ floatingTaskView.getRight(),
+ floatingTaskView.getBottom());
+ excludeZone.offset((int) (floatingTaskView.getTranslationX()),
+ (int) (floatingTaskView.getTranslationY()));
+ setSystemGestureExclusionRects(mSystemGestureExclusionRects);
+ } else {
+ excludeZone.setEmpty();
+ setSystemGestureExclusionRects(Collections.emptyList());
+ }
+ }
+
+ /**
+ * Fills in the touchable region for floating windows. This is used by WindowManager to
+ * decide which touch events go to the floating windows.
+ */
+ private void getTouchableRegion(Region outRegion) {
+ int childCount = getChildCount();
+ Rect temp = new Rect();
+ for (int i = 0; i < childCount; i++) {
+ View child = getChildAt(i);
+ if (child instanceof FloatingTaskView) {
+ child.getBoundsOnScreen(temp);
+ outRegion.op(temp, Region.Op.UNION);
+ }
+ }
+ }
+
+ /**
+ * Implementation of the touch handler. Animates the task view based on touch events.
+ */
+ private class TouchHandlerImpl implements FloatingTaskTouchHandler {
+ /**
+ * The view can be stashed by swiping it towards the current edge or moving it there. If
+ * the view gets moved in a way that is not one of these gestures, this is flipped to false.
+ */
+ private boolean mCanStash = true;
+ /**
+ * This is used to indicate that the view has been un-stuck from the dismiss target and
+ * needs to spring to the current touch location.
+ */
+ // TODO: implement this behavior
+ private boolean mSpringToTouchOnNextMotionEvent = false;
+
+ private ArrayList<FlingAnimation> mFlingAnimations;
+ private ViewPropertyAnimator mViewPropertyAnimation;
+
+ private float mViewInitialX;
+ private float mViewInitialY;
+
+ private float[] mMinMax = new float[2];
+
+ @Override
+ public void onDown(@NonNull FloatingTaskView v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY) {
+ mCanStash = true;
+ mViewInitialX = viewInitialX;
+ mViewInitialY = viewInitialY;
+ mDismissController.setUpMagneticObject(v);
+ mDismissController.passEventToMagnetizedObject(ev);
+ }
+
+ @Override
+ public void onMove(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
+ float dx, float dy) {
+ // Shows the magnetic dismiss target if needed.
+ mDismissController.showDismiss(/* show= */ true);
+
+ // Send it to magnetic target first.
+ if (mDismissController.passEventToMagnetizedObject(ev)) {
+ v.setStashed(false);
+ mCanStash = true;
+
+ return;
+ }
+
+ // If we're here magnetic target didn't want it so move as per normal.
+
+ v.setTranslationX(capX(v, mViewInitialX + dx, /* isMoving= */ true));
+ v.setTranslationY(capY(v, mViewInitialY + dy));
+ if (v.isStashed()) {
+ // Check if we've moved far enough to be not stashed.
+ final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f);
+ final boolean viewInitiallyOnLeftSide = mViewInitialX < centerX;
+ if (viewInitiallyOnLeftSide) {
+ if (v.getTranslationX() > mPositionRect.left) {
+ v.setStashed(false);
+ mCanStash = true;
+ }
+ } else if (v.getTranslationX() + v.getWidth() < mPositionRect.right) {
+ v.setStashed(false);
+ mCanStash = true;
+ }
+ }
+ }
+
+ // Reference for math / values: StackAnimationController#flingStackThenSpringToEdge.
+ // TODO clean up the code here, pretty hard to comprehend
+ // TODO code here doesn't work the best when in portrait (e.g. can't fling up/down on edges)
+ @Override
+ public void onUp(@NonNull FloatingTaskView v, @NonNull MotionEvent ev,
+ float dx, float dy, float velX, float velY) {
+
+ // Send it to magnetic target first.
+ if (mDismissController.passEventToMagnetizedObject(ev)) {
+ v.setStashed(false);
+ return;
+ }
+ mDismissController.showDismiss(/* show= */ false);
+
+ // If we're here magnetic target didn't want it so handle up as per normal.
+
+ final float x = capX(v, mViewInitialX + dx, /* isMoving= */ false);
+ final float centerX = mPositionRect.centerX();
+ final boolean viewInitiallyOnLeftSide = mViewInitialX + v.getWidth() < centerX;
+ final boolean viewOnLeftSide = x + v.getWidth() < centerX;
+ final boolean isFling = Math.abs(velX) > ESCAPE_VELOCITY;
+ final boolean isFlingLeft = isFling && velX < ESCAPE_VELOCITY;
+ // TODO: check velX here sometimes it doesn't stash on move when I think it should
+ final boolean shouldStashFromMove =
+ (velX < 0 && v.getTranslationX() < mPositionRect.left)
+ || (velX > 0
+ && v.getTranslationX() + v.getWidth() > mPositionRect.right);
+ final boolean shouldStashFromFling = viewInitiallyOnLeftSide == viewOnLeftSide
+ && isFling
+ && ((viewOnLeftSide && velX < ESCAPE_VELOCITY)
+ || (!viewOnLeftSide && velX > ESCAPE_VELOCITY));
+ final boolean shouldStash = mCanStash && (shouldStashFromFling || shouldStashFromMove);
+
+ ProtoLog.d(WM_SHELL_FLOATING_APPS,
+ "shouldStash=%s shouldStashFromFling=%s shouldStashFromMove=%s"
+ + " viewInitiallyOnLeftSide=%s viewOnLeftSide=%s isFling=%s velX=%f"
+ + " isStashed=%s", shouldStash, shouldStashFromFling, shouldStashFromMove,
+ viewInitiallyOnLeftSide, viewOnLeftSide, isFling, velX, v.isStashed());
+
+ if (v.isStashed()) {
+ mMinMax[0] = viewOnLeftSide
+ ? mPositionRect.left - v.getWidth() + mOverhangWhenStashed
+ : mPositionRect.right - v.getWidth();
+ mMinMax[1] = viewOnLeftSide
+ ? mPositionRect.left
+ : mPositionRect.right - mOverhangWhenStashed;
+ } else {
+ populateMinMax(v, viewOnLeftSide, shouldStash, mMinMax);
+ }
+
+ boolean movingLeft = isFling ? isFlingLeft : viewOnLeftSide;
+ float destinationRelativeX = movingLeft
+ ? mMinMax[0]
+ : mMinMax[1];
+
+ // TODO: why is this necessary / when does this happen?
+ if (mMinMax[1] < v.getTranslationX()) {
+ mMinMax[1] = v.getTranslationX();
+ }
+ if (v.getTranslationX() < mMinMax[0]) {
+ mMinMax[0] = v.getTranslationX();
+ }
+
+ // Use the touch event's velocity if it's sufficient, otherwise use the minimum velocity
+ // so that it'll make it all the way to the side of the screen.
+ final float minimumVelocityToReachEdge =
+ getMinimumVelocityToReachEdge(v, destinationRelativeX);
+ final float startXVelocity = movingLeft
+ ? Math.min(minimumVelocityToReachEdge, velX)
+ : Math.max(minimumVelocityToReachEdge, velX);
+
+ cancelAnyAnimations(v);
+
+ mFlingAnimations = getAnimationForUpEvent(v, shouldStash,
+ startXVelocity, mMinMax[0], mMinMax[1], destinationRelativeX);
+ for (int i = 0; i < mFlingAnimations.size(); i++) {
+ mFlingAnimations.get(i).start();
+ }
+ }
+
+ @Override
+ public void onClick(@NonNull FloatingTaskView v) {
+ if (v.isStashed()) {
+ final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f);
+ final boolean viewOnLeftSide = v.getTranslationX() < centerX;
+ final float destinationRelativeX = viewOnLeftSide
+ ? mPositionRect.left
+ : mPositionRect.right - v.getWidth();
+ final float minimumVelocityToReachEdge =
+ getMinimumVelocityToReachEdge(v, destinationRelativeX);
+ populateMinMax(v, viewOnLeftSide, /* stashed= */ true, mMinMax);
+
+ cancelAnyAnimations(v);
+
+ FlingAnimation flingAnimation = new FlingAnimation(v,
+ DynamicAnimation.TRANSLATION_X);
+ flingAnimation.setFriction(FLING_FRICTION)
+ .setStartVelocity(minimumVelocityToReachEdge)
+ .setMinValue(mMinMax[0])
+ .setMaxValue(mMinMax[1])
+ .addEndListener((animation, canceled, value, velocity) -> {
+ if (canceled) return;
+ mController.setLastPosition((int) v.getTranslationX(),
+ (int) v.getTranslationY());
+ v.setStashed(false);
+ v.updateLocation();
+ });
+ mFlingAnimations = new ArrayList<>();
+ mFlingAnimations.add(flingAnimation);
+ flingAnimation.start();
+ }
+ }
+
+ public void onUnstuckFromTarget(FloatingTaskView v, float velX, float velY,
+ boolean wasFlungOut) {
+ if (wasFlungOut) {
+ snapTaskViewToEdge(v, velX, /* shouldStash= */ false);
+ } else {
+ // TODO: use this for something / to spring the view to the touch location
+ mSpringToTouchOnNextMotionEvent = true;
+ }
+ }
+
+ public void stashTaskView(FloatingTaskView v, boolean shouldStash) {
+ if (v.isStashed() == shouldStash) {
+ return;
+ }
+ final float centerX = mPositionRect.centerX() - (v.getWidth() / 2f);
+ final boolean viewOnLeftSide = v.getTranslationX() < centerX;
+ snapTaskViewToEdge(v, viewOnLeftSide ? -ESCAPE_VELOCITY : ESCAPE_VELOCITY, shouldStash);
+ }
+
+ public boolean isStashedPosition(View v) {
+ return v.getTranslationX() < mPositionRect.left
+ || v.getTranslationX() + v.getWidth() > mPositionRect.right;
+ }
+
+ // TODO: a lot of this is duplicated in onUp -- can it be unified?
+ private void snapTaskViewToEdge(FloatingTaskView v, float velX, boolean shouldStash) {
+ final boolean movingLeft = velX < ESCAPE_VELOCITY;
+ populateMinMax(v, movingLeft, shouldStash, mMinMax);
+ float destinationRelativeX = movingLeft
+ ? mMinMax[0]
+ : mMinMax[1];
+
+ // TODO: why is this necessary / when does this happen?
+ if (mMinMax[1] < v.getTranslationX()) {
+ mMinMax[1] = v.getTranslationX();
+ }
+ if (v.getTranslationX() < mMinMax[0]) {
+ mMinMax[0] = v.getTranslationX();
+ }
+
+ // Use the touch event's velocity if it's sufficient, otherwise use the minimum velocity
+ // so that it'll make it all the way to the side of the screen.
+ final float minimumVelocityToReachEdge =
+ getMinimumVelocityToReachEdge(v, destinationRelativeX);
+ final float startXVelocity = movingLeft
+ ? Math.min(minimumVelocityToReachEdge, velX)
+ : Math.max(minimumVelocityToReachEdge, velX);
+
+ cancelAnyAnimations(v);
+
+ mFlingAnimations = getAnimationForUpEvent(v,
+ shouldStash, startXVelocity, mMinMax[0], mMinMax[1],
+ destinationRelativeX);
+ for (int i = 0; i < mFlingAnimations.size(); i++) {
+ mFlingAnimations.get(i).start();
+ }
+ }
+
+ private void cancelAnyAnimations(FloatingTaskView v) {
+ if (mFlingAnimations != null) {
+ for (int i = 0; i < mFlingAnimations.size(); i++) {
+ if (mFlingAnimations.get(i).isRunning()) {
+ mFlingAnimations.get(i).cancel();
+ }
+ }
+ }
+ if (mViewPropertyAnimation != null) {
+ mViewPropertyAnimation.cancel();
+ mViewPropertyAnimation = null;
+ }
+ }
+
+ private ArrayList<FlingAnimation> getAnimationForUpEvent(FloatingTaskView v,
+ boolean shouldStash, float startVelX, float minValue, float maxValue,
+ float destinationRelativeX) {
+ final float ty = v.getTranslationY();
+ final ArrayList<FlingAnimation> animations = new ArrayList<>();
+ if (ty != capY(v, ty)) {
+ // The view was being dismissed so the Y is out of bounds, need to animate that.
+ FlingAnimation yFlingAnimation = new FlingAnimation(v,
+ DynamicAnimation.TRANSLATION_Y);
+ yFlingAnimation.setFriction(FLING_FRICTION)
+ .setStartVelocity(startVelX)
+ .setMinValue(mPositionRect.top)
+ .setMaxValue(mPositionRect.bottom - mTaskViewSize.y);
+ animations.add(yFlingAnimation);
+ }
+ FlingAnimation flingAnimation = new FlingAnimation(v, DynamicAnimation.TRANSLATION_X);
+ flingAnimation.setFriction(FLING_FRICTION)
+ .setStartVelocity(startVelX)
+ .setMinValue(minValue)
+ .setMaxValue(maxValue)
+ .addEndListener((animation, canceled, value, velocity) -> {
+ if (canceled) return;
+ Runnable endAction = () -> {
+ v.setStashed(shouldStash);
+ v.updateLocation();
+ if (!v.isStashed()) {
+ mController.setLastPosition((int) v.getTranslationX(),
+ (int) v.getTranslationY());
+ }
+ };
+ if (!shouldStash) {
+ final int xTranslation = (int) v.getTranslationX();
+ if (xTranslation != destinationRelativeX) {
+ // TODO: this animation doesn't feel great, should figure out
+ // a better way to do this or remove the need for it all together.
+ mViewPropertyAnimation = v.animate()
+ .translationX(destinationRelativeX)
+ .setListener(getAnimationListener(endAction));
+ mViewPropertyAnimation.start();
+ } else {
+ endAction.run();
+ }
+ } else {
+ endAction.run();
+ }
+ });
+ animations.add(flingAnimation);
+ return animations;
+ }
+
+ private AnimatorListenerAdapter getAnimationListener(Runnable endAction) {
+ return new AnimatorListenerAdapter() {
+ boolean translationCanceled = false;
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ super.onAnimationCancel(animation);
+ translationCanceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ if (!translationCanceled) {
+ endAction.run();
+ }
+ }
+ };
+ }
+
+ private void populateMinMax(FloatingTaskView v, boolean onLeft, boolean shouldStash,
+ float[] out) {
+ if (shouldStash) {
+ out[0] = onLeft
+ ? mPositionRect.left - v.getWidth() + mOverhangWhenStashed
+ : mPositionRect.right - v.getWidth();
+ out[1] = onLeft
+ ? mPositionRect.left
+ : mPositionRect.right - mOverhangWhenStashed;
+ } else {
+ out[0] = mPositionRect.left;
+ out[1] = mPositionRect.right - mTaskViewSize.x;
+ }
+ }
+
+ private float getMinimumVelocityToReachEdge(FloatingTaskView v,
+ float destinationRelativeX) {
+ // Minimum velocity required for the view to make it to the targeted side of the screen,
+ // taking friction into account (4.2f is the number that friction scalars are multiplied
+ // by in DynamicAnimation.DragForce). This is an estimate and could be slightly off, the
+ // animation at the end will ensure that it reaches the destination X regardless.
+ return (destinationRelativeX - v.getTranslationX()) * (FLING_FRICTION * 4.2f);
+ }
+
+ private float capX(FloatingTaskView v, float x, boolean isMoving) {
+ final int width = v.getWidth();
+ if (v.isStashed() || isMoving) {
+ if (x < mPositionRect.left - v.getWidth() + mOverhangWhenStashed) {
+ return mPositionRect.left - v.getWidth() + mOverhangWhenStashed;
+ }
+ if (x > mPositionRect.right - mOverhangWhenStashed) {
+ return mPositionRect.right - mOverhangWhenStashed;
+ }
+ } else {
+ if (x < mPositionRect.left) {
+ return mPositionRect.left;
+ }
+ if (x > mPositionRect.right - width) {
+ return mPositionRect.right - width;
+ }
+ }
+ return x;
+ }
+
+ private float capY(FloatingTaskView v, float y) {
+ final int height = v.getHeight();
+ if (y < mPositionRect.top) {
+ return mPositionRect.top;
+ }
+ if (y > mPositionRect.bottom - height) {
+ return mPositionRect.bottom - height;
+ }
+ return y;
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java
new file mode 100644
index 000000000000..581204a82ec7
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/floating/views/FloatingTaskView.java
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.floating.views;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
+
+import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_FLOATING_APPS;
+
+import android.app.ActivityManager;
+import android.app.ActivityOptions;
+import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.policy.ScreenDecorationsUtils;
+import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.R;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.TaskView;
+import com.android.wm.shell.TaskViewTransitions;
+import com.android.wm.shell.bubbles.RelativeTouchListener;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.common.annotations.ShellMainThread;
+import com.android.wm.shell.floating.FloatingTasksController;
+
+/**
+ * A view that holds a floating task using {@link TaskView} along with additional UI to manage
+ * the task.
+ */
+public class FloatingTaskView extends FrameLayout {
+
+ private static final String TAG = FloatingTaskView.class.getSimpleName();
+
+ private FloatingTasksController mController;
+
+ private FloatingMenuView mMenuView;
+ private int mMenuHeight;
+ private TaskView mTaskView;
+
+ private float mCornerRadius = 0f;
+ private int mBackgroundColor;
+
+ private FloatingTasksController.Task mTask;
+
+ private boolean mIsStashed;
+
+ /**
+ * Creates a floating task view.
+ *
+ * @param context the context to use.
+ * @param controller the controller to notify about changes in the floating task (e.g. removal).
+ */
+ public FloatingTaskView(Context context, FloatingTasksController controller) {
+ super(context);
+ mController = controller;
+ setElevation(getResources().getDimensionPixelSize(R.dimen.floating_task_elevation));
+ mMenuHeight = context.getResources().getDimensionPixelSize(R.dimen.floating_task_menu_size);
+ mMenuView = new FloatingMenuView(context);
+ addView(mMenuView);
+
+ applyThemeAttrs();
+
+ setClipToOutline(true);
+ setOutlineProvider(new ViewOutlineProvider() {
+ @Override
+ public void getOutline(View view, Outline outline) {
+ outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), mCornerRadius);
+ }
+ });
+ }
+
+ // TODO: call this when theme/config changes
+ void applyThemeAttrs() {
+ boolean supportsRoundedCorners = ScreenDecorationsUtils.supportsRoundedCornersOnWindows(
+ mContext.getResources());
+ final TypedArray ta = mContext.obtainStyledAttributes(new int[] {
+ android.R.attr.dialogCornerRadius,
+ android.R.attr.colorBackgroundFloating});
+ mCornerRadius = supportsRoundedCorners ? ta.getDimensionPixelSize(0, 0) : 0;
+ mCornerRadius = mCornerRadius / 2f;
+ mBackgroundColor = ta.getColor(1, Color.WHITE);
+
+ ta.recycle();
+
+ mMenuView.setCornerRadius(mCornerRadius);
+ mMenuHeight = getResources().getDimensionPixelSize(
+ R.dimen.floating_task_menu_size);
+
+ if (mTaskView != null) {
+ mTaskView.setCornerRadius(mCornerRadius);
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int height = MeasureSpec.getSize(heightMeasureSpec);
+
+ // Add corner radius here so that the menu extends behind the rounded corners of TaskView.
+ int menuViewHeight = Math.min((int) (mMenuHeight + mCornerRadius), height);
+ measureChild(mMenuView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(menuViewHeight,
+ MeasureSpec.getMode(heightMeasureSpec)));
+
+ if (mTaskView != null) {
+ int taskViewHeight = height - menuViewHeight;
+ measureChild(mTaskView, widthMeasureSpec, MeasureSpec.makeMeasureSpec(taskViewHeight,
+ MeasureSpec.getMode(heightMeasureSpec)));
+ }
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // Drag handle above
+ final int dragHandleBottom = t + mMenuView.getMeasuredHeight();
+ mMenuView.layout(l, t, r, dragHandleBottom);
+ if (mTaskView != null) {
+ // Subtract radius so that the menu extends behind the rounded corners of TaskView.
+ mTaskView.layout(l, (int) (dragHandleBottom - mCornerRadius), r,
+ dragHandleBottom + mTaskView.getMeasuredHeight());
+ }
+ }
+
+ /**
+ * Constructs the TaskView to display the task. Must be called for {@link #startTask} to work.
+ */
+ public void createTaskView(Context context, ShellTaskOrganizer organizer,
+ TaskViewTransitions transitions, SyncTransactionQueue syncQueue) {
+ mTaskView = new TaskView(context, organizer, transitions, syncQueue);
+ addView(mTaskView);
+ mTaskView.setEnableSurfaceClipping(true);
+ mTaskView.setCornerRadius(mCornerRadius);
+ }
+
+ /**
+ * Starts the provided task in the TaskView, if the TaskView exists. This should be called after
+ * {@link #createTaskView}.
+ */
+ public void startTask(@ShellMainThread ShellExecutor executor,
+ FloatingTasksController.Task task) {
+ if (mTaskView == null) {
+ Log.e(TAG, "starting task before creating the view!");
+ return;
+ }
+ mTask = task;
+ mTaskView.setListener(executor, mTaskViewListener);
+ }
+
+ /**
+ * Sets the touch handler for the view.
+ *
+ * @param handler the touch handler for the view.
+ */
+ public void setTouchHandler(FloatingTaskLayer.FloatingTaskTouchHandler handler) {
+ setOnTouchListener(new RelativeTouchListener() {
+ @Override
+ public boolean onDown(@NonNull View v, @NonNull MotionEvent ev) {
+ handler.onDown(FloatingTaskView.this, ev, v.getTranslationX(), v.getTranslationY());
+ return true;
+ }
+
+ @Override
+ public void onMove(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy) {
+ handler.onMove(FloatingTaskView.this, ev, dx, dy);
+ }
+
+ @Override
+ public void onUp(@NonNull View v, @NonNull MotionEvent ev, float viewInitialX,
+ float viewInitialY, float dx, float dy, float velX, float velY) {
+ handler.onUp(FloatingTaskView.this, ev, dx, dy, velX, velY);
+ }
+ });
+ setOnClickListener(view -> {
+ handler.onClick(FloatingTaskView.this);
+ });
+
+ mMenuView.addMenuItem(null, view -> {
+ if (mIsStashed) {
+ // If we're stashed all clicks un-stash.
+ handler.onClick(FloatingTaskView.this);
+ }
+ });
+ }
+
+ private void setContentVisibility(boolean visible) {
+ if (mTaskView == null) return;
+ mTaskView.setAlpha(visible ? 1f : 0f);
+ }
+
+ /**
+ * Sets the alpha of both this view and the TaskView.
+ */
+ public void setTaskViewAlpha(float alpha) {
+ if (mTaskView != null) {
+ mTaskView.setAlpha(alpha);
+ }
+ setAlpha(alpha);
+ }
+
+ /**
+ * Call when the location or size of the view has changed to update TaskView.
+ */
+ public void updateLocation() {
+ if (mTaskView == null) return;
+ mTaskView.onLocationChanged();
+ }
+
+ private void updateMenuColor() {
+ ActivityManager.RunningTaskInfo info = mTaskView.getTaskInfo();
+ int color = info != null ? info.taskDescription.getBackgroundColor() : -1;
+ if (color != -1) {
+ mMenuView.setBackgroundColor(color);
+ } else {
+ mMenuView.setBackgroundColor(mBackgroundColor);
+ }
+ }
+
+ /**
+ * Sets whether the view is stashed or not.
+ *
+ * Also updates the touchable area based on this. If the view is stashed we don't direct taps
+ * on the activity to the activity, instead a tap will un-stash the view.
+ */
+ public void setStashed(boolean isStashed) {
+ if (mIsStashed != isStashed) {
+ mIsStashed = isStashed;
+ if (mTaskView == null) {
+ return;
+ }
+ updateObscuredTouchRect();
+ }
+ }
+
+ /** Whether the view is stashed at the edge of the screen or not. **/
+ public boolean isStashed() {
+ return mIsStashed;
+ }
+
+ private void updateObscuredTouchRect() {
+ if (mIsStashed) {
+ Rect tmpRect = new Rect();
+ getBoundsOnScreen(tmpRect);
+ mTaskView.setObscuredTouchRect(tmpRect);
+ } else {
+ mTaskView.setObscuredTouchRect(null);
+ }
+ }
+
+ /**
+ * Whether the task needs to be restarted, this can happen when {@link #cleanUpTaskView()} has
+ * been called on this view or if
+ * {@link #startTask(ShellExecutor, FloatingTasksController.Task)} was never called.
+ */
+ public boolean needsTaskStarted() {
+ // If the task needs to be restarted then TaskView would have been cleaned up.
+ return mTaskView == null;
+ }
+
+ /** Call this when the floating task activity is no longer in use. */
+ public void cleanUpTaskView() {
+ if (mTask != null && mTask.taskId != INVALID_TASK_ID) {
+ try {
+ ActivityTaskManager.getService().removeTask(mTask.taskId);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage());
+ }
+ }
+ if (mTaskView != null) {
+ mTaskView.release();
+ removeView(mTaskView);
+ mTaskView = null;
+ }
+ }
+
+ // TODO: use task background colour / how to get the taskInfo ?
+ private static int getDragBarColor(ActivityManager.RunningTaskInfo taskInfo) {
+ final int taskBgColor = taskInfo.taskDescription.getStatusBarColor();
+ return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).toArgb();
+ }
+
+ private final TaskView.Listener mTaskViewListener = new TaskView.Listener() {
+ private boolean mInitialized = false;
+ private boolean mDestroyed = false;
+
+ @Override
+ public void onInitialized() {
+ if (mDestroyed || mInitialized) {
+ return;
+ }
+ // Custom options so there is no activity transition animation
+ ActivityOptions options = ActivityOptions.makeCustomAnimation(getContext(),
+ /* enterResId= */ 0, /* exitResId= */ 0);
+
+ Rect launchBounds = new Rect();
+ mTaskView.getBoundsOnScreen(launchBounds);
+
+ try {
+ options.setTaskAlwaysOnTop(true);
+ if (mTask.intent != null) {
+ Intent fillInIntent = new Intent();
+ // Apply flags to make behaviour match documentLaunchMode=always.
+ fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+ fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
+
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0, mTask.intent,
+ PendingIntent.FLAG_MUTABLE,
+ null);
+ mTaskView.startActivity(pi, fillInIntent, options, launchBounds);
+ } else {
+ ProtoLog.e(WM_SHELL_FLOATING_APPS, "Tried to start a task with null intent");
+ }
+ } catch (RuntimeException e) {
+ ProtoLog.e(WM_SHELL_FLOATING_APPS, "Exception while starting task: %s",
+ e.getMessage());
+ mController.removeTask();
+ }
+ mInitialized = true;
+ }
+
+ @Override
+ public void onReleased() {
+ mDestroyed = true;
+ }
+
+ @Override
+ public void onTaskCreated(int taskId, ComponentName name) {
+ mTask.taskId = taskId;
+ updateMenuColor();
+ setContentVisibility(true);
+ }
+
+ @Override
+ public void onTaskVisibilityChanged(int taskId, boolean visible) {
+ setContentVisibility(visible);
+ }
+
+ @Override
+ public void onTaskRemovalStarted(int taskId) {
+ // Must post because this is called from a binder thread.
+ post(() -> {
+ mController.removeTask();
+ cleanUpTaskView();
+ });
+ }
+
+ @Override
+ public void onBackPressedOnTaskRoot(int taskId) {
+ if (mTask.taskId == taskId && !mIsStashed) {
+ // TODO: is removing the window the desired behavior?
+ post(() -> mController.removeTask());
+ }
+ }
+ };
+}
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
index 1baac718ee95..e2d5a499d1e1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskListener.java
@@ -28,9 +28,9 @@ import androidx.annotation.Nullable;
import com.android.internal.protolog.common.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.desktopmode.DesktopMode;
+import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
-import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
@@ -49,7 +49,7 @@ public class FreeformTaskListener<T extends AutoCloseable>
private static final String TAG = "FreeformTaskListener";
private final ShellTaskOrganizer mShellTaskOrganizer;
- private final Optional<RecentTasksController> mRecentTasksOptional;
+ private final Optional<DesktopModeTaskRepository> mDesktopModeTaskRepository;
private final WindowDecorViewModel<T> mWindowDecorationViewModel;
private final SparseArray<State<T>> mTasks = new SparseArray<>();
@@ -64,11 +64,11 @@ public class FreeformTaskListener<T extends AutoCloseable>
public FreeformTaskListener(
ShellInit shellInit,
ShellTaskOrganizer shellTaskOrganizer,
- Optional<RecentTasksController> recentTasksController,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
WindowDecorViewModel<T> windowDecorationViewModel) {
mShellTaskOrganizer = shellTaskOrganizer;
mWindowDecorationViewModel = windowDecorationViewModel;
- mRecentTasksOptional = recentTasksController;
+ mDesktopModeTaskRepository = desktopModeTaskRepository;
if (shellInit != null) {
shellInit.addInitCallback(this::onInit, this);
}
@@ -90,10 +90,10 @@ public class FreeformTaskListener<T extends AutoCloseable>
t.apply();
}
- if (DesktopMode.IS_SUPPORTED && taskInfo.isVisible) {
+ if (DesktopModeStatus.IS_SUPPORTED && taskInfo.isVisible) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Adding active freeform task: #%d", taskInfo.taskId);
- mRecentTasksOptional.ifPresent(rt -> rt.addActiveFreeformTask(taskInfo.taskId));
+ mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTask(taskInfo.taskId));
}
}
@@ -123,10 +123,10 @@ public class FreeformTaskListener<T extends AutoCloseable>
taskInfo.taskId);
mTasks.remove(taskInfo.taskId);
- if (DesktopMode.IS_SUPPORTED) {
+ if (DesktopModeStatus.IS_SUPPORTED) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Removing active freeform task: #%d", taskInfo.taskId);
- mRecentTasksOptional.ifPresent(rt -> rt.removeActiveFreeformTask(taskInfo.taskId));
+ mDesktopModeTaskRepository.ifPresent(it -> it.removeActiveTask(taskInfo.taskId));
}
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
@@ -150,11 +150,11 @@ public class FreeformTaskListener<T extends AutoCloseable>
mWindowDecorationViewModel.onTaskInfoChanged(state.mTaskInfo, state.mWindowDecoration);
}
- if (DesktopMode.IS_SUPPORTED) {
+ if (DesktopModeStatus.IS_SUPPORTED) {
if (taskInfo.isVisible) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
"Adding active freeform task: #%d", taskInfo.taskId);
- mRecentTasksOptional.ifPresent(rt -> rt.addActiveFreeformTask(taskInfo.taskId));
+ mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTask(taskInfo.taskId));
}
}
}
@@ -191,15 +191,19 @@ public class FreeformTaskListener<T extends AutoCloseable>
*
* @param change the change of this task transition that needs to have the task layer as the
* leash
- * @return {@code true} if it adopts the window decoration; {@code false} otherwise
+ * @return {@code true} if it creates the window decoration; {@code false} otherwise
*/
- void createWindowDecoration(
+ boolean createWindowDecoration(
TransitionInfo.Change change,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
+ if (state.mWindowDecoration != null) {
+ return false;
+ }
state.mWindowDecoration = mWindowDecorationViewModel.createWindowDecoration(
state.mTaskInfo, state.mLeash, startT, finishT);
+ return true;
}
/**
@@ -222,6 +226,9 @@ public class FreeformTaskListener<T extends AutoCloseable>
windowDecor =
mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId);
}
+ if (windowDecor == null) {
+ return null;
+ }
mWindowDecorationViewModel.setupWindowDecorationForTransition(
taskInfo, startT, finishT, windowDecor);
return windowDecor;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
index a780ec102ea9..17d60671e964 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/freeform/FreeformTaskTransitionObserver.java
@@ -26,6 +26,7 @@ import android.util.Log;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;
@@ -80,6 +81,7 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
@NonNull SurfaceControl.Transaction startT,
@NonNull SurfaceControl.Transaction finishT) {
final ArrayList<AutoCloseable> windowDecors = new ArrayList<>();
+ final ArrayList<WindowContainerToken> taskParents = new ArrayList<>();
for (TransitionInfo.Change change : info.getChanges()) {
if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
continue;
@@ -89,9 +91,22 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
if (taskInfo == null || taskInfo.taskId == -1) {
continue;
}
+ // Filter out non-leaf tasks. Freeform/fullscreen don't nest tasks, but split-screen
+ // does, so this prevents adding duplicate captions in that scenario.
+ if (change.getParent() != null
+ && info.getChange(change.getParent()).getTaskInfo() != null) {
+ // This logic relies on 2 assumptions: 1 is that child tasks will be visited before
+ // parents (due to how z-order works). 2 is that no non-tasks are interleaved
+ // between tasks (hierarchically).
+ taskParents.add(change.getContainer());
+ }
+ if (taskParents.contains(change.getContainer())) {
+ continue;
+ }
switch (change.getMode()) {
case WindowManager.TRANSIT_OPEN:
+ case WindowManager.TRANSIT_TO_FRONT:
onOpenTransitionReady(change, startT, finishT);
break;
case WindowManager.TRANSIT_CLOSE: {
@@ -154,20 +169,28 @@ public class FreeformTaskTransitionObserver implements Transitions.TransitionObs
boolean adopted = false;
final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (type == Transitions.TRANSIT_MAXIMIZE
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
windowDecor = mFreeformTaskListener.giveWindowDecoration(
change.getTaskInfo(), startT, finishT);
- adopted = mFullscreenTaskListener.adoptWindowDecoration(
- change, startT, finishT, windowDecor);
+ if (windowDecor != null) {
+ adopted = mFullscreenTaskListener.adoptWindowDecoration(
+ change, startT, finishT, windowDecor);
+ } else {
+ // will return false if it already has the window decor.
+ adopted = mFullscreenTaskListener.createWindowDecoration(change, startT, finishT);
+ }
}
- if (type == Transitions.TRANSIT_RESTORE_FROM_MAXIMIZE
- && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
windowDecor = mFullscreenTaskListener.giveWindowDecoration(
change.getTaskInfo(), startT, finishT);
- adopted = mFreeformTaskListener.adoptWindowDecoration(
- change, startT, finishT, windowDecor);
+ if (windowDecor != null) {
+ adopted = mFreeformTaskListener.adoptWindowDecoration(
+ change, startT, finishT, windowDecor);
+ } else {
+ // will return false if it already has the window decor.
+ adopted = mFreeformTaskListener.createWindowDecoration(change, startT, finishT);
+ }
}
if (!adopted) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
index e9f9bb5d7327..76e296bb8c61 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/fullscreen/FullscreenTaskListener.java
@@ -16,8 +16,6 @@
package com.android.wm.shell.fullscreen;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
import static com.android.wm.shell.ShellTaskOrganizer.TASK_LISTENER_TYPE_FULLSCREEN;
import static com.android.wm.shell.ShellTaskOrganizer.taskListenerTypeToString;
@@ -107,13 +105,14 @@ public class FullscreenTaskListener<T extends AutoCloseable>
if (Transitions.ENABLE_SHELL_TRANSITIONS) return;
updateRecentsForVisibleFullscreenTask(taskInfo);
- if (shouldShowWindowDecor(taskInfo) && mWindowDecorViewModelOptional.isPresent()) {
+ if (mWindowDecorViewModelOptional.isPresent()) {
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
state.mWindowDecoration =
mWindowDecorViewModelOptional.get().createWindowDecoration(taskInfo,
leash, t, t);
t.apply();
- } else {
+ }
+ if (state.mWindowDecoration == null) {
mSyncQueue.runInSync(t -> {
// Reset several properties back to fullscreen (PiP, for example, leaves all these
// properties in a bad state).
@@ -174,17 +173,23 @@ public class FullscreenTaskListener<T extends AutoCloseable>
*
* @param change the change of this task transition that needs to have the task layer as the
* leash
+ * @return {@code true} if a decoration was actually created.
*/
- public void createWindowDecoration(TransitionInfo.Change change,
+ public boolean createWindowDecoration(TransitionInfo.Change change,
SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
- if (!mWindowDecorViewModelOptional.isPresent()
- || !shouldShowWindowDecor(state.mTaskInfo)) {
- return;
+ if (!mWindowDecorViewModelOptional.isPresent()) return false;
+ if (state.mWindowDecoration != null) {
+ // Already has a decoration.
+ return false;
}
-
- state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration(
+ T newWindowDecor = mWindowDecorViewModelOptional.get().createWindowDecoration(
state.mTaskInfo, state.mLeash, startT, finishT);
+ if (newWindowDecor != null) {
+ state.mWindowDecoration = newWindowDecor;
+ return true;
+ }
+ return false;
}
/**
@@ -202,8 +207,7 @@ public class FullscreenTaskListener<T extends AutoCloseable>
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT,
@Nullable AutoCloseable windowDecor) {
- if (!mWindowDecorViewModelOptional.isPresent()
- || !shouldShowWindowDecor(change.getTaskInfo())) {
+ if (!mWindowDecorViewModelOptional.isPresent()) {
return false;
}
final State<T> state = createOrUpdateTaskState(change.getTaskInfo(), change.getLeash());
@@ -214,8 +218,11 @@ public class FullscreenTaskListener<T extends AutoCloseable>
state.mTaskInfo, startT, finishT, state.mWindowDecoration);
return true;
} else {
- state.mWindowDecoration = mWindowDecorViewModelOptional.get().createWindowDecoration(
+ T newWindowDecor = mWindowDecorViewModelOptional.get().createWindowDecoration(
state.mTaskInfo, state.mLeash, startT, finishT);
+ if (newWindowDecor != null) {
+ state.mWindowDecoration = newWindowDecor;
+ }
return false;
}
}
@@ -256,7 +263,7 @@ public class FullscreenTaskListener<T extends AutoCloseable>
windowDecor =
mWindowDecorOfVanishedTasks.removeReturnOld(taskInfo.taskId);
}
- if (mWindowDecorViewModelOptional.isPresent()) {
+ if (mWindowDecorViewModelOptional.isPresent() && windowDecor != null) {
mWindowDecorViewModelOptional.get().setupWindowDecorationForTransition(
taskInfo, startT, finishT, windowDecor);
}
@@ -336,10 +343,5 @@ public class FullscreenTaskListener<T extends AutoCloseable>
return TAG + ":" + taskListenerTypeToString(TASK_LISTENER_TYPE_FULLSCREEN);
}
- private static boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
- return taskInfo.getConfiguration().windowConfiguration.getDisplayWindowingMode()
- == WINDOWING_MODE_FREEFORM;
- }
-
}
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 b32c3eed2fb4..6728c00af51b 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
@@ -195,6 +195,17 @@ public class PipAnimationController {
}
/**
+ * Returns true if the PiP window is currently being animated.
+ */
+ public boolean isAnimating() {
+ PipAnimationController.PipTransitionAnimator animator = getCurrentAnimator();
+ if (animator != null && animator.isRunning()) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Quietly cancel the animator by removing the listeners first.
*/
static void quietCancel(@NonNull ValueAnimator animator) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
index 29434f73e84b..fa0061982c45 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipUtils.java
@@ -83,7 +83,9 @@ public class PipUtils {
public static boolean remoteActionsMatch(RemoteAction action1, RemoteAction action2) {
if (action1 == action2) return true;
if (action1 == null || action2 == null) return false;
- return Objects.equals(action1.getTitle(), action2.getTitle())
+ return action1.isEnabled() == action2.isEnabled()
+ && action1.shouldShowIcon() == action2.shouldShowIcon()
+ && Objects.equals(action1.getTitle(), action2.getTitle())
&& Objects.equals(action1.getContentDescription(), action2.getContentDescription())
&& Objects.equals(action1.getActionIntent(), action2.getActionIntent());
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
index 6dd02e46d657..84071e08d472 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
@@ -54,13 +54,8 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithm {
? pipBoundsAlgorithm.getEntryDestinationBoundsIgnoringKeepClearAreas()
: pipBoundsState.getBounds();
float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
- int verticalGravity;
+ int verticalGravity = Gravity.BOTTOM;
int horizontalGravity;
- if (snapFraction < 1.5f || snapFraction >= 3.5f) {
- verticalGravity = Gravity.NO_GRAVITY;
- } else {
- verticalGravity = Gravity.BOTTOM;
- }
if (snapFraction >= 0.5f && snapFraction < 2.5f) {
horizontalGravity = Gravity.RIGHT;
} else {
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 3d879b685706..af47666efa5a 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
@@ -130,6 +130,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private DisplayController mDisplayController;
private PipInputConsumer mPipInputConsumer;
private WindowManagerShellWrapper mWindowManagerShellWrapper;
+ private PipAnimationController mPipAnimationController;
private PipAppOpsListener mAppOpsListener;
private PipMediaController mMediaController;
private PipBoundsAlgorithm mPipBoundsAlgorithm;
@@ -149,7 +150,42 @@ public class PipController implements PipTransitionController.PipTransitionCallb
private final Rect mTmpInsetBounds = new Rect();
private final int mEnterAnimationDuration;
- private final Runnable mMovePipInResponseToKeepClearAreasChangeCallback;
+ private final Runnable mMovePipInResponseToKeepClearAreasChangeCallback =
+ this::onKeepClearAreasChangedCallback;
+
+ private void onKeepClearAreasChangedCallback() {
+ if (!mEnablePipKeepClearAlgorithm) {
+ // early bail out if the keep clear areas feature is disabled
+ return;
+ }
+ // if there is another animation ongoing, wait for it to finish and try again
+ if (mPipAnimationController.isAnimating()) {
+ mMainExecutor.removeCallbacks(
+ mMovePipInResponseToKeepClearAreasChangeCallback);
+ mMainExecutor.executeDelayed(
+ mMovePipInResponseToKeepClearAreasChangeCallback,
+ PIP_KEEP_CLEAR_AREAS_DELAY);
+ return;
+ }
+ updatePipPositionForKeepClearAreas();
+ }
+
+ private void updatePipPositionForKeepClearAreas() {
+ if (!mEnablePipKeepClearAlgorithm) {
+ // early bail out if the keep clear areas feature is disabled
+ return;
+ }
+ // only move if already in pip, other transitions account for keep clear areas
+ if (mPipTransitionState.hasEnteredPip()) {
+ Rect destBounds = mPipKeepClearAlgorithm.adjust(mPipBoundsState,
+ mPipBoundsAlgorithm);
+ // only move if the bounds are actually different
+ if (destBounds != mPipBoundsState.getBounds()) {
+ mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
+ mEnterAnimationDuration, null);
+ }
+ }
+ }
private boolean mIsInFixedRotation;
private PipAnimationListener mPinnedStackAnimationRecentsCallback;
@@ -302,6 +338,9 @@ public class PipController implements PipTransitionController.PipTransitionCallb
public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
mPipBoundsState.setImeVisibility(imeVisible, imeHeight);
mTouchHandler.onImeVisibilityChanged(imeVisible, imeHeight);
+ if (imeVisible) {
+ updatePipPositionForKeepClearAreas();
+ }
}
@Override
@@ -330,6 +369,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
+ PipAnimationController pipAnimationController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipKeepClearAlgorithm pipKeepClearAlgorithm,
@@ -354,11 +394,12 @@ public class PipController implements PipTransitionController.PipTransitionCallb
}
return new PipController(context, shellInit, shellCommandHandler, shellController,
- displayController, pipAppOpsListener, pipBoundsAlgorithm, pipKeepClearAlgorithm,
- pipBoundsState, pipMotionHelper, pipMediaController, phonePipMenuController,
- pipTaskOrganizer, pipTransitionState, pipTouchHandler, pipTransitionController,
- windowManagerShellWrapper, taskStackListener, pipParamsChangedForwarder,
- displayInsetsController, oneHandedController, mainExecutor)
+ displayController, pipAnimationController, pipAppOpsListener,
+ pipBoundsAlgorithm, pipKeepClearAlgorithm, pipBoundsState, pipMotionHelper,
+ pipMediaController, phonePipMenuController, pipTaskOrganizer, pipTransitionState,
+ pipTouchHandler, pipTransitionController, windowManagerShellWrapper,
+ taskStackListener, pipParamsChangedForwarder, displayInsetsController,
+ oneHandedController, mainExecutor)
.mImpl;
}
@@ -367,6 +408,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
ShellCommandHandler shellCommandHandler,
ShellController shellController,
DisplayController displayController,
+ PipAnimationController pipAnimationController,
PipAppOpsListener pipAppOpsListener,
PipBoundsAlgorithm pipBoundsAlgorithm,
PipKeepClearAlgorithm pipKeepClearAlgorithm,
@@ -407,6 +449,7 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mMediaController = pipMediaController;
mMenuController = phonePipMenuController;
mTouchHandler = pipTouchHandler;
+ mPipAnimationController = pipAnimationController;
mAppOpsListener = pipAppOpsListener;
mOneHandedController = oneHandedController;
mPipTransitionController = pipTransitionController;
@@ -414,15 +457,6 @@ public class PipController implements PipTransitionController.PipTransitionCallb
mEnterAnimationDuration = mContext.getResources()
.getInteger(R.integer.config_pipEnterAnimationDuration);
- mMovePipInResponseToKeepClearAreasChangeCallback = () -> {
- // only move if already in pip, other transitions account for keep clear areas
- if (mPipTransitionState.hasEnteredPip()) {
- Rect destBounds = mPipKeepClearAlgorithm.adjust(mPipBoundsState,
- mPipBoundsAlgorithm);
- mPipTaskOrganizer.scheduleAnimateResizePip(destBounds,
- mEnterAnimationDuration, null);
- }
- };
mPipParamsChangedForwarder = pipParamsChangedForwarder;
mDisplayInsetsController = displayInsetsController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 84d9217e6fb3..1f3f31e025a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -34,6 +34,7 @@ import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.os.SystemProperties;
import android.provider.DeviceConfig;
import android.util.Size;
import android.view.DisplayCutout;
@@ -70,6 +71,9 @@ public class PipTouchHandler {
private static final String TAG = "PipTouchHandler";
private static final float DEFAULT_STASH_VELOCITY_THRESHOLD = 18000.f;
+ private static final boolean ENABLE_PIP_KEEP_CLEAR_ALGORITHM =
+ SystemProperties.getBoolean("persist.wm.debug.enable_pip_keep_clear_algorithm", false);
+
// Allow PIP to resize to a slightly bigger state upon touch
private boolean mEnableResize;
private final Context mContext;
@@ -426,6 +430,9 @@ public class PipTouchHandler {
if (mTouchState.isUserInteracting()) {
// Defer the update of the current movement bounds until after the user finishes
// touching the screen
+ } else if (ENABLE_PIP_KEEP_CLEAR_ALGORITHM) {
+ // Ignore moving PiP if keep clear algorithm is enabled, since IME and shelf height
+ // now are accounted for in the keep clear algorithm calculations
} else {
final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu();
final Rect toMovementBounds = new Rect();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
index ca22882187d8..1651f8993dc1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipBoundsState.java
@@ -87,6 +87,7 @@ public class TvPipBoundsState extends PipBoundsState {
public void resetTvPipState() {
mTvFixedPipOrientation = ORIENTATION_UNDETERMINED;
mTvPipGravity = DEFAULT_TV_GRAVITY;
+ mTvPipManuallyCollapsed = false;
}
/** Set the tv expanded bounds of PiP */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 3fef82352728..c52ed249c2ca 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -48,6 +48,8 @@ public enum ShellProtoLogGroup implements IProtoLogGroup {
Consts.TAG_WM_SHELL),
WM_SHELL_DESKTOP_MODE(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
Consts.TAG_WM_SHELL),
+ WM_SHELL_FLOATING_APPS(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
+ Consts.TAG_WM_SHELL),
TEST_GROUP(true, true, false, "WindowManagerShellProtoLogTest");
private final boolean mEnabled;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
index a5748f69388f..2a625524b48b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasks.java
@@ -17,6 +17,11 @@
package com.android.wm.shell.recents;
import com.android.wm.shell.common.annotations.ExternalThread;
+import com.android.wm.shell.util.GroupedRecentTaskInfo;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Interface for interacting with the recent tasks.
@@ -29,4 +34,11 @@ public interface RecentTasks {
default IRecentTasks createExternalInterface() {
return null;
}
+
+ /**
+ * Gets the set of recent tasks.
+ */
+ default void getRecentTasks(int maxNum, int flags, int userId, Executor callbackExecutor,
+ Consumer<List<GroupedRecentTaskInfo>> callback) {
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
index 27bc1a189086..02b5a35f653b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java
@@ -24,6 +24,7 @@ import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTas
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.TaskInfo;
+import android.content.ComponentName;
import android.content.Context;
import android.os.RemoteException;
import android.util.Slog;
@@ -43,7 +44,8 @@ import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.annotations.ExternalThread;
import com.android.wm.shell.common.annotations.ShellMainThread;
-import com.android.wm.shell.desktopmode.DesktopMode;
+import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
@@ -53,22 +55,26 @@ import com.android.wm.shell.util.SplitBounds;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
/**
* Manages the recent task list from the system, caching it as necessary.
*/
public class RecentTasksController implements TaskStackListenerCallback,
- RemoteCallable<RecentTasksController> {
+ RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.Listener {
private static final String TAG = RecentTasksController.class.getSimpleName();
private final Context mContext;
private final ShellCommandHandler mShellCommandHandler;
+ private final Optional<DesktopModeTaskRepository> mDesktopModeTaskRepository;
private final ShellExecutor mMainExecutor;
private final TaskStackListenerImpl mTaskStackListener;
private final RecentTasks mImpl = new RecentTasksImpl();
+ private final ActivityTaskManager mActivityTaskManager;
private IRecentTasksListener mListener;
private final boolean mIsDesktopMode;
@@ -84,15 +90,6 @@ public class RecentTasksController implements TaskStackListenerCallback,
private final Map<Integer, SplitBounds> mTaskSplitBoundsMap = new HashMap<>();
/**
- * Set of taskId's that have been launched in freeform mode.
- * This includes tasks that are currently running, visible and in freeform mode. And also
- * includes tasks that are running in the background, are no longer visible, but at some point
- * were visible to the user.
- * This is used to decide which freeform apps belong to the user's desktop.
- */
- private final HashSet<Integer> mActiveFreeformTasks = new HashSet<>();
-
- /**
* Creates {@link RecentTasksController}, returns {@code null} if the feature is not
* supported.
*/
@@ -102,24 +99,30 @@ public class RecentTasksController implements TaskStackListenerCallback,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ ActivityTaskManager activityTaskManager,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor
) {
if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) {
return null;
}
return new RecentTasksController(context, shellInit, shellCommandHandler, taskStackListener,
- mainExecutor);
+ activityTaskManager, desktopModeTaskRepository, mainExecutor);
}
RecentTasksController(Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
TaskStackListenerImpl taskStackListener,
+ ActivityTaskManager activityTaskManager,
+ Optional<DesktopModeTaskRepository> desktopModeTaskRepository,
ShellExecutor mainExecutor) {
mContext = context;
mShellCommandHandler = shellCommandHandler;
+ mActivityTaskManager = activityTaskManager;
mIsDesktopMode = mContext.getPackageManager().hasSystemFeature(FEATURE_PC);
mTaskStackListener = taskStackListener;
+ mDesktopModeTaskRepository = desktopModeTaskRepository;
mMainExecutor = mainExecutor;
shellInit.addInitCallback(this::onInit, this);
}
@@ -131,6 +134,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
private void onInit() {
mShellCommandHandler.addDumpCallback(this::dump, this);
mTaskStackListener.addListener(this);
+ mDesktopModeTaskRepository.ifPresent(it -> it.addListener(this));
}
/**
@@ -217,19 +221,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
notifyRecentTasksChanged();
}
- /**
- * Mark a task with given {@code taskId} as active in freeform
- */
- public void addActiveFreeformTask(int taskId) {
- mActiveFreeformTasks.add(taskId);
- notifyRecentTasksChanged();
- }
-
- /**
- * Remove task with given {@code taskId} from active freeform tasks
- */
- public void removeActiveFreeformTask(int taskId) {
- mActiveFreeformTasks.remove(taskId);
+ @Override
+ public void onActiveTasksChanged() {
notifyRecentTasksChanged();
}
@@ -283,15 +276,10 @@ public class RecentTasksController implements TaskStackListenerCallback,
}
@VisibleForTesting
- List<ActivityManager.RecentTaskInfo> getRawRecentTasks(int maxNum, int flags, int userId) {
- return ActivityTaskManager.getInstance().getRecentTasks(maxNum, flags, userId);
- }
-
- @VisibleForTesting
ArrayList<GroupedRecentTaskInfo> getRecentTasks(int maxNum, int flags, int userId) {
// Note: the returned task list is from the most-recent to least-recent order
- final List<ActivityManager.RecentTaskInfo> rawList = getRawRecentTasks(maxNum, flags,
- userId);
+ final List<ActivityManager.RecentTaskInfo> rawList = mActivityTaskManager.getRecentTasks(
+ maxNum, flags, userId);
// Make a mapping of task id -> task info
final SparseArray<ActivityManager.RecentTaskInfo> rawMapping = new SparseArray<>();
@@ -300,7 +288,7 @@ public class RecentTasksController implements TaskStackListenerCallback,
rawMapping.put(taskInfo.taskId, taskInfo);
}
- boolean desktopModeActive = DesktopMode.isActive(mContext);
+ boolean desktopModeActive = DesktopModeStatus.isActive(mContext);
ArrayList<ActivityManager.RecentTaskInfo> freeformTasks = new ArrayList<>();
// Pull out the pairs as we iterate back in the list
@@ -312,7 +300,8 @@ public class RecentTasksController implements TaskStackListenerCallback,
continue;
}
- if (desktopModeActive && mActiveFreeformTasks.contains(taskInfo.taskId)) {
+ if (desktopModeActive && mDesktopModeTaskRepository.isPresent()
+ && mDesktopModeTaskRepository.get().isActiveTask(taskInfo.taskId)) {
// Freeform tasks will be added as a separate entry
freeformTasks.add(taskInfo);
continue;
@@ -332,7 +321,6 @@ public class RecentTasksController implements TaskStackListenerCallback,
// Add a special entry for freeform tasks
if (!freeformTasks.isEmpty()) {
- // First task is added separately
recentTasks.add(0, GroupedRecentTaskInfo.forFreeformTasks(
freeformTasks.toArray(new ActivityManager.RecentTaskInfo[0])));
}
@@ -340,6 +328,29 @@ public class RecentTasksController implements TaskStackListenerCallback,
return recentTasks;
}
+ /**
+ * Find the background task that match the given component.
+ */
+ @Nullable
+ public ActivityManager.RecentTaskInfo findTaskInBackground(ComponentName componentName) {
+ if (componentName == null) {
+ return null;
+ }
+ List<ActivityManager.RecentTaskInfo> tasks = mActivityTaskManager.getRecentTasks(
+ Integer.MAX_VALUE, ActivityManager.RECENT_IGNORE_UNAVAILABLE,
+ ActivityManager.getCurrentUser());
+ for (int i = 0; i < tasks.size(); i++) {
+ final ActivityManager.RecentTaskInfo task = tasks.get(i);
+ if (task.isVisible) {
+ continue;
+ }
+ if (componentName.equals(task.baseIntent.getComponent())) {
+ return task;
+ }
+ }
+ return null;
+ }
+
public void dump(@NonNull PrintWriter pw, String prefix) {
final String innerPrefix = prefix + " ";
pw.println(prefix + TAG);
@@ -365,6 +376,16 @@ public class RecentTasksController implements TaskStackListenerCallback,
mIRecentTasks = new IRecentTasksImpl(RecentTasksController.this);
return mIRecentTasks;
}
+
+ @Override
+ public void getRecentTasks(int maxNum, int flags, int userId, Executor executor,
+ Consumer<List<GroupedRecentTaskInfo>> callback) {
+ mMainExecutor.execute(() -> {
+ List<GroupedRecentTaskInfo> tasks =
+ RecentTasksController.this.getRecentTasks(maxNum, flags, userId);
+ executor.execute(() -> callback.accept(tasks));
+ });
+ }
}
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 3714fe713288..ecdafa9a63f4 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
@@ -21,6 +21,7 @@ import android.content.Intent;
import android.content.pm.ShortcutInfo;
import android.os.Bundle;
import android.os.UserHandle;
+import com.android.internal.logging.InstanceId;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.window.RemoteTransition;
@@ -67,34 +68,35 @@ interface ISplitScreen {
* Starts a shortcut in a stage.
*/
oneway void startShortcut(String packageName, String shortcutId, int position,
- in Bundle options, in UserHandle user) = 8;
+ in Bundle options, in UserHandle user, in InstanceId instanceId) = 8;
/**
* Starts an activity in a stage.
*/
oneway void startIntent(in PendingIntent intent, in Intent fillInIntent, int position,
- in Bundle options) = 9;
+ in Bundle options, in InstanceId instanceId) = 9;
/**
* Starts tasks simultaneously in one transition.
*/
oneway void startTasks(int mainTaskId, in Bundle mainOptions, int sideTaskId,
in Bundle sideOptions, int sidePosition, float splitRatio,
- in RemoteTransition remoteTransition) = 10;
+ in RemoteTransition remoteTransition, in InstanceId instanceId) = 10;
/**
* Version of startTasks using legacy transition system.
*/
oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
int sideTaskId, in Bundle sideOptions, int sidePosition,
- float splitRatio, in RemoteAnimationAdapter adapter) = 11;
+ float splitRatio, in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 11;
/**
* Starts a pair of intent and task using legacy transition system.
*/
oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
in Intent fillInIntent, int taskId, in Bundle mainOptions,in Bundle sideOptions,
- int sidePosition, float splitRatio, in RemoteAnimationAdapter adapter) = 12;
+ int sidePosition, float splitRatio, in RemoteAnimationAdapter adapter,
+ in InstanceId instanceId) = 12;
/**
* Blocking call that notifies and gets additional split-screen targets when entering
@@ -115,5 +117,5 @@ interface ISplitScreen {
*/
oneway void startShortcutAndTaskWithLegacyTransition(in ShortcutInfo shortcutInfo, int taskId,
in Bundle mainOptions, in Bundle sideOptions, int sidePosition, float splitRatio,
- in RemoteAnimationAdapter adapter) = 15;
+ in RemoteAnimationAdapter adapter, in InstanceId instanceId) = 15;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
index b0080b24c609..e7ec15e70c11 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
@@ -45,11 +45,6 @@ class MainStage extends StageTaskListener {
iconProvider);
}
- @Override
- void dismiss(WindowContainerTransaction wct, boolean toTop) {
- deactivate(wct, toTop);
- }
-
boolean isActive() {
return mIsActive;
}
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 86efbe0af79c..8639b36faf4c 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
@@ -42,11 +42,6 @@ class SideStage extends StageTaskListener {
iconProvider);
}
- @Override
- void dismiss(WindowContainerTransaction wct, boolean toTop) {
- removeAllTasks(wct, toTop);
- }
-
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
if (mChildrenTaskInfo.size() == 0) return false;
wct.reparentTasks(
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 9206afb00e27..07a6895e2720 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
@@ -133,6 +133,20 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
+ public static final int ENTER_REASON_UNKNOWN = 0;
+ public static final int ENTER_REASON_MULTI_INSTANCE = 1;
+ public static final int ENTER_REASON_DRAG = 2;
+ public static final int ENTER_REASON_LAUNCHER = 3;
+ /** Acts as a mapping to the actual EnterReasons as defined in the logging proto */
+ @IntDef(value = {
+ ENTER_REASON_MULTI_INSTANCE,
+ ENTER_REASON_DRAG,
+ ENTER_REASON_LAUNCHER,
+ ENTER_REASON_UNKNOWN
+ })
+ public @interface SplitEnterReason {
+ }
+
private final ShellCommandHandler mShellCommandHandler;
private final ShellController mShellController;
private final ShellTaskOrganizer mTaskOrganizer;
@@ -154,7 +168,8 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
private StageCoordinator mStageCoordinator;
// Only used for the legacy recents animation from splitscreen to allow the tasks to be animated
// outside the bounds of the roots by being reparented into a higher level fullscreen container
- private SurfaceControl mSplitTasksContainerLayer;
+ private SurfaceControl mGoingToRecentsTasksLayer;
+ private SurfaceControl mStartingSplitTasksLayer;
public SplitScreenController(Context context,
ShellInit shellInit,
@@ -370,6 +385,9 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ mStageCoordinator.prepareEvictInvisibleChildTasks(evictWct);
+ mSyncQueue.queue(evictWct);
}
};
options = mStageCoordinator.resolveStartStage(STAGE_TYPE_UNDEFINED, position, options,
@@ -387,6 +405,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
}
+ /**
+ * See {@link #startShortcut(String, String, int, Bundle, UserHandle)}
+ * @param instanceId to be used by {@link SplitscreenEventLogger}
+ */
+ public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
+ @Nullable Bundle options, UserHandle user, @NonNull InstanceId instanceId) {
+ mStageCoordinator.getLogger().enterRequested(instanceId, ENTER_REASON_LAUNCHER);
+ startShortcut(packageName, shortcutId, position, options, user);
+ }
+
+ @Override
public void startShortcut(String packageName, String shortcutId, @SplitPosition int position,
@Nullable Bundle options, UserHandle user) {
IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
@@ -415,7 +444,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
0 /* duration */, 0 /* statusBarTransitionDelay */);
ActivityOptions activityOptions = ActivityOptions.fromBundle(options);
activityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
-
try {
LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class);
launcherApps.startShortcut(packageName, shortcutId, null /* sourceBounds */,
@@ -425,6 +453,17 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
}
+ /**
+ * See {@link #startIntent(PendingIntent, Intent, int, Bundle)}
+ * @param instanceId to be used by {@link SplitscreenEventLogger}
+ */
+ public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
+ @SplitPosition int position, @Nullable Bundle options, @NonNull InstanceId instanceId) {
+ mStageCoordinator.getLogger().enterRequested(instanceId, ENTER_REASON_LAUNCHER);
+ startIntent(intent, fillInIntent, position, options);
+ }
+
+ @Override
public void startIntent(PendingIntent intent, @Nullable Intent fillInIntent,
@SplitPosition int position, @Nullable Bundle options) {
if (fillInIntent == null) {
@@ -436,8 +475,16 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
fillInIntent.addFlags(FLAG_ACTIVITY_NO_USER_ACTION);
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of the
- // split.
+ // split and there is no reusable background task.
if (shouldAddMultipleTaskFlag(intent.getIntent(), position)) {
+ final ActivityManager.RecentTaskInfo taskInfo = mRecentTasksOptional.isPresent()
+ ? mRecentTasksOptional.get().findTaskInBackground(
+ intent.getIntent().getComponent())
+ : null;
+ if (taskInfo != null) {
+ startTask(taskInfo.taskId, position, options);
+ return;
+ }
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
}
@@ -446,7 +493,6 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
mStageCoordinator.startIntentLegacy(intent, fillInIntent, position, options);
return;
}
-
mStageCoordinator.startIntent(intent, fillInIntent, position, options);
}
@@ -489,19 +535,53 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
RemoteAnimationTarget[] onGoingToRecentsLegacy(RemoteAnimationTarget[] apps) {
+ if (ENABLE_SHELL_TRANSITIONS) return null;
+
if (isSplitScreenVisible()) {
// Evict child tasks except the top visible one under split root to ensure it could be
// launched as full screen when switching to it on recents.
final WindowContainerTransaction wct = new WindowContainerTransaction();
mStageCoordinator.prepareEvictInvisibleChildTasks(wct);
mSyncQueue.queue(wct);
+ } else {
+ return null;
+ }
+
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
+ if (mGoingToRecentsTasksLayer != null) {
+ t.remove(mGoingToRecentsTasksLayer);
}
- return reparentSplitTasksForAnimation(apps, false /* enterSplitScreen */);
+ mGoingToRecentsTasksLayer = reparentSplitTasksForAnimation(apps, t,
+ "SplitScreenController#onGoingToRecentsLegacy" /* callsite */);
+ t.apply();
+ mTransactionPool.release(t);
+
+ return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
}
RemoteAnimationTarget[] onStartingSplitLegacy(RemoteAnimationTarget[] apps) {
+ if (ENABLE_SHELL_TRANSITIONS) return null;
+
+ int openingApps = 0;
+ for (int i = 0; i < apps.length; ++i) {
+ if (apps[i].mode == MODE_OPENING) openingApps++;
+ }
+ if (openingApps < 2) {
+ // Not having enough apps to enter split screen
+ return null;
+ }
+
+ SurfaceControl.Transaction t = mTransactionPool.acquire();
+ if (mStartingSplitTasksLayer != null) {
+ t.remove(mStartingSplitTasksLayer);
+ }
+ mStartingSplitTasksLayer = reparentSplitTasksForAnimation(apps, t,
+ "SplitScreenController#onStartingSplitLegacy" /* callsite */);
+ t.apply();
+ mTransactionPool.release(t);
+
try {
- return reparentSplitTasksForAnimation(apps, true /* enterSplitScreen */);
+ return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
} finally {
for (RemoteAnimationTarget appTarget : apps) {
if (appTarget.leash != null) {
@@ -511,45 +591,23 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
}
}
- private RemoteAnimationTarget[] reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
- boolean enterSplitScreen) {
- if (ENABLE_SHELL_TRANSITIONS) return null;
-
- if (enterSplitScreen) {
- int openingApps = 0;
- for (int i = 0; i < apps.length; ++i) {
- if (apps[i].mode == MODE_OPENING) openingApps++;
- }
- if (openingApps < 2) {
- // Not having enough apps to enter split screen
- return null;
- }
- } else if (!isSplitScreenVisible()) {
- return null;
- }
-
- final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
- if (mSplitTasksContainerLayer != null) {
- // Remove the previous layer before recreating
- transaction.remove(mSplitTasksContainerLayer);
- }
+ private SurfaceControl reparentSplitTasksForAnimation(RemoteAnimationTarget[] apps,
+ SurfaceControl.Transaction t, String callsite) {
final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
.setContainerLayer()
.setName("RecentsAnimationSplitTasks")
.setHidden(false)
- .setCallsite("SplitScreenController#onGoingtoRecentsLegacy");
+ .setCallsite(callsite);
mRootTDAOrganizer.attachToDisplayArea(DEFAULT_DISPLAY, builder);
- mSplitTasksContainerLayer = builder.build();
+ final SurfaceControl splitTasksLayer = builder.build();
for (int i = 0; i < apps.length; ++i) {
final RemoteAnimationTarget appTarget = apps[i];
- transaction.reparent(appTarget.leash, mSplitTasksContainerLayer);
- transaction.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
+ t.reparent(appTarget.leash, splitTasksLayer);
+ t.setPosition(appTarget.leash, appTarget.screenSpaceBounds.left,
appTarget.screenSpaceBounds.top);
}
- transaction.apply();
- mTransactionPool.release(transaction);
- return new RemoteAnimationTarget[]{mStageCoordinator.getDividerBarLegacyTarget()};
+ return splitTasksLayer;
}
/**
* Sets drag info to be logged when splitscreen is entered.
@@ -772,60 +830,64 @@ public class SplitScreenController implements DragAndDropPolicy.Starter,
@Override
public void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- float splitRatio, RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasksWithLegacyTransition(
mainTaskId, mainOptions, sideTaskId, sideOptions, sidePosition,
- splitRatio, adapter));
+ splitRatio, adapter, instanceId));
}
@Override
public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
Intent fillInIntent, int taskId, Bundle mainOptions, Bundle sideOptions,
- int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+ int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController,
"startIntentAndTaskWithLegacyTransition", (controller) ->
controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition(
pendingIntent, fillInIntent, taskId, mainOptions, sideOptions,
- sidePosition, splitRatio, adapter));
+ sidePosition, splitRatio, adapter, instanceId));
}
@Override
public void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
- @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+ @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController,
"startShortcutAndTaskWithLegacyTransition", (controller) ->
controller.mStageCoordinator.startShortcutAndTaskWithLegacyTransition(
shortcutInfo, taskId, mainOptions, sideOptions, sidePosition,
- splitRatio, adapter));
+ splitRatio, adapter, instanceId));
}
@Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition) {
+ @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startTasks",
(controller) -> controller.mStageCoordinator.startTasks(mainTaskId, mainOptions,
- sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition));
+ sideTaskId, sideOptions, sidePosition, splitRatio, remoteTransition,
+ instanceId));
}
@Override
public void startShortcut(String packageName, String shortcutId, int position,
- @Nullable Bundle options, UserHandle user) {
+ @Nullable Bundle options, UserHandle user, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startShortcut",
(controller) -> {
- controller.startShortcut(packageName, shortcutId, position, options, user);
+ controller.startShortcut(packageName, shortcutId, position, options, user,
+ instanceId);
});
}
@Override
public void startIntent(PendingIntent intent, Intent fillInIntent, int position,
- @Nullable Bundle options) {
+ @Nullable Bundle options, InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startIntent",
(controller) -> {
- controller.startIntent(intent, fillInIntent, position, options);
+ controller.startIntent(intent, fillInIntent, position, options, instanceId);
});
}
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
index 3e7a1004ed7a..2dc4a0441b06 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -16,7 +16,9 @@
package com.android.wm.shell.splitscreen;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__LAUNCHER;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__MULTI_INSTANCE;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__ENTER_REASON__UNKNOWN_ENTER;
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;
@@ -28,6 +30,10 @@ import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED_
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__UNKNOWN_EXIT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_DRAG;
+import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_LAUNCHER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_MULTI_INSTANCE;
+import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_UNKNOWN;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
@@ -38,6 +44,7 @@ import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_SCREEN_LOCKED_SHOW_ON_TOP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_UNKNOWN;
+import android.annotation.Nullable;
import android.util.Slog;
import com.android.internal.logging.InstanceId;
@@ -59,7 +66,7 @@ public class SplitscreenEventLogger {
// Drag info
private @SplitPosition int mDragEnterPosition;
- private InstanceId mDragEnterSessionId;
+ private @Nullable InstanceId mEnterSessionId;
// For deduping async events
private int mLastMainStagePosition = -1;
@@ -67,6 +74,7 @@ public class SplitscreenEventLogger {
private int mLastSideStagePosition = -1;
private int mLastSideStageUid = -1;
private float mLastSplitRatio = -1f;
+ private @SplitScreenController.SplitEnterReason int mEnterReason = ENTER_REASON_UNKNOWN;
public SplitscreenEventLogger() {
mIdSequence = new InstanceIdSequence(Integer.MAX_VALUE);
@@ -79,12 +87,35 @@ public class SplitscreenEventLogger {
return mLoggerSessionId != null;
}
+ public boolean isEnterRequestedByDrag() {
+ return mEnterReason == ENTER_REASON_DRAG;
+ }
+
/**
* May be called before logEnter() to indicate that the session was started from a drag.
*/
- public void enterRequestedByDrag(@SplitPosition int position, InstanceId dragSessionId) {
+ public void enterRequestedByDrag(@SplitPosition int position, InstanceId enterSessionId) {
mDragEnterPosition = position;
- mDragEnterSessionId = dragSessionId;
+ enterRequested(enterSessionId, ENTER_REASON_DRAG);
+ }
+
+ /**
+ * May be called before logEnter() to indicate that the session was started from launcher.
+ * This specifically is for all the scenarios where split started without a drag interaction
+ */
+ public void enterRequested(@Nullable InstanceId enterSessionId,
+ @SplitScreenController.SplitEnterReason int enterReason) {
+ mEnterSessionId = enterSessionId;
+ mEnterReason = enterReason;
+ }
+
+ /**
+ * @return if an enterSessionId has been set via either
+ * {@link #enterRequested(InstanceId, int)} or
+ * {@link #enterRequestedByDrag(int, InstanceId)}
+ */
+ public boolean hasValidEnterSessionId() {
+ return mEnterSessionId != null;
}
/**
@@ -95,9 +126,7 @@ public class SplitscreenEventLogger {
@SplitPosition int sideStagePosition, int sideStageUid,
boolean isLandscape) {
mLoggerSessionId = mIdSequence.newInstanceId();
- int enterReason = mDragEnterPosition != SPLIT_POSITION_UNDEFINED
- ? getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape)
- : SPLITSCREEN_UICHANGED__ENTER_REASON__OVERVIEW;
+ int enterReason = getLoggerEnterReason(isLandscape);
updateMainStageState(getMainStagePositionFromSplitPosition(mainStagePosition, isLandscape),
mainStageUid);
updateSideStageState(getSideStagePositionFromSplitPosition(sideStagePosition, isLandscape),
@@ -112,10 +141,24 @@ public class SplitscreenEventLogger {
mLastMainStageUid,
mLastSideStagePosition,
mLastSideStageUid,
- mDragEnterSessionId != null ? mDragEnterSessionId.getId() : 0,
+ mEnterSessionId != null ? mEnterSessionId.getId() : 0,
mLoggerSessionId.getId());
}
+ private int getLoggerEnterReason(boolean isLandscape) {
+ switch (mEnterReason) {
+ case ENTER_REASON_MULTI_INSTANCE:
+ return SPLITSCREEN_UICHANGED__ENTER_REASON__MULTI_INSTANCE;
+ case ENTER_REASON_LAUNCHER:
+ return SPLITSCREEN_UICHANGED__ENTER_REASON__LAUNCHER;
+ case ENTER_REASON_DRAG:
+ return getDragEnterReasonFromSplitPosition(mDragEnterPosition, isLandscape);
+ case ENTER_REASON_UNKNOWN:
+ default:
+ return SPLITSCREEN_UICHANGED__ENTER_REASON__UNKNOWN_ENTER;
+ }
+ }
+
/**
* Returns the framework logging constant given a splitscreen exit reason.
*/
@@ -176,11 +219,12 @@ public class SplitscreenEventLogger {
// Reset states
mLoggerSessionId = null;
mDragEnterPosition = SPLIT_POSITION_UNDEFINED;
- mDragEnterSessionId = null;
+ mEnterSessionId = null;
mLastMainStagePosition = -1;
mLastMainStageUid = -1;
mLastSideStagePosition = -1;
mLastSideStageUid = -1;
+ mEnterReason = ENTER_REASON_UNKNOWN;
}
/**
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 8ed16ce4fbfc..c17f8226c925 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
@@ -46,6 +46,8 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
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 static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_LAUNCHER;
+import static com.android.wm.shell.splitscreen.SplitScreenController.ENTER_REASON_MULTI_INSTANCE;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_APP_FINISHED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
@@ -69,6 +71,7 @@ import android.annotation.CallSuper;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.ActivityOptions;
import android.app.IActivityTaskManager;
import android.app.PendingIntent;
import android.app.WindowConfiguration;
@@ -87,6 +90,7 @@ import android.util.Log;
import android.util.Slog;
import android.view.Choreographer;
import android.view.IRemoteAnimationFinishedCallback;
+import android.view.IRemoteAnimationRunner;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
@@ -121,6 +125,7 @@ import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.splitscreen.SplitScreen.StageType;
import com.android.wm.shell.splitscreen.SplitScreenController.ExitReason;
+import com.android.wm.shell.transition.DefaultMixedHandler;
import com.android.wm.shell.transition.LegacyTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.util.SplitBounds;
@@ -201,6 +206,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
@StageType
private int mTopStageAfterFoldDismiss = STAGE_TYPE_UNDEFINED;
+ private DefaultMixedHandler mMixedHandler;
+
private final SplitWindowManager.ParentContainerCallbacks mParentContainerCallbacks =
new SplitWindowManager.ParentContainerCallbacks() {
@Override
@@ -324,6 +331,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
transitions.addHandler(this);
}
+ public void setMixedHandler(DefaultMixedHandler mixedHandler) {
+ mMixedHandler = mixedHandler;
+ }
+
@VisibleForTesting
SplitScreenTransitions getSplitTransitions() {
return mSplitTransitions;
@@ -405,6 +416,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return result;
}
+ SplitscreenEventLogger getLogger() {
+ return mLogger;
+ }
+
/** Launches an activity into split. */
void startIntent(PendingIntent intent, Intent fillInIntent, @SplitPosition int position,
@Nullable Bundle options) {
@@ -490,6 +505,12 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
final WindowContainerTransaction wct = new WindowContainerTransaction();
options = resolveStartStage(STAGE_TYPE_UNDEFINED, position, options, wct);
+ // If split still not active, apply windows bounds first to avoid surface reset to
+ // wrong pos by SurfaceAnimator from wms.
+ if (!mMainStage.isActive() && mLogger.isEnterRequestedByDrag()) {
+ updateWindowBounds(mSplitLayout, wct);
+ }
+
wct.sendPendingIntent(intent, fillInIntent, options);
mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
}
@@ -497,7 +518,7 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
/** Starts 2 tasks in one transition. */
void startTasks(int mainTaskId, @Nullable Bundle mainOptions, int sideTaskId,
@Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
- @Nullable RemoteTransition remoteTransition) {
+ @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
mainOptions = mainOptions != null ? mainOptions : new Bundle();
sideOptions = sideOptions != null ? sideOptions : new Bundle();
@@ -526,46 +547,57 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mSplitTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, wct, remoteTransition, this, null);
+ setEnterInstanceId(instanceId);
}
/** Starts 2 tasks in one legacy transition. */
void startTasksWithLegacyTransition(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions, @SplitPosition int sidePosition,
- float splitRatio, RemoteAnimationAdapter adapter) {
+ float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideOptions == null) sideOptions = new Bundle();
addActivityOptions(sideOptions, mSideStage);
wct.startTask(sideTaskId, sideOptions);
- startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter);
+ startWithLegacyTransition(wct, mainTaskId, mainOptions, sidePosition, splitRatio, adapter,
+ instanceId);
}
/** Start an intent and a task ordered by {@code intentFirst}. */
void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
- @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+ @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideOptions == null) sideOptions = new Bundle();
addActivityOptions(sideOptions, mSideStage);
wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
- startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
+ startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter,
+ instanceId);
}
void startShortcutAndTaskWithLegacyTransition(ShortcutInfo shortcutInfo,
int taskId, @Nullable Bundle mainOptions, @Nullable Bundle sideOptions,
- @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter) {
+ @SplitPosition int sidePosition, float splitRatio, RemoteAnimationAdapter adapter,
+ InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
if (sideOptions == null) sideOptions = new Bundle();
addActivityOptions(sideOptions, mSideStage);
wct.startShortcut(mContext.getPackageName(), shortcutInfo, sideOptions);
- startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter);
+ startWithLegacyTransition(wct, taskId, mainOptions, sidePosition, splitRatio, adapter,
+ instanceId);
}
- private void startWithLegacyTransition(WindowContainerTransaction sideWct, int mainTaskId,
+ /**
+ * @param instanceId if {@code null}, will not log. Otherwise it will be used in
+ * {@link SplitscreenEventLogger#logEnter(float, int, int, int, int, boolean)}
+ */
+ private void startWithLegacyTransition(WindowContainerTransaction wct, int mainTaskId,
@Nullable Bundle mainOptions, @SplitPosition int sidePosition, float splitRatio,
- RemoteAnimationAdapter adapter) {
+ RemoteAnimationAdapter adapter, InstanceId instanceId) {
// Init divider first to make divider leash for remote animation target.
mSplitLayout.init();
mSplitLayout.setDivideRatio(splitRatio);
@@ -574,59 +606,56 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
mShouldUpdateRecents = false;
mIsDividerRemoteAnimating = true;
- LegacyTransitions.ILegacyTransition transition = new LegacyTransitions.ILegacyTransition() {
- @Override
- public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers, RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback,
- SurfaceControl.Transaction t) {
- if (apps == null || apps.length == 0) {
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
- setDividerVisibility(true, t);
- t.apply();
- onRemoteAnimationFinished(apps);
- try {
- adapter.getRunner().onAnimationCancelled(mKeyguardShowing);
- } catch (RemoteException e) {
- Slog.e(TAG, "Error starting remote animation", e);
- }
- return;
- }
-
- // Wrap the divider bar into non-apps target to animate together.
- nonApps = ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
- getDividerBarLegacyTarget());
-
- updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
- setDividerVisibility(true, t);
- for (int i = 0; i < apps.length; ++i) {
- if (apps[i].mode == MODE_OPENING) {
- t.show(apps[i].leash);
- // Reset the surface position of the opening app to prevent double-offset.
- t.setPosition(apps[i].leash, 0, 0);
- }
- }
- t.apply();
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
+ prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
+ IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ final IRemoteAnimationFinishedCallback finishedCallback) {
IRemoteAnimationFinishedCallback wrapCallback =
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
- onRemoteAnimationFinished(apps);
+ onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct);
finishedCallback.onAnimationFinished();
}
};
Transitions.setRunningRemoteTransitionDelegate(adapter.getCallingApplication());
try {
- adapter.getRunner().onAnimationStart(
- transit, apps, wallpapers, nonApps, wrapCallback);
+ adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
+ ArrayUtils.appendElement(RemoteAnimationTarget.class, nonApps,
+ getDividerBarLegacyTarget()), wrapCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled(boolean isKeyguardOccluded) {
+ onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct);
+ try {
+ adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
} 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));
+ mainOptions = mainActivityOptions.toBundle();
+ }
- final WindowContainerTransaction wct = new WindowContainerTransaction();
setSideStagePosition(sidePosition, wct);
if (!mMainStage.isActive()) {
mMainStage.activate(wct, false /* reparent */);
@@ -634,34 +663,40 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
if (mainOptions == null) mainOptions = new Bundle();
addActivityOptions(mainOptions, mMainStage);
- wct.startTask(mainTaskId, mainOptions);
- wct.merge(sideWct, true);
-
updateWindowBounds(mSplitLayout, wct);
+ wct.startTask(mainTaskId, mainOptions);
wct.reorder(mRootTaskInfo.token, true);
wct.setForceTranslucent(mRootTaskInfo.token, false);
- mSyncQueue.queue(transition, WindowManager.TRANSIT_OPEN, wct);
+ mSyncQueue.queue(wct);
+ mSyncQueue.runInSync(t -> {
+ setDividerVisibility(true, t);
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+ });
+
+ setEnterInstanceId(instanceId);
+ }
+
+ private void setEnterInstanceId(InstanceId instanceId) {
+ if (instanceId != null) {
+ mLogger.enterRequested(instanceId, ENTER_REASON_LAUNCHER);
+ }
}
- private void onRemoteAnimationFinished(RemoteAnimationTarget[] apps) {
+ private void onRemoteAnimationFinishedOrCancelled(boolean cancel,
+ WindowContainerTransaction evictWct) {
mIsDividerRemoteAnimating = false;
mShouldUpdateRecents = true;
- if (apps == null || apps.length == 0) return;
-
- // If any stage has no child after finished animation, that side of the split will display
- // nothing. This might happen if starting the same app on the both sides while not
- // supporting multi-instance. Exit the split screen and expand that app to full screen.
- if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
- mMainExecutor.execute(() -> exitSplitScreen(mMainStage.getChildCount() == 0
- ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
- return;
+ // If any stage has no child after animation finished, it means that split will display
+ // nothing, such status will happen if task and intent is same app but not support
+ // multi-instance, we should exit split and expand that app as full screen.
+ if (!cancel && (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0)) {
+ mMainExecutor.execute(() ->
+ exitSplitScreen(mMainStage.getChildCount() == 0
+ ? mSideStage : mMainStage, EXIT_REASON_UNKNOWN));
+ } else {
+ mSyncQueue.queue(evictWct);
}
-
- final WindowContainerTransaction evictWct = new WindowContainerTransaction();
- prepareEvictNonOpeningChildTasks(SPLIT_POSITION_TOP_OR_LEFT, apps, evictWct);
- prepareEvictNonOpeningChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, apps, evictWct);
- mSyncQueue.queue(evictWct);
}
/**
@@ -907,13 +942,10 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Expand to top side split as full screen for fading out decor animation and dismiss
// another side split(Moving its children to bottom).
mIsExiting = true;
- final StageTaskListener tempFullStage = childrenToTop;
- final StageTaskListener dismissStage = mMainStage == childrenToTop
- ? mSideStage : mMainStage;
- tempFullStage.resetBounds(wct);
- wct.setSmallestScreenWidthDp(tempFullStage.mRootTaskInfo.token,
+ childrenToTop.resetBounds(wct);
+ wct.reorder(childrenToTop.mRootTaskInfo.token, true);
+ wct.setSmallestScreenWidthDp(childrenToTop.mRootTaskInfo.token,
SMALLEST_SCREEN_WIDTH_DP_UNDEFINED);
- dismissStage.dismiss(wct, false /* toTop */);
}
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
@@ -930,7 +962,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
childrenToTop.fadeOutDecor(() -> {
WindowContainerTransaction finishedWCT = new WindowContainerTransaction();
mIsExiting = false;
- childrenToTop.dismiss(finishedWCT, true /* toTop */);
+ mMainStage.deactivate(finishedWCT, childrenToTop == mMainStage /* toTop */);
+ mSideStage.removeAllTasks(finishedWCT, childrenToTop == mSideStage /* toTop */);
finishedWCT.reorder(mRootTaskInfo.token, false /* toTop */);
finishedWCT.setForceTranslucent(mRootTaskInfo.token, true);
finishedWCT.setBounds(mSideStage.mRootTaskInfo.token, mTempRect1);
@@ -1084,7 +1117,8 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
private void addActivityOptions(Bundle opts, StageTaskListener stage) {
opts.putParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, stage.mRootTaskInfo.token);
- // Put BAL flags to avoid activity start aborted.
+ // Put BAL flags to avoid activity start aborted. Otherwise, flows like shortcut to split
+ // will be canceled.
opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED, true);
opts.putBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION, true);
}
@@ -1433,18 +1467,27 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
}
}
} else if (isSideStage && hasChildren && !mMainStage.isActive()) {
- // TODO (b/238697912) : Add the validation to prevent entering non-recovered status
- onSplitScreenEnter();
final WindowContainerTransaction wct = new WindowContainerTransaction();
mSplitLayout.init();
- mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
- mMainStage.activate(wct, true /* includingTopTask */);
- updateWindowBounds(mSplitLayout, wct);
- wct.reorder(mRootTaskInfo.token, true);
- wct.setForceTranslucent(mRootTaskInfo.token, false);
+ if (mLogger.isEnterRequestedByDrag()) {
+ prepareEnterSplitScreen(wct);
+ } else {
+ // TODO (b/238697912) : Add the validation to prevent entering non-recovered status
+ onSplitScreenEnter();
+ mSplitLayout.setDividerAtBorder(mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
+ mMainStage.activate(wct, true /* includingTopTask */);
+ updateWindowBounds(mSplitLayout, wct);
+ wct.reorder(mRootTaskInfo.token, true);
+ wct.setForceTranslucent(mRootTaskInfo.token, false);
+ }
+
mSyncQueue.queue(wct);
mSyncQueue.runInSync(t -> {
- mSplitLayout.flingDividerToCenter();
+ if (mLogger.isEnterRequestedByDrag()) {
+ updateSurfaceBounds(mSplitLayout, t, false /* applyResizingOffset */);
+ } else {
+ mSplitLayout.flingDividerToCenter();
+ }
});
}
if (mMainStageListener.mHasChildren && mSideStageListener.mHasChildren) {
@@ -1452,6 +1495,9 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
updateRecentTasksSplitPair();
if (!mLogger.hasStartedSession()) {
+ if (!mLogger.hasValidEnterSessionId()) {
+ mLogger.enterRequested(null /*enterSessionId*/, ENTER_REASON_MULTI_INSTANCE);
+ }
mLogger.logEnter(mSplitLayout.getDividerPositionAsFraction(),
getMainStagePosition(), mMainStage.getTopChildTaskUid(),
getSideStagePosition(), mSideStage.getTopChildTaskUid(),
@@ -1859,8 +1905,25 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
// Use normal animations.
return false;
+ } else if (mMixedHandler != null && hasDisplayChange(info)) {
+ // A display-change has been un-expectedly inserted into the transition. Redirect
+ // handling to the mixed-handler to deal with splitting it up.
+ if (mMixedHandler.animatePendingSplitWithDisplayChange(transition, info,
+ startTransaction, finishTransaction, finishCallback)) {
+ return true;
+ }
}
+ return startPendingAnimation(transition, info, startTransaction, finishTransaction,
+ finishCallback);
+ }
+
+ /** Starts the pending transition animation. */
+ public boolean startPendingAnimation(@NonNull IBinder transition,
+ @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startTransaction,
+ @NonNull SurfaceControl.Transaction finishTransaction,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
boolean shouldAnimate = true;
if (mSplitTransitions.isPendingEnter(transition)) {
shouldAnimate = startPendingEnterAnimation(
@@ -1879,6 +1942,15 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
return true;
}
+ private boolean hasDisplayChange(TransitionInfo info) {
+ boolean has = false;
+ for (int iC = 0; iC < info.getChanges().size() && !has; ++iC) {
+ final TransitionInfo.Change change = info.getChanges().get(iC);
+ has = change.getMode() == TRANSIT_CHANGE && (change.getFlags() & FLAG_IS_DISPLAY) != 0;
+ }
+ return has;
+ }
+
/** Called to clean-up state and do house-keeping after the animation is done. */
public void onTransitionAnimationComplete() {
// If still playing, let it finish.
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 1af9415fca3a..6b90eabe3bd2 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
@@ -106,11 +106,6 @@ class StageTaskListener implements ShellTaskOrganizer.TaskListener {
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
}
- /**
- * General function for dismiss this stage.
- */
- void dismiss(WindowContainerTransaction wct, boolean toTop) {}
-
int getChildCount() {
return mChildrenTaskInfo.size();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index bcf4fbda0e0c..3cba92956f95 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.transition;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -57,6 +58,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
private static class MixedTransition {
static final int TYPE_ENTER_PIP_FROM_SPLIT = 1;
+ /** Both the display and split-state (enter/exit) is changing */
+ static final int TYPE_DISPLAY_AND_SPLIT_CHANGE = 2;
+
/** The default animation for this mixed transition. */
static final int ANIM_TYPE_DEFAULT = 0;
@@ -69,6 +73,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
Transitions.TransitionFinishCallback mFinishCallback = null;
Transitions.TransitionHandler mLeftoversHandler = null;
+ WindowContainerTransaction mFinishWCT = null;
/**
* Mixed transitions are made up of multiple "parts". This keeps track of how many
@@ -95,6 +100,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
mPipHandler = pipTouchHandlerOptional.get().getTransitionHandler();
mSplitHandler = splitScreenControllerOptional.get().getTransitionHandler();
mPlayer.addHandler(this);
+ if (mSplitHandler != null) {
+ mSplitHandler.setMixedHandler(this);
+ }
}, this);
}
}
@@ -122,10 +130,12 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
}
private TransitionInfo subCopy(@NonNull TransitionInfo info,
- @WindowManager.TransitionType int newType) {
- final TransitionInfo out = new TransitionInfo(newType, info.getFlags());
- for (int i = 0; i < info.getChanges().size(); ++i) {
- out.getChanges().add(info.getChanges().get(i));
+ @WindowManager.TransitionType int newType, boolean withChanges) {
+ final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
+ if (withChanges) {
+ for (int i = 0; i < info.getChanges().size(); ++i) {
+ out.getChanges().add(info.getChanges().get(i));
+ }
}
out.setRootLeash(info.getRootLeash(), info.getRootOffset().x, info.getRootOffset().y);
out.setAnimationOptions(info.getAnimationOptions());
@@ -157,6 +167,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) {
return animateEnterPipFromSplit(mixed, info, startTransaction, finishTransaction,
finishCallback);
+ } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
+ return false;
} else {
mActiveTransitions.remove(mixed);
throw new IllegalStateException("Starting mixed animation without a known mixed type? "
@@ -173,7 +185,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
+ "entering PIP while Split-Screen is active.");
TransitionInfo.Change pipChange = null;
TransitionInfo.Change wallpaper = null;
- final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK);
+ final TransitionInfo everythingElse = subCopy(info, TRANSIT_TO_BACK, true /* changes */);
boolean homeIsOpening = false;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
TransitionInfo.Change change = info.getChanges().get(i);
@@ -254,6 +266,87 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
return true;
}
+ private void unlinkMissingParents(TransitionInfo from) {
+ for (int i = 0; i < from.getChanges().size(); ++i) {
+ final TransitionInfo.Change chg = from.getChanges().get(i);
+ if (chg.getParent() == null) continue;
+ if (from.getChange(chg.getParent()) == null) {
+ from.getChanges().get(i).setParent(null);
+ }
+ }
+ }
+
+ private boolean isWithinTask(TransitionInfo info, TransitionInfo.Change chg) {
+ TransitionInfo.Change curr = chg;
+ while (curr != null) {
+ if (curr.getTaskInfo() != null) return true;
+ if (curr.getParent() == null) break;
+ curr = info.getChange(curr.getParent());
+ }
+ return false;
+ }
+
+ /**
+ * This is intended to be called by SplitCoordinator as a helper to mix an already-pending
+ * split transition with a display-change. The use-case for this is when a display
+ * change/rotation gets collected into a split-screen enter/exit transition which has already
+ * been claimed by StageCoordinator.handleRequest . This happens during launcher tests.
+ */
+ public boolean animatePendingSplitWithDisplayChange(@NonNull IBinder transition,
+ @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT,
+ @NonNull SurfaceControl.Transaction finishT,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ final TransitionInfo everythingElse = subCopy(info, info.getType(), true /* withChanges */);
+ final TransitionInfo displayPart = subCopy(info, TRANSIT_CHANGE, false /* withChanges */);
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ TransitionInfo.Change change = info.getChanges().get(i);
+ if (isWithinTask(info, change)) continue;
+ displayPart.addChange(change);
+ everythingElse.getChanges().remove(i);
+ }
+ if (displayPart.getChanges().isEmpty()) return false;
+ unlinkMissingParents(everythingElse);
+ final MixedTransition mixed = new MixedTransition(
+ MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE, transition);
+ mixed.mFinishCallback = finishCallback;
+ mActiveTransitions.add(mixed);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Animation is a mix of display change "
+ + "and split change.");
+ // We need to split the transition into 2 parts: the split part and the display part.
+ mixed.mInFlightSubAnimations = 2;
+
+ Transitions.TransitionFinishCallback finishCB = (wct, wctCB) -> {
+ --mixed.mInFlightSubAnimations;
+ if (wctCB != null) {
+ throw new IllegalArgumentException("Can't mix transitions that require finish"
+ + " sync callback");
+ }
+ if (wct != null) {
+ if (mixed.mFinishWCT == null) {
+ mixed.mFinishWCT = wct;
+ } else {
+ mixed.mFinishWCT.merge(wct, true /* transfer */);
+ }
+ }
+ if (mixed.mInFlightSubAnimations > 0) return;
+ mActiveTransitions.remove(mixed);
+ mixed.mFinishCallback.onTransitionFinished(mixed.mFinishWCT, null /* wctCB */);
+ };
+
+ // Dispatch the display change. This will most-likely be taken by the default handler.
+ // Do this first since the first handler used will apply the startT; the display change
+ // needs to take a screenshot before that happens so we need it to be the first handler.
+ mixed.mLeftoversHandler = mPlayer.dispatchTransition(mixed.mTransition, displayPart,
+ startT, finishT, finishCB, mSplitHandler);
+
+ // Note: at this point, startT has probably already been applied, so we are basically
+ // giving splitHandler an empty startT. This is currently OK because display-change will
+ // grab a screenshot and paste it on top anyways.
+ mSplitHandler.startPendingAnimation(
+ transition, everythingElse, startT, finishT, finishCB);
+ return true;
+ }
+
@Override
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@@ -279,6 +372,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler {
} else {
mPipHandler.end();
}
+ } else if (mixed.mType == MixedTransition.TYPE_DISPLAY_AND_SPLIT_CHANGE) {
+ // queue
} else {
throw new IllegalStateException("Playing a mixed transition with unknown type? "
+ mixed.mType);
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 4c927b6e84b8..1ae779e25bbf 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
@@ -96,6 +96,7 @@ import android.view.WindowManager.TransitionType;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Transformation;
+import android.window.ScreenCapture;
import android.window.TransitionInfo;
import android.window.TransitionMetrics;
import android.window.TransitionRequestInfo;
@@ -630,16 +631,16 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
.setBufferSize(extensionRect.width(), extensionRect.height())
.build();
- SurfaceControl.LayerCaptureArgs captureArgs =
- new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend)
+ ScreenCapture.LayerCaptureArgs captureArgs =
+ new ScreenCapture.LayerCaptureArgs.Builder(surfaceToExtend)
.setSourceCrop(edgeBounds)
.setFrameScale(1)
.setPixelFormat(PixelFormat.RGBA_8888)
.setChildrenOnly(true)
.setAllowProtected(true)
.build();
- final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
- SurfaceControl.captureLayers(captureArgs);
+ final ScreenCapture.ScreenshotHardwareBuffer edgeBuffer =
+ ScreenCapture.captureLayers(captureArgs);
if (edgeBuffer == null) {
ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
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
index b647f43da522..2b27baeb515a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/ScreenRotationAnimation.java
@@ -46,6 +46,7 @@ import android.view.SurfaceControl.Transaction;
import android.view.SurfaceSession;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import android.window.ScreenCapture;
import android.window.TransitionInfo;
import com.android.internal.R;
@@ -144,14 +145,14 @@ class ScreenRotationAnimation {
t.reparent(mScreenshotLayer, mAnimLeash);
mStartLuma = change.getSnapshotLuma();
} else {
- SurfaceControl.LayerCaptureArgs args =
- new SurfaceControl.LayerCaptureArgs.Builder(mSurfaceControl)
+ ScreenCapture.LayerCaptureArgs args =
+ new ScreenCapture.LayerCaptureArgs.Builder(mSurfaceControl)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
.setSourceCrop(new Rect(0, 0, mStartWidth, mStartHeight))
.build();
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureLayers(args);
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+ ScreenCapture.captureLayers(args);
if (screenshotBuffer == null) {
Slog.w(TAG, "Unable to take screenshot of display");
return;
@@ -481,8 +482,8 @@ class ScreenRotationAnimation {
}
Rect crop = new Rect(0, 0, bounds.width(), bounds.height());
- SurfaceControl.ScreenshotHardwareBuffer buffer =
- SurfaceControl.captureLayers(surfaceControl, crop, 1);
+ ScreenCapture.ScreenshotHardwareBuffer buffer =
+ ScreenCapture.captureLayers(surfaceControl, crop, 1);
if (buffer == null) {
return 0;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
index 83aa539d24d6..9e49b51e1504 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.windowdecor;
+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.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
@@ -36,6 +37,8 @@ import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.desktopmode.DesktopModeController;
+import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.freeform.FreeformTaskTransitionStarter;
import com.android.wm.shell.transition.Transitions;
@@ -52,6 +55,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
private final DisplayController mDisplayController;
private final SyncTransactionQueue mSyncQueue;
private FreeformTaskTransitionStarter mTransitionStarter;
+ private DesktopModeController mDesktopModeController;
public CaptionWindowDecorViewModel(
Context context,
@@ -59,7 +63,8 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
Choreographer mainChoreographer,
ShellTaskOrganizer taskOrganizer,
DisplayController displayController,
- SyncTransactionQueue syncQueue) {
+ SyncTransactionQueue syncQueue,
+ DesktopModeController desktopModeController) {
mContext = context;
mMainHandler = mainHandler;
mMainChoreographer = mainChoreographer;
@@ -67,6 +72,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
mTaskOrganizer = taskOrganizer;
mDisplayController = displayController;
mSyncQueue = syncQueue;
+ mDesktopModeController = desktopModeController;
}
@Override
@@ -80,6 +86,7 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
SurfaceControl.Transaction finishT) {
+ if (!shouldShowWindowDecor(taskInfo)) return null;
final CaptionWindowDecoration windowDecoration = new CaptionWindowDecoration(
mContext,
mDisplayController,
@@ -101,9 +108,12 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
@Override
public CaptionWindowDecoration adoptWindowDecoration(AutoCloseable windowDecor) {
- return (windowDecor instanceof CaptionWindowDecoration)
- ? (CaptionWindowDecoration) windowDecor
- : null;
+ if (!(windowDecor instanceof CaptionWindowDecoration)) return null;
+ final CaptionWindowDecoration captionWindowDecor = (CaptionWindowDecoration) windowDecor;
+ if (!shouldShowWindowDecor(captionWindowDecor.mTaskInfo)) {
+ return null;
+ }
+ return captionWindowDecor;
}
@Override
@@ -205,8 +215,10 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
}
private void handleEventForMove(MotionEvent e) {
- if (mTaskOrganizer.getRunningTaskInfo(mTaskId).getWindowingMode()
- == WINDOWING_MODE_FULLSCREEN) {
+ RunningTaskInfo taskInfo = mTaskOrganizer.getRunningTaskInfo(mTaskId);
+ int windowingMode = mDesktopModeController
+ .getDisplayAreaWindowingMode(taskInfo.displayId);
+ if (windowingMode == WINDOWING_MODE_FULLSCREEN) {
return;
}
switch (e.getActionMasked()) {
@@ -224,11 +236,25 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel<Caption
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
int dragPointerIdx = e.findPointerIndex(mDragPointerId);
+ int statusBarHeight = mDisplayController.getDisplayLayout(taskInfo.displayId)
+ .stableInsets().top;
mDragResizeCallback.onDragResizeEnd(
e.getRawX(dragPointerIdx), e.getRawY(dragPointerIdx));
+ if (e.getRawY(dragPointerIdx) <= statusBarHeight
+ && windowingMode == WINDOWING_MODE_FREEFORM) {
+ mDesktopModeController.setDesktopModeActive(false);
+ }
break;
}
}
}
}
+
+ private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
+ if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
+ return DesktopModeStatus.IS_SUPPORTED
+ && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD
+ && mDisplayController.getDisplayContext(taskInfo.displayId)
+ .getResources().getConfiguration().smallestScreenWidthDp >= 600;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
index 5040bc37c614..733f6b7d5dbf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecoration.java
@@ -34,7 +34,7 @@ import com.android.wm.shell.R;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.desktopmode.DesktopMode;
+import com.android.wm.shell.desktopmode.DesktopModeStatus;
/**
* Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
@@ -164,7 +164,7 @@ public class CaptionWindowDecoration extends WindowDecoration<WindowDecorLinearL
View caption = mResult.mRootView.findViewById(R.id.caption);
caption.setOnTouchListener(mOnCaptionTouchListener);
View maximize = caption.findViewById(R.id.maximize_window);
- if (DesktopMode.IS_SUPPORTED) {
+ if (DesktopModeStatus.IS_SUPPORTED) {
// Hide maximize button when desktop mode is available
maximize.setVisibility(View.GONE);
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
index c234949572bf..d9697d288ab6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecorViewModel.java
@@ -42,6 +42,7 @@ public interface WindowDecorViewModel<T extends AutoCloseable> {
/**
* Creates a window decoration for the given task.
+ * Can be {@code null} for Fullscreen tasks but not Freeform ones.
*
* @param taskInfo the initial task info of the task
* @param taskSurface the surface of the task
@@ -49,7 +50,7 @@ public interface WindowDecorViewModel<T extends AutoCloseable> {
* @param finishT the finish transaction to restore states after the transition
* @return the window decoration object
*/
- T createWindowDecoration(
+ @Nullable T createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo,
SurfaceControl taskSurface,
SurfaceControl.Transaction startT,
@@ -57,11 +58,12 @@ public interface WindowDecorViewModel<T extends AutoCloseable> {
/**
* Adopts the window decoration if possible.
+ * May be {@code null} if a window decor is not needed or the given one is incompatible.
*
* @param windowDecor the potential window decoration to adopt
* @return the window decoration if it can be adopted, or {@code null} otherwise.
*/
- T adoptWindowDecoration(@Nullable AutoCloseable windowDecor);
+ @Nullable T adoptWindowDecoration(@Nullable AutoCloseable windowDecor);
/**
* Notifies a task info update on the given task, with the window decoration created previously
diff --git a/libs/WindowManager/Shell/tests/flicker/Android.bp b/libs/WindowManager/Shell/tests/flicker/Android.bp
index 3ca5b9c38aff..d6adaa7d533b 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -48,6 +48,6 @@ android_test {
"wm-flicker-common-assertions",
"wm-flicker-common-app-helpers",
"platform-test-annotations",
- "wmshell-flicker-test-components",
+ "flickertestapplib",
],
}
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 574a9f4da627..1284c41af855 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -19,7 +19,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
<option name="test-file-name" value="WMShellFlickerTests.apk"/>
- <option name="test-file-name" value="WMShellFlickerTestApp.apk" />
+ <option name="test-file-name" value="FlickerTestApp.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest">
<option name="package" value="com.android.wm.shell.flicker"/>
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 55979905cc4f..f9d1e88e70b8 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
@@ -46,6 +46,66 @@ fun FlickerTestParameter.appPairsDividerBecomesVisible() {
}
}
+fun FlickerTestParameter.splitScreenEntered(
+ component1: IComponentMatcher,
+ component2: IComponentMatcher,
+ fromOtherApp: Boolean
+) {
+ if (fromOtherApp) {
+ appWindowIsInvisibleAtStart(component1)
+ } else {
+ appWindowIsVisibleAtStart(component1)
+ }
+ appWindowIsInvisibleAtStart(component2)
+ splitScreenDividerIsInvisibleAtStart()
+
+ appWindowIsVisibleAtEnd(component1)
+ appWindowIsVisibleAtEnd(component2)
+ splitScreenDividerIsVisibleAtEnd()
+}
+
+fun FlickerTestParameter.splitScreenDismissed(
+ component1: IComponentMatcher,
+ component2: IComponentMatcher,
+ toHome: Boolean
+) {
+ appWindowIsVisibleAtStart(component1)
+ appWindowIsVisibleAtStart(component2)
+ splitScreenDividerIsVisibleAtStart()
+
+ appWindowIsInvisibleAtEnd(component1)
+ if (toHome) {
+ appWindowIsInvisibleAtEnd(component2)
+ } else {
+ appWindowIsVisibleAtEnd(component2)
+ }
+ splitScreenDividerIsInvisibleAtEnd()
+}
+
+fun FlickerTestParameter.splitScreenDividerIsVisibleAtStart() {
+ assertLayersStart {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+}
+
+fun FlickerTestParameter.splitScreenDividerIsVisibleAtEnd() {
+ assertLayersEnd {
+ this.isVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+}
+
+fun FlickerTestParameter.splitScreenDividerIsInvisibleAtStart() {
+ assertLayersStart {
+ this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+}
+
+fun FlickerTestParameter.splitScreenDividerIsInvisibleAtEnd() {
+ assertLayersEnd {
+ this.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
+ }
+}
+
fun FlickerTestParameter.splitScreenDividerBecomesVisible() {
layerBecomesVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
}
@@ -114,14 +174,14 @@ fun FlickerTestParameter.splitAppLayerBoundsBecomesVisibleByDrag(
) {
assertLayers {
if (isShellTransitionsEnabled) {
- this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
+ this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true)
.then()
.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
.then()
// TODO(b/245472831): Verify the component should snap to divider.
.isVisible(component)
} else {
- this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
+ this.notContains(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component), isOptional = true)
.then()
.isInvisible(SPLIT_SCREEN_DIVIDER_COMPONENT.or(component))
.then()
@@ -271,6 +331,14 @@ fun FlickerTestParameter.appWindowBecomesInvisible(
}
}
+fun FlickerTestParameter.appWindowIsVisibleAtStart(
+ component: IComponentMatcher
+) {
+ assertWmStart {
+ this.isAppWindowVisible(component)
+ }
+}
+
fun FlickerTestParameter.appWindowIsVisibleAtEnd(
component: IComponentMatcher
) {
@@ -279,6 +347,22 @@ fun FlickerTestParameter.appWindowIsVisibleAtEnd(
}
}
+fun FlickerTestParameter.appWindowIsInvisibleAtStart(
+ component: IComponentMatcher
+) {
+ assertWmStart {
+ this.isAppWindowInvisible(component)
+ }
+}
+
+fun FlickerTestParameter.appWindowIsInvisibleAtEnd(
+ component: IComponentMatcher
+) {
+ assertWmEnd {
+ this.isAppWindowInvisible(component)
+ }
+}
+
fun FlickerTestParameter.appWindowKeepVisible(
component: IComponentMatcher
) {
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 06361f9ca4ab..53dd8b04afeb 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
@@ -24,3 +24,7 @@ val APP_PAIR_SPLIT_DIVIDER_COMPONENT = ComponentNameMatcher("", "AppPairSplitDiv
val DOCKED_STACK_DIVIDER_COMPONENT = ComponentNameMatcher("", "DockedStackDivider#")
val SPLIT_SCREEN_DIVIDER_COMPONENT = ComponentNameMatcher("", "StageCoordinatorSplitDivider#")
val SPLIT_DECOR_MANAGER = ComponentNameMatcher("", "SplitDecorManager#")
+
+enum class Direction {
+ UP, DOWN, LEFT, RIGHT
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt
new file mode 100644
index 000000000000..c045325f19c3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/MultiWindowUtils.kt
@@ -0,0 +1,55 @@
+/*
+ * 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
+
+import android.app.Instrumentation
+import android.content.Context
+import android.provider.Settings
+import android.util.Log
+import com.android.compatibility.common.util.SystemUtil
+import java.io.IOException
+
+object MultiWindowUtils {
+ private fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
+ try {
+ SystemUtil.runShellCommand(instrumentation, cmd)
+ } catch (e: IOException) {
+ Log.e(MultiWindowUtils::class.simpleName, "executeShellCommand error! $e")
+ }
+ }
+
+ fun getDevEnableNonResizableMultiWindow(context: Context): Int =
+ Settings.Global.getInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
+
+ fun setDevEnableNonResizableMultiWindow(context: Context, configValue: Int) =
+ Settings.Global.putInt(context.contentResolver,
+ Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW, configValue)
+
+ fun setSupportsNonResizableMultiWindow(instrumentation: Instrumentation, configValue: Int) =
+ executeShellCommand(
+ instrumentation,
+ createConfigSupportsNonResizableMultiWindowCommand(configValue))
+
+ fun resetMultiWindowConfig(instrumentation: Instrumentation) =
+ executeShellCommand(instrumentation, resetMultiWindowConfigCommand)
+
+ private fun createConfigSupportsNonResizableMultiWindowCommand(configValue: Int): String =
+ "wm set-multi-window-config --supportsNonResizable $configValue"
+
+ private const val resetMultiWindowConfigCommand: String = "wm reset-multi-window-config"
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
index 1390334f7938..cbe085be8952 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/BaseBubbleScreen.kt
@@ -19,6 +19,7 @@ package com.android.wm.shell.flicker.bubble
import android.app.INotificationManager
import android.app.NotificationManager
import android.content.Context
+import android.content.pm.PackageManager
import android.os.ServiceManager
import android.view.Surface
import androidx.test.uiautomator.By
@@ -28,9 +29,9 @@ import com.android.server.wm.flicker.Flicker
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.LaunchBubbleHelper
import com.android.server.wm.flicker.helpers.SYSTEMUI_PACKAGE
import com.android.wm.shell.flicker.BaseTest
-import com.android.wm.shell.flicker.helpers.LaunchBubbleHelper
import org.junit.runners.Parameterized
/**
@@ -47,7 +48,7 @@ abstract class BaseBubbleScreen(
ServiceManager.getService(Context.NOTIFICATION_SERVICE))
private val uid = context.packageManager.getApplicationInfo(
- testApp.`package`, 0).uid
+ testApp.`package`, PackageManager.ApplicationInfoFlags.of(0)).uid
@JvmOverloads
protected open fun buildTransition(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
index effd33095530..9a6fd1103766 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/LaunchBubbleScreen.kt
@@ -16,8 +16,9 @@
package com.android.wm.shell.flicker.bubble
-import android.platform.test.annotations.Presubmit
import android.platform.test.annotations.RequiresDevice
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.annotation.Group4
@@ -47,10 +48,15 @@ open class LaunchBubbleScreen(testSpec: FlickerTestParameter) : BaseBubbleScreen
transitions {
val addBubbleBtn = waitAndGetAddBubbleBtn()
addBubbleBtn?.click() ?: error("Bubble widget not found")
+
+ device.wait(
+ Until.findObjects(
+ By.res(SYSTEM_UI_PACKAGE, BUBBLE_RES_NAME)
+ ), FIND_OBJECT_TIMEOUT
+ ) ?: error("No bubbles found")
}
}
- @Presubmit
@Test
open fun testAppIsAlwaysVisible() {
testSpec.assertLayers {
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
deleted file mode 100644
index 01ba9907c24c..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/BaseAppHelper.kt
+++ /dev/null
@@ -1,67 +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.helpers
-
-import android.app.Instrumentation
-import android.content.pm.PackageManager.FEATURE_LEANBACK
-import android.content.pm.PackageManager.FEATURE_LEANBACK_ONLY
-import android.support.test.launcherhelper.LauncherStrategyFactory
-import android.util.Log
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.UiObject2
-import androidx.test.uiautomator.Until
-import com.android.compatibility.common.util.SystemUtil
-import com.android.server.wm.flicker.helpers.StandardAppHelper
-import com.android.server.wm.traces.common.IComponentNameMatcher
-import java.io.IOException
-
-abstract class BaseAppHelper(
- instrumentation: Instrumentation,
- launcherName: String,
- component: IComponentNameMatcher
-) : StandardAppHelper(
- instrumentation,
- launcherName,
- component,
- LauncherStrategyFactory.getInstance(instrumentation).launcherStrategy
-) {
- private val appSelector = By.pkg(`package`).depth(0)
-
- protected val isTelevision: Boolean
- get() = context.packageManager.run {
- hasSystemFeature(FEATURE_LEANBACK) || hasSystemFeature(FEATURE_LEANBACK_ONLY)
- }
-
- val ui: UiObject2?
- get() = uiDevice.findObject(appSelector)
-
- fun waitUntilClosed(): Boolean {
- return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS)
- }
-
- companion object {
- private const val APP_CLOSE_WAIT_TIME_MS = 3_000L
-
- fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
- try {
- SystemUtil.runShellCommand(instrumentation, cmd)
- } catch (e: IOException) {
- Log.e("BaseAppHelper", "executeShellCommand error! $e")
- }
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
deleted file mode 100644
index 471e010cf560..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/FixedAppHelper.kt
+++ /dev/null
@@ -1,27 +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.helpers
-
-import android.app.Instrumentation
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.wm.shell.flicker.testapp.Components
-
-class FixedAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
- instrumentation,
- Components.FixedActivity.LABEL,
- Components.FixedActivity.COMPONENT.toFlickerComponent()
-) \ No newline at end of file
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
deleted file mode 100644
index 2e690de666f4..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/ImeAppHelper.kt
+++ /dev/null
@@ -1,76 +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.helpers
-
-import android.app.Instrumentation
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.Until
-import com.android.server.wm.flicker.helpers.FIND_TIMEOUT
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.testapp.Components
-
-open class ImeAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
- instrumentation,
- Components.ImeActivity.LABEL,
- Components.ImeActivity.COMPONENT.toFlickerComponent()
-) {
- /**
- * Opens the IME and wait for it to be displayed
- *
- * @param wmHelper Helper used to wait for WindowManager states
- */
- open fun openIME(wmHelper: WindowManagerStateHelper) {
- if (!isTelevision) {
- val editText = uiDevice.wait(
- Until.findObject(By.res(getPackage(), "plain_text_input")),
- FIND_TIMEOUT)
-
- require(editText != null) {
- "Text field not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)"
- }
- editText.click()
- wmHelper.StateSyncBuilder()
- .withImeShown()
- .waitForAndVerify()
- } else {
- // If we do the same thing as above - editText.click() - on TV, that's going to force TV
- // into the touch mode. We really don't want that.
- launchViaIntent(action = Components.ImeActivity.ACTION_OPEN_IME)
- }
- }
-
- /**
- * Opens the IME and wait for it to be gone
- *
- * @param wmHelper Helper used to wait for WindowManager states
- */
- open fun closeIME(wmHelper: WindowManagerStateHelper) {
- if (!isTelevision) {
- uiDevice.pressBack()
- // Using only the AccessibilityInfo it is not possible to identify if the IME is active
- wmHelper.StateSyncBuilder()
- .withImeGone()
- .waitForAndVerify()
- } else {
- // While pressing the back button should close the IME on TV as well, it may also lead
- // to the app closing. So let's instead just ask the app to close the IME.
- launchViaIntent(action = Components.ImeActivity.ACTION_CLOSE_IME)
- }
- }
-}
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
deleted file mode 100644
index 52e5d7e14ab9..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SplitScreenHelper.kt
+++ /dev/null
@@ -1,354 +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.helpers
-
-import android.app.Instrumentation
-import android.graphics.Point
-import android.os.SystemClock
-import android.view.InputDevice
-import android.view.MotionEvent
-import android.view.ViewConfiguration
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.BySelector
-import androidx.test.uiautomator.UiDevice
-import androidx.test.uiautomator.Until
-import com.android.launcher3.tapl.LauncherInstrumentation
-import com.android.server.wm.traces.common.IComponentMatcher
-import com.android.server.wm.traces.common.IComponentNameMatcher
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.SPLIT_DECOR_MANAGER
-import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
-import com.android.wm.shell.flicker.testapp.Components
-
-class SplitScreenHelper(
- instrumentation: Instrumentation,
- activityLabel: String,
- componentInfo: IComponentNameMatcher
-) : BaseAppHelper(instrumentation, activityLabel, componentInfo) {
-
- companion object {
- const val TIMEOUT_MS = 3_000L
- const val DRAG_DURATION_MS = 1_000L
- const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
- const val DIVIDER_BAR = "docked_divider_handle"
- const val GESTURE_STEP_MS = 16L
- const val LONG_PRESS_TIME_MS = 100L
-
- private val notificationScrollerSelector: BySelector
- get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER)
- private val notificationContentSelector: BySelector
- get() = By.text("Notification content")
- private val dividerBarSelector: BySelector
- get() = By.res(SYSTEM_UI_PACKAGE_NAME, DIVIDER_BAR)
-
- fun getPrimary(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(
- instrumentation,
- Components.SplitScreenActivity.LABEL,
- Components.SplitScreenActivity.COMPONENT.toFlickerComponent()
- )
-
- fun getSecondary(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(
- instrumentation,
- Components.SplitScreenSecondaryActivity.LABEL,
- Components.SplitScreenSecondaryActivity.COMPONENT.toFlickerComponent()
- )
-
- fun getNonResizeable(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(
- instrumentation,
- Components.NonResizeableActivity.LABEL,
- Components.NonResizeableActivity.COMPONENT.toFlickerComponent()
- )
-
- fun getSendNotification(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(
- instrumentation,
- Components.SendNotificationActivity.LABEL,
- Components.SendNotificationActivity.COMPONENT.toFlickerComponent()
- )
-
- fun getIme(instrumentation: Instrumentation): SplitScreenHelper =
- SplitScreenHelper(
- instrumentation,
- Components.ImeActivity.LABEL,
- Components.ImeActivity.COMPONENT.toFlickerComponent()
- )
-
- fun waitForSplitComplete(
- wmHelper: WindowManagerStateHelper,
- primaryApp: IComponentMatcher,
- secondaryApp: IComponentMatcher,
- ) {
- wmHelper.StateSyncBuilder()
- .withWindowSurfaceAppeared(primaryApp)
- .withWindowSurfaceAppeared(secondaryApp)
- .withSplitDividerVisible()
- .waitForAndVerify()
- }
-
- fun enterSplit(
- wmHelper: WindowManagerStateHelper,
- tapl: LauncherInstrumentation,
- primaryApp: SplitScreenHelper,
- secondaryApp: SplitScreenHelper
- ) {
- tapl.workspace.switchToOverview().dismissAllTasks()
- primaryApp.launchViaIntent(wmHelper)
- secondaryApp.launchViaIntent(wmHelper)
- tapl.goHome()
- wmHelper.StateSyncBuilder()
- .withHomeActivityVisible()
- .waitForAndVerify()
- splitFromOverview(tapl)
- waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
-
- fun splitFromOverview(tapl: LauncherInstrumentation) {
- // Note: The initial split position in landscape is different between tablet and phone.
- // In landscape, tablet will let the first app split to right side, and phone will
- // split to left side.
- if (tapl.isTablet) {
- tapl.workspace.switchToOverview().overviewActions
- .clickSplit()
- .currentTask
- .open()
- } else {
- tapl.workspace.switchToOverview().currentTask
- .tapMenu()
- .tapSplitMenuItem()
- .currentTask
- .open()
- }
- SystemClock.sleep(TIMEOUT_MS)
- }
-
- fun dragFromNotificationToSplit(
- instrumentation: Instrumentation,
- device: UiDevice,
- wmHelper: WindowManagerStateHelper
- ) {
- val displayBounds = wmHelper.currentState.layerState
- .displays.firstOrNull { !it.isVirtual }
- ?.layerStackSpace
- ?: error("Display not found")
-
- // Pull down the notifications
- device.swipe(
- displayBounds.centerX(), 5,
- displayBounds.centerX(), displayBounds.bottom, 20 /* steps */
- )
- SystemClock.sleep(TIMEOUT_MS)
-
- // Find the target notification
- val notificationScroller = device.wait(
- Until.findObject(notificationScrollerSelector), TIMEOUT_MS
- )
- var notificationContent = notificationScroller.findObject(notificationContentSelector)
-
- while (notificationContent == null) {
- device.swipe(
- displayBounds.centerX(), displayBounds.centerY(),
- displayBounds.centerX(), displayBounds.centerY() - 150, 20 /* steps */
- )
- notificationContent = notificationScroller.findObject(notificationContentSelector)
- }
-
- // Drag to split
- val dragStart = notificationContent.visibleCenter
- val dragMiddle = Point(dragStart.x + 50, dragStart.y)
- val dragEnd = Point(displayBounds.width / 4, displayBounds.width / 4)
- val downTime = SystemClock.uptimeMillis()
-
- touch(
- instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime,
- TIMEOUT_MS, dragStart
- )
- // It needs a horizontal movement to trigger the drag
- touchMove(
- instrumentation, downTime, SystemClock.uptimeMillis(),
- DRAG_DURATION_MS, dragStart, dragMiddle
- )
- touchMove(
- instrumentation, downTime, SystemClock.uptimeMillis(),
- DRAG_DURATION_MS, dragMiddle, dragEnd
- )
- // Wait for a while to start splitting
- SystemClock.sleep(TIMEOUT_MS)
- touch(
- instrumentation, MotionEvent.ACTION_UP, downTime, SystemClock.uptimeMillis(),
- GESTURE_STEP_MS, dragEnd
- )
- SystemClock.sleep(TIMEOUT_MS)
- }
-
- fun touch(
- instrumentation: Instrumentation,
- action: Int,
- downTime: Long,
- eventTime: Long,
- duration: Long,
- point: Point
- ) {
- val motionEvent = MotionEvent.obtain(
- downTime, eventTime, action, point.x.toFloat(), point.y.toFloat(), 0
- )
- motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN
- instrumentation.uiAutomation.injectInputEvent(motionEvent, true)
- motionEvent.recycle()
- SystemClock.sleep(duration)
- }
-
- fun touchMove(
- instrumentation: Instrumentation,
- downTime: Long,
- eventTime: Long,
- duration: Long,
- from: Point,
- to: Point
- ) {
- val steps: Long = duration / GESTURE_STEP_MS
- var currentTime = eventTime
- var currentX = from.x.toFloat()
- var currentY = from.y.toFloat()
- val stepX = (to.x.toFloat() - from.x.toFloat()) / steps.toFloat()
- val stepY = (to.y.toFloat() - from.y.toFloat()) / steps.toFloat()
-
- for (i in 1..steps) {
- val motionMove = MotionEvent.obtain(
- downTime, currentTime, MotionEvent.ACTION_MOVE, currentX, currentY, 0
- )
- motionMove.source = InputDevice.SOURCE_TOUCHSCREEN
- instrumentation.uiAutomation.injectInputEvent(motionMove, true)
- motionMove.recycle()
-
- currentTime += GESTURE_STEP_MS
- if (i == steps - 1) {
- currentX = to.x.toFloat()
- currentY = to.y.toFloat()
- } else {
- currentX += stepX
- currentY += stepY
- }
- SystemClock.sleep(GESTURE_STEP_MS)
- }
- }
-
- fun longPress(
- instrumentation: Instrumentation,
- point: Point
- ) {
- val downTime = SystemClock.uptimeMillis()
- touch(instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, TIMEOUT_MS, point)
- SystemClock.sleep(LONG_PRESS_TIME_MS)
- touch(instrumentation, MotionEvent.ACTION_UP, downTime, downTime, TIMEOUT_MS, point)
- }
-
- fun createShortcutOnHotseatIfNotExist(
- tapl: LauncherInstrumentation,
- appName: String
- ) {
- tapl.workspace.deleteAppIcon(tapl.workspace.getHotseatAppIcon(0))
- val allApps = tapl.workspace.switchToAllApps()
- allApps.freeze()
- try {
- allApps.getAppIcon(appName).dragToHotseat(0)
- } finally {
- allApps.unfreeze()
- }
- }
-
- fun dragDividerToResizeAndWait(
- device: UiDevice,
- wmHelper: WindowManagerStateHelper
- ) {
- val displayBounds = wmHelper.currentState.layerState
- .displays.firstOrNull { !it.isVirtual }
- ?.layerStackSpace
- ?: error("Display not found")
- val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
- dividerBar.drag(Point(displayBounds.width * 1 / 3, displayBounds.height * 2 / 3))
-
- wmHelper.StateSyncBuilder()
- .withWindowSurfaceDisappeared(SPLIT_DECOR_MANAGER)
- .waitForAndVerify()
- }
-
- fun dragDividerToDismissSplit(
- device: UiDevice,
- wmHelper: WindowManagerStateHelper,
- dragToRight: Boolean,
- dragToBottom: Boolean
- ) {
- val displayBounds = wmHelper.currentState.layerState
- .displays.firstOrNull { !it.isVirtual }
- ?.layerStackSpace
- ?: error("Display not found")
- val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
- dividerBar.drag(Point(
- if (dragToRight) {
- displayBounds.width * 4 / 5
- } else {
- displayBounds.width * 1 / 5
- },
- if (dragToBottom) {
- displayBounds.height * 4 / 5
- } else {
- displayBounds.height * 1 / 5
- }))
- }
-
- fun doubleTapDividerToSwitch(device: UiDevice) {
- val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
- val interval = (ViewConfiguration.getDoubleTapTimeout() +
- ViewConfiguration.getDoubleTapMinTime()) / 2
- dividerBar.click()
- SystemClock.sleep(interval.toLong())
- dividerBar.click()
- }
-
- fun copyContentInSplit(
- instrumentation: Instrumentation,
- device: UiDevice,
- sourceApp: IComponentNameMatcher,
- destinationApp: IComponentNameMatcher,
- ) {
- // Copy text from sourceApp
- val textView = device.wait(Until.findObject(
- By.res(sourceApp.packageName, "SplitScreenTest")), TIMEOUT_MS)
- longPress(instrumentation, textView.getVisibleCenter())
-
- val copyBtn = device.wait(Until.findObject(By.text("Copy")), TIMEOUT_MS)
- copyBtn.click()
-
- // Paste text to destinationApp
- val editText = device.wait(Until.findObject(
- By.res(destinationApp.packageName, "plain_text_input")), TIMEOUT_MS)
- longPress(instrumentation, editText.getVisibleCenter())
-
- val pasteBtn = device.wait(Until.findObject(By.text("Paste")), TIMEOUT_MS)
- pasteBtn.click()
-
- // Verify text
- if (!textView.getText().contentEquals(editText.getText())) {
- error("Fail to copy content in split")
- }
- }
- }
-}
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 87d800c3dc6a..4788507c1443 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
@@ -28,16 +28,17 @@ 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.FixedOrientationAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerPositionAtStartAndEnd
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP
+import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
-import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
-import com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -72,7 +73,7 @@ import org.junit.runners.Parameterized
class EnterPipToOtherOrientationTest(
testSpec: FlickerTestParameter
) : PipTransition(testSpec) {
- private val testApp = FixedAppHelper(instrumentation)
+ private val testApp = FixedOrientationAppHelper(instrumentation)
private val startingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_90)
private val endingBounds = WindowUtils.getDisplayBounds(Surface.ROTATION_0)
@@ -127,7 +128,7 @@ class EnterPipToOtherOrientationTest(
}
/**
- * Checks that the [ComponentMatcher.NAV_BAR] has the correct position at
+ * Checks that the [ComponentNameMatcher.NAV_BAR] has the correct position at
* the start and end of the transition
*/
@FlakyTest
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
index 45851c8b6326..628599160c65 100644
--- 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
@@ -18,14 +18,14 @@ package com.android.wm.shell.flicker.pip
import android.platform.test.annotations.Presubmit
import com.android.server.wm.flicker.FlickerTestParameter
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.server.wm.flicker.helpers.FixedOrientationAppHelper
import org.junit.Test
/**
* Base class for pip expand tests
*/
abstract class ExitPipToAppTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- protected val testApp = FixedAppHelper(instrumentation)
+ protected val testApp = FixedOrientationAppHelper(instrumentation)
/**
* Checks that the pip app window remains inside the display bounds throughout the whole
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
index 0f7d8a9510b3..39be89d2592b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipTransition.kt
@@ -30,7 +30,7 @@ import org.junit.Test
*/
abstract class ExitPipTransition(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = true) {
+ get() = buildTransition {
setup {
this.setRotation(testSpec.startRotation)
}
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
index 53450de5221d..c2fd0d78f0d5 100644
--- 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
@@ -62,7 +62,7 @@ class ExitPipViaExpandButtonClickTest(
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = true) {
+ get() = buildTransition {
setup {
// launch an app behind the pip one
testApp.launchViaIntent(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
index 75018d161267..0d75e02bd467 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipViaIntentTest.kt
@@ -62,7 +62,7 @@ class ExitPipViaIntentTest(testSpec: FlickerTestParameter) : ExitPipToAppTransit
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = true) {
+ get() = buildTransition {
setup {
// launch an app behind the pip one
testApp.launchViaIntent(wmHelper)
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
index 4d39ec53f18c..513ce95042d0 100644
--- 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
@@ -56,7 +56,7 @@ import org.junit.runners.Parameterized
@Group3
class ExpandPipOnDoubleClickTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = true) {
+ get() = buildTransition {
transitions {
pipApp.doubleClickPipWindow(wmHelper)
}
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
index de8c0093bfc5..746ce913c587 100644
--- 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
@@ -16,28 +16,30 @@
package com.android.wm.shell.flicker.pip
+import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
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.region.RegionSubject
+import com.android.wm.shell.flicker.Direction
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 (decrease).
+ * Test Pip movement with Launcher shelf height change (increase).
*
- * To run this test: `atest WMShellFlickerTests:MovePipDownShelfHeightChangeTest`
+ * To run this test: `atest WMShellFlickerTests:MovePipUpShelfHeightChangeTest`
*
* Actions:
* Launch [pipApp] in pip mode
- * Launch [testApp]
* Press home
+ * Launch [testApp]
* Check if pip window moves down (visually)
*
* Notes:
@@ -53,26 +55,41 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
-open class MovePipDownShelfHeightChangeTest(
+class MovePipDownShelfHeightChangeTest(
testSpec: FlickerTestParameter
) : MovePipShelfHeightTransition(testSpec) {
+// @Before
+// fun before() {
+// Assume.assumeFalse(isShellTransitionsEnabled)
+// }
+
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = false) {
+ get() = buildTransition {
teardown {
- testApp.launchViaIntent(wmHelper)
+ tapl.pressHome()
testApp.exit(wmHelper)
}
transitions {
- tapl.pressHome()
+ testApp.launchViaIntent(wmHelper)
}
}
- override fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) {
- current.isHigherOrEqual(previous.region)
- }
+ /**
+ * Checks that the visible region of [pipApp] window always moves down during the animation.
+ */
+ @Presubmit
+ @Test
+ fun pipWindowMovesDown() = pipWindowMoves(Direction.DOWN)
+
+ /**
+ * Checks that the visible region of [pipApp] layer always moves down during the animation.
+ */
+ @Presubmit
+ @Test
+ fun pipLayerMovesDown() = pipLayerMoves(Direction.DOWN)
companion object {
/**
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
index 9fb941e2a584..5f9419694c13 100644
--- 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
@@ -18,8 +18,9 @@ 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.flicker.helpers.FixedOrientationAppHelper
import com.android.server.wm.flicker.traces.region.RegionSubject
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
+import com.android.wm.shell.flicker.Direction
import org.junit.Test
/**
@@ -28,12 +29,7 @@ import org.junit.Test
abstract class MovePipShelfHeightTransition(
testSpec: FlickerTestParameter
) : PipTransition(testSpec) {
- protected val testApp = FixedAppHelper(instrumentation)
-
- /**
- * Checks if the window movement direction is valid
- */
- protected abstract fun assertRegionMovement(previous: RegionSubject, current: RegionSubject)
+ protected val testApp = FixedOrientationAppHelper(instrumentation)
/**
* Checks [pipApp] window remains visible throughout the animation
@@ -82,31 +78,46 @@ abstract class MovePipShelfHeightTransition(
}
/**
- * Checks that the visible region of [pipApp] always moves in the correct direction
+ * Checks that the visible region of [pipApp] window always moves in the specified direction
* during the animation.
*/
- @Presubmit
- @Test
- open fun pipWindowMoves() {
+ protected fun pipWindowMoves(direction: Direction) {
testSpec.assertWm {
- val pipWindowList = this.windowStates { pipApp.windowMatchesAnyOf(it) && it.isVisible }
- pipWindowList.zipWithNext { previous, current ->
- assertRegionMovement(previous.frame, current.frame)
+ val pipWindowFrameList = this.windowStates {
+ pipApp.windowMatchesAnyOf(it) && it.isVisible
+ }.map { it.frame }
+ when (direction) {
+ Direction.UP -> assertRegionMovementUp(pipWindowFrameList)
+ Direction.DOWN -> assertRegionMovementDown(pipWindowFrameList)
+ else -> error("Unhandled direction")
}
}
}
/**
- * Checks that the visible region of [pipApp] always moves up during the animation
+ * Checks that the visible region of [pipApp] layer always moves in the specified direction
+ * during the animation.
*/
- @Presubmit
- @Test
- open fun pipLayerMoves() {
+ protected fun pipLayerMoves(direction: Direction) {
testSpec.assertLayers {
- val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
- pipLayerList.zipWithNext { previous, current ->
- assertRegionMovement(previous.visibleRegion, current.visibleRegion)
+ val pipLayerRegionList = this.layers {
+ pipApp.layerMatchesAnyOf(it) && it.isVisible
+ }.map { it.visibleRegion }
+ when (direction) {
+ Direction.UP -> assertRegionMovementUp(pipLayerRegionList)
+ Direction.DOWN -> assertRegionMovementDown(pipLayerRegionList)
+ else -> error("Unhandled direction")
}
}
}
+
+ private fun assertRegionMovementDown(regions: List<RegionSubject>) {
+ regions.zipWithNext { previous, current -> current.isLowerOrEqual(previous) }
+ regions.last().isLower(regions.first())
+ }
+
+ private fun assertRegionMovementUp(regions: List<RegionSubject>) {
+ regions.zipWithNext { previous, current -> current.isHigherOrEqual(previous.region) }
+ regions.last().isHigher(regions.first())
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
index 3e08bfb247e6..93e7d5ca45ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/MovePipUpShelfHeightChangeTest.kt
@@ -16,31 +16,30 @@
package com.android.wm.shell.flicker.pip
-import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.Presubmit
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.helpers.isShellTransitionsEnabled
-import com.android.server.wm.flicker.traces.region.RegionSubject
-import org.junit.Assume
-import org.junit.Before
+import com.android.wm.shell.flicker.Direction
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 (increase).
+ * Test Pip movement with Launcher shelf height change (decrease).
*
- * To run this test: `atest WMShellFlickerTests:MovePipUpShelfHeightChangeTest`
+ * To run this test: `atest WMShellFlickerTests:MovePipDownShelfHeightChangeTest`
*
* Actions:
* Launch [pipApp] in pip mode
- * Press home
* Launch [testApp]
+ * Press home
* Check if pip window moves up (visually)
*
* Notes:
@@ -56,31 +55,38 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group3
-class MovePipUpShelfHeightChangeTest(
+open class MovePipUpShelfHeightChangeTest(
testSpec: FlickerTestParameter
) : MovePipShelfHeightTransition(testSpec) {
- @Before
- fun before() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- }
-
/**
* Defines the transition used to run the test
*/
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = false) {
- teardown {
- tapl.pressHome()
- testApp.exit(wmHelper)
+ get() = buildTransition() {
+ setup {
+ testApp.launchViaIntent(wmHelper)
}
transitions {
- testApp.launchViaIntent(wmHelper)
+ tapl.pressHome()
+ }
+ teardown {
+ testApp.exit(wmHelper)
}
}
- override fun assertRegionMovement(previous: RegionSubject, current: RegionSubject) {
- current.isLowerOrEqual(previous.region)
- }
+ /**
+ * Checks that the visible region of [pipApp] window always moves up during the animation.
+ */
+ @Presubmit
+ @Test
+ fun pipWindowMovesUp() = pipWindowMoves(Direction.UP)
+
+ /**
+ * Checks that the visible region of [pipApp] layer always moves up during the animation.
+ */
+ @Presubmit
+ @Test
+ fun pipLayerMovesUp() = pipLayerMoves(Direction.UP)
companion object {
/**
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 27fc2ed23b83..2aa0da95dfd5 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
@@ -24,11 +24,11 @@ import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.traces.common.ComponentNameMatcher
-import com.android.wm.shell.flicker.helpers.ImeAppHelper
import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.FixMethodOrder
@@ -56,7 +56,7 @@ open class PipKeyboardTest(testSpec: FlickerTestParameter) : PipTransition(testS
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = false) {
+ get() = buildTransition {
setup {
imeApp.launchViaIntent(wmHelper)
setRotation(testSpec.startRotation)
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 fd5ff2933f8c..0fce64ea073b 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
@@ -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.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.setRotation
-import com.android.wm.shell.flicker.helpers.FixedAppHelper
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -62,7 +62,7 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group4
open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testSpec) {
- private val fixedApp = FixedAppHelper(instrumentation)
+ private val testApp = SimpleAppHelper(instrumentation)
private val screenBoundsStart = WindowUtils.getDisplayBounds(testSpec.startRotation)
private val screenBoundsEnd = WindowUtils.getDisplayBounds(testSpec.endRotation)
@@ -72,9 +72,9 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS
}
override val transition: FlickerBuilder.() -> Unit
- get() = buildTransition(eachRun = false) {
+ get() = buildTransition {
setup {
- fixedApp.launchViaIntent(wmHelper)
+ testApp.launchViaIntent(wmHelper)
setRotation(testSpec.startRotation)
}
transitions {
@@ -90,48 +90,48 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS
override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
/**
- * Checks that [fixedApp] layer is within [screenBoundsStart] at the start of the transition
+ * Checks that [testApp] layer is within [screenBoundsStart] at the start of the transition
*/
@Presubmit
@Test
fun fixedAppLayer_StartingBounds() {
testSpec.assertLayersStart {
- visibleRegion(fixedApp).coversAtMost(screenBoundsStart)
+ visibleRegion(testApp).coversAtMost(screenBoundsStart)
}
}
/**
- * Checks that [fixedApp] layer is within [screenBoundsEnd] at the end of the transition
+ * Checks that [testApp] layer is within [screenBoundsEnd] at the end of the transition
*/
@Presubmit
@Test
fun fixedAppLayer_EndingBounds() {
testSpec.assertLayersEnd {
- visibleRegion(fixedApp).coversAtMost(screenBoundsEnd)
+ visibleRegion(testApp).coversAtMost(screenBoundsEnd)
}
}
/**
- * Checks that [fixedApp] plus [pipApp] layers are within [screenBoundsEnd] at the start
+ * Checks that [testApp] plus [pipApp] layers are within [screenBoundsEnd] at the start
* of the transition
*/
@Presubmit
@Test
fun appLayers_StartingBounds() {
testSpec.assertLayersStart {
- visibleRegion(fixedApp.or(pipApp)).coversExactly(screenBoundsStart)
+ visibleRegion(testApp.or(pipApp)).coversExactly(screenBoundsStart)
}
}
/**
- * Checks that [fixedApp] plus [pipApp] layers are within [screenBoundsEnd] at the end
+ * Checks that [testApp] plus [pipApp] layers are within [screenBoundsEnd] at the end
* of the transition
*/
@Presubmit
@Test
fun appLayers_EndingBounds() {
testSpec.assertLayersEnd {
- visibleRegion(fixedApp.or(pipApp)).coversExactly(screenBoundsEnd)
+ visibleRegion(testApp.or(pipApp)).coversExactly(screenBoundsEnd)
}
}
@@ -165,26 +165,26 @@ open class PipRotationTest(testSpec: FlickerTestParameter) : PipTransition(testS
}
/**
- * Ensure that the [pipApp] window does not obscure the [fixedApp] at the start of the
+ * Ensure that the [pipApp] window does not obscure the [testApp] at the start of the
* transition
*/
@Presubmit
@Test
fun pipIsAboveFixedAppWindow_Start() {
testSpec.assertWmStart {
- isAboveWindow(pipApp, fixedApp)
+ isAboveWindow(pipApp, testApp)
}
}
/**
- * Ensure that the [pipApp] window does not obscure the [fixedApp] at the end of the
+ * Ensure that the [pipApp] window does not obscure the [testApp] at the end of the
* transition
*/
@Presubmit
@Test
fun pipIsAboveFixedAppWindow_End() {
testSpec.assertWmEnd {
- isAboveWindow(pipApp, fixedApp)
+ isAboveWindow(pipApp, testApp)
}
}
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 faf6afc64c69..ff505a04290b 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
@@ -21,12 +21,12 @@ import android.content.Intent
import android.view.Surface
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.flicker.helpers.PipAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.wm.shell.flicker.BaseTest
-import com.android.wm.shell.flicker.helpers.PipAppHelper
-import com.android.wm.shell.flicker.testapp.Components
abstract class PipTransition(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
protected val pipApp = PipAppHelper(instrumentation)
@@ -61,37 +61,23 @@ abstract class PipTransition(testSpec: FlickerTestParameter) : BaseTest(testSpec
*
* @param eachRun If the pip app should be launched in each run (otherwise only 1x per test)
* @param stringExtras Arguments to pass to the PIP launch intent
- * @param extraSpec Addicional segment of flicker specification
+ * @param extraSpec Additional segment of flicker specification
*/
@JvmOverloads
protected open fun buildTransition(
- eachRun: Boolean,
- stringExtras: Map<String, String> = mapOf(Components.PipActivity.EXTRA_ENTER_PIP to "true"),
+ stringExtras: Map<String, String> = mapOf(ActivityOptions.Pip.EXTRA_ENTER_PIP to "true"),
extraSpec: FlickerBuilder.() -> Unit = {}
): FlickerBuilder.() -> Unit {
return {
setup {
setRotation(Surface.ROTATION_0)
removeAllTasksButHome()
-
- if (!eachRun) {
- pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
- }
- if (eachRun) {
- pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
- }
+ pipApp.launchViaIntentAndWaitForPip(wmHelper, stringExtras = stringExtras)
}
teardown {
setRotation(Surface.ROTATION_0)
removeAllTasksButHome()
pipApp.exit(wmHelper)
-
- if (eachRun) {
- pipApp.exit(wmHelper)
- }
- if (!eachRun) {
- pipApp.exit(wmHelper)
- }
}
extraSpec(this)
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 866e4e83ec1e..30332f6c4aaf 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
@@ -31,9 +31,9 @@ import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule.Companion.removeAllTasksButHome
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION
import com.android.wm.shell.flicker.pip.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
-import com.android.wm.shell.flicker.testapp.Components
-import com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -68,7 +68,7 @@ open class SetRequestedOrientationWhilePinnedTest(
pipApp.launchViaIntent(wmHelper, stringExtras = mapOf(
EXTRA_FIXED_ORIENTATION to ORIENTATION_LANDSCAPE.toString()))
// Enter PiP.
- broadcastActionTrigger.doAction(Components.PipActivity.ACTION_ENTER_PIP)
+ broadcastActionTrigger.doAction(ActivityOptions.Pip.ACTION_ENTER_PIP)
// System bar may fade out during fixed rotation.
wmHelper.StateSyncBuilder()
.withPipShown()
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
new file mode 100644
index 000000000000..cdd768abd5bd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipAppHelperTv.kt
@@ -0,0 +1,86 @@
+/*
+ * 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.tv
+
+import android.app.Instrumentation
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+
+/**
+ * Helper class for PIP app on AndroidTV
+ */
+open class PipAppHelperTv(instrumentation: Instrumentation) : PipAppHelper(instrumentation) {
+ private val appSelector = By.pkg(`package`).depth(0)
+
+ val ui: UiObject2?
+ get() = uiDevice.findObject(appSelector)
+
+ private fun focusOnObject(selector: BySelector): Boolean {
+ // We expect all the focusable UI elements to be arranged in a way so that it is possible
+ // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top"
+ // from "the bottom".
+ repeat(FOCUS_ATTEMPTS) {
+ uiDevice.findObject(selector)?.apply { if (isFocusedOrHasFocusedChild) return true }
+ ?: error("The object we try to focus on is gone.")
+
+ uiDevice.pressDPadDown()
+ uiDevice.waitForIdle()
+ }
+ return false
+ }
+
+ override fun clickObject(resId: String) {
+ val selector = By.res(`package`, resId)
+ focusOnObject(selector) || error("Could not focus on `$resId` object")
+ uiDevice.pressDPadCenter()
+ }
+
+ @Deprecated(
+ "Use PipAppHelper.closePipWindow(wmHelper) instead",
+ ReplaceWith("closePipWindow(wmHelper)")
+ )
+ override fun closePipWindow() {
+ uiDevice.closeTvPipWindow()
+ }
+
+ /**
+ * Taps the pip window and dismisses it by clicking on the X button.
+ */
+ override fun closePipWindow(wmHelper: WindowManagerStateHelper) {
+ uiDevice.closeTvPipWindow()
+
+ // Wait for animation to complete.
+ wmHelper.StateSyncBuilder()
+ .withPipGone()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+
+ fun waitUntilClosed(): Boolean {
+ val appSelector = By.pkg(`package`).depth(0)
+ return uiDevice.wait(Until.gone(appSelector), APP_CLOSE_WAIT_TIME_MS)
+ }
+
+ companion object {
+ private const val FOCUS_ATTEMPTS = 20
+ private const val APP_CLOSE_WAIT_TIME_MS = 3_000L
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
index 180ced0a6814..a16f5f6f1620 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/PipTestBase.kt
@@ -21,7 +21,6 @@ import android.content.pm.PackageManager
import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
-import com.android.wm.shell.flicker.helpers.PipAppHelper
import org.junit.Before
import org.junit.runners.Parameterized
@@ -38,7 +37,7 @@ abstract class PipTestBase(
hasSystemFeature(PackageManager.FEATURE_LEANBACK_ONLY)
}
}
- protected val testApp = PipAppHelper(instrumentation)
+ protected val testApp = PipAppHelperTv(instrumentation)
@Before
open fun televisionSetUp() {
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 4be19d61278b..68dbbfb89b6c 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
@@ -19,8 +19,8 @@ package com.android.wm.shell.flicker.pip.tv
import android.graphics.Rect
import androidx.test.filters.RequiresDevice
import androidx.test.uiautomator.UiObject2
+import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
-import com.android.wm.shell.flicker.testapp.Components
import com.android.wm.shell.flicker.wait
import org.junit.Assert.assertNull
import org.junit.Assert.assertTrue
@@ -165,44 +165,44 @@ class TvPipMenuTests : TvPipTestBase() {
enterPip_openMenu_assertShown()
// PiP menu should contain "No-Op", "Off" and "Clear" buttons...
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_NO_OP)
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_NO_OP)
?: fail("\"No-Op\" button should be shown in Pip menu")
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_OFF)
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_OFF)
?: fail("\"Off\" button should be shown in Pip menu")
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_CLEAR)
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
?: fail("\"Clear\" button should be shown in Pip menu")
// ... and should also contain the "Full screen" and "Close" buttons.
assertFullscreenAndCloseButtonsAreShown()
- uiDevice.clickTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_OFF)
+ uiDevice.clickTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_OFF)
// Invoking the "Off" action should replace it with the "On" action/button and should
// remove the "No-Op" action/button. "Clear" action/button should remain in the menu ...
- uiDevice.waitForTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_ON)
+ uiDevice.waitForTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_ON)
?: fail("\"On\" button should be shown in Pip for a corresponding custom action")
assertNull("\"No-Op\" button should not be shown in Pip menu",
uiDevice.findTvPipMenuElementWithDescription(
- Components.PipActivity.MENU_ACTION_NO_OP))
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_CLEAR)
+ ActivityOptions.Pip.MENU_ACTION_NO_OP))
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
?: fail("\"Clear\" button should be shown in Pip menu")
// ... as well as the "Full screen" and "Close" buttons.
assertFullscreenAndCloseButtonsAreShown()
- uiDevice.clickTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_CLEAR)
+ uiDevice.clickTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
// Invoking the "Clear" action should remove all the custom actions and their corresponding
// buttons, ...
uiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(
- Components.PipActivity.MENU_ACTION_ON)?.also {
+ ActivityOptions.Pip.MENU_ACTION_ON)?.also {
isGone -> if (!isGone) fail("\"On\" button should not be shown in Pip menu")
}
assertNull("\"Off\" button should not be shown in Pip menu",
uiDevice.findTvPipMenuElementWithDescription(
- Components.PipActivity.MENU_ACTION_OFF))
+ ActivityOptions.Pip.MENU_ACTION_OFF))
assertNull("\"Clear\" button should not be shown in Pip menu",
uiDevice.findTvPipMenuElementWithDescription(
- Components.PipActivity.MENU_ACTION_CLEAR))
+ ActivityOptions.Pip.MENU_ACTION_CLEAR))
assertNull("\"No-Op\" button should not be shown in Pip menu",
uiDevice.findTvPipMenuElementWithDescription(
- Components.PipActivity.MENU_ACTION_NO_OP))
+ ActivityOptions.Pip.MENU_ACTION_NO_OP))
// ... but the menu should still contain the "Full screen" and "Close" buttons.
assertFullscreenAndCloseButtonsAreShown()
@@ -217,11 +217,11 @@ class TvPipMenuTests : TvPipTestBase() {
enterPip_openMenu_assertShown()
// PiP menu should contain "No-Op", "Off" and "Clear" buttons for the custom actions...
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_NO_OP)
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_NO_OP)
?: fail("\"No-Op\" button should be shown in Pip menu")
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_OFF)
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_OFF)
?: fail("\"Off\" button should be shown in Pip menu")
- uiDevice.findTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_CLEAR)
+ uiDevice.findTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
?: fail("\"Clear\" button should be shown in Pip menu")
// ... should also contain the "Full screen" and "Close" buttons, ...
assertFullscreenAndCloseButtonsAreShown()
@@ -231,7 +231,7 @@ class TvPipMenuTests : TvPipTestBase() {
assertNull("\"Pause\" button should not be shown in menu when there are custom actions",
uiDevice.findTvPipMenuElementWithDescription(pauseButtonDescription))
- uiDevice.clickTvPipMenuElementWithDescription(Components.PipActivity.MENU_ACTION_CLEAR)
+ uiDevice.clickTvPipMenuElementWithDescription(ActivityOptions.Pip.MENU_ACTION_CLEAR)
// Invoking the "Clear" action should remove all the custom actions, which should bring up
// media buttons...
uiDevice.waitForTvPipMenuElementWithDescription(pauseButtonDescription)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
index 3c439fdb9d98..5b044e3fcbc3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/CopyContentInSplit.kt
@@ -27,10 +27,13 @@ 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.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.appWindowKeepVisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerKeepVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsKeepVisible
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,16 +51,16 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
- private val textEditApp = SplitScreenHelper.getIme(instrumentation)
+ private val textEditApp = SplitScreenUtils.getIme(instrumentation)
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, textEditApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, textEditApp)
}
transitions {
- SplitScreenHelper.copyContentInSplit(
+ SplitScreenUtils.copyContentInSplit(
instrumentation, device, primaryApp, textEditApp)
}
}
@@ -65,36 +68,44 @@ class CopyContentInSplit(testSpec: FlickerTestParameter) : SplitScreenBase(testS
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() {
+ testSpec.appWindowIsVisibleAtStart(primaryApp)
+ testSpec.appWindowIsVisibleAtStart(textEditApp)
+ testSpec.splitScreenDividerIsVisibleAtStart()
+
+ testSpec.appWindowIsVisibleAtEnd(primaryApp)
+ testSpec.appWindowIsVisibleAtEnd(textEditApp)
+ testSpec.splitScreenDividerIsVisibleAtEnd()
+
+ // The validation of copied text is already done in SplitScreenUtils.copyContentInSplit()
+ }
+
+ @Presubmit
+ @Test
fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerKeepVisible() = testSpec.layerKeepVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun textEditAppLayerKeepVisible() = testSpec.layerKeepVisible(textEditApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun textEditAppBoundsKeepVisible() = testSpec.splitAppLayerBoundsKeepVisible(
textEditApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun textEditAppWindowKeepVisible() = testSpec.appWindowKeepVisible(textEditApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
index 60e5f78fa724..838026fdc6a6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByDivider.kt
@@ -30,10 +30,10 @@ import com.android.server.wm.flicker.helpers.WindowUtils
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesInvisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesInvisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
+import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
import org.junit.FixMethodOrder
import org.junit.Test
@@ -57,14 +57,14 @@ class DismissSplitScreenByDivider (testSpec: FlickerTestParameter) : SplitScreen
get() = {
super.transition(this)
setup {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
}
transitions {
if (tapl.isTablet) {
- SplitScreenHelper.dragDividerToDismissSplit(device, wmHelper,
+ SplitScreenUtils.dragDividerToDismissSplit(device, wmHelper,
dragToRight = false, dragToBottom = true)
} else {
- SplitScreenHelper.dragDividerToDismissSplit(device, wmHelper,
+ SplitScreenUtils.dragDividerToDismissSplit(device, wmHelper,
dragToRight = true, dragToBottom = true)
}
wmHelper.StateSyncBuilder()
@@ -76,25 +76,25 @@ class DismissSplitScreenByDivider (testSpec: FlickerTestParameter) : SplitScreen
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenDismissed(primaryApp, secondaryApp, toHome = false)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesInvisible() = testSpec.splitScreenDividerBecomesInvisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerBecomesInvisible() = testSpec.layerBecomesInvisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsIsFullscreenAtEnd() {
@@ -117,12 +117,10 @@ class DismissSplitScreenByDivider (testSpec: FlickerTestParameter) : SplitScreen
}
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
index 4e8773f22768..a764206fb33e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DismissSplitScreenByGoHome.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
@@ -27,9 +28,9 @@ 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.appWindowBecomesInvisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesInvisible
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesInvisible
+import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitScreenDividerBecomesInvisible
import org.junit.FixMethodOrder
import org.junit.Test
@@ -55,7 +56,7 @@ class DismissSplitScreenByGoHome(
get() = {
super.transition(this)
setup {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
}
transitions {
tapl.goHome()
@@ -64,40 +65,39 @@ class DismissSplitScreenByGoHome(
.waitForAndVerify()
}
}
-
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenDismissed(primaryApp, secondaryApp, toHome = true)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesInvisible() = testSpec.splitScreenDividerBecomesInvisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerBecomesInvisible() = testSpec.layerBecomesInvisible(primaryApp)
- @IwTest(focusArea = "sysui")
- @Presubmit
+ // TODO(b/245472831): Move back to presubmit after shell transitions landing.
+ @FlakyTest(bugId = 245472831)
@Test
fun secondaryAppLayerBecomesInvisible() = testSpec.layerBecomesInvisible(primaryApp)
- @IwTest(focusArea = "sysui")
- @Presubmit
+ // TODO(b/245472831): Move back to presubmit after shell transitions landing.
+ @FlakyTest(bugId = 245472831)
@Test
fun primaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsBecomesInvisible() = testSpec.splitAppLayerBoundsBecomesInvisible(
secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesInvisible() = testSpec.appWindowBecomesInvisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
index fddd84cfe4db..ba0231755690 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/DragDividerToResize.kt
@@ -28,10 +28,13 @@ 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.SPLIT_SCREEN_DIVIDER_COMPONENT
+import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.appWindowKeepVisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerKeepVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsChanges
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -54,24 +57,37 @@ class DragDividerToResize (testSpec: FlickerTestParameter) : SplitScreenBase(tes
get() = {
super.transition(this)
setup {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
}
transitions {
- SplitScreenHelper.dragDividerToResizeAndWait(device, wmHelper)
+ SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper)
}
}
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() {
+ testSpec.appWindowIsVisibleAtStart(primaryApp)
+ testSpec.appWindowIsVisibleAtStart(secondaryApp)
+ testSpec.splitScreenDividerIsVisibleAtStart()
+
+ testSpec.appWindowIsVisibleAtEnd(primaryApp)
+ testSpec.appWindowIsVisibleAtEnd(secondaryApp)
+ testSpec.splitScreenDividerIsVisibleAtEnd()
+
+ // TODO(b/246490534): Add validation for resized app after withAppTransitionIdle is
+ // robust enough to get the correct end state.
+ }
+
+ @Presubmit
+ @Test
fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerKeepVisible() = testSpec.layerKeepVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerVisibilityChanges() {
@@ -84,23 +100,19 @@ class DragDividerToResize (testSpec: FlickerTestParameter) : SplitScreenBase(tes
}
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowKeepVisible() = testSpec.appWindowKeepVisible(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges(
primaryApp, landscapePosLeft = true, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsChanges() = testSpec.splitAppLayerBoundsChanges(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
index a7c689884e98..bb4478940939 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromAllApps.kt
@@ -30,12 +30,12 @@ import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -76,20 +76,23 @@ class EnterSplitScreenByDragFromAllApps(
.openAllApps()
.getAppIcon(secondaryApp.appName)
.dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
- SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() {
Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.splitScreenDividerBecomesVisible()
}
// TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
@@ -99,12 +102,10 @@ class EnterSplitScreenByDragFromAllApps(
}
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() {
@@ -128,24 +129,20 @@ class EnterSplitScreenByDragFromAllApps(
testSpec.layerBecomesVisible(secondaryApp)
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = false, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisibleByDrag(
secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
index 7d8a8db1a1ee..e208196000fc 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromNotification.kt
@@ -21,8 +21,6 @@ import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
import android.view.WindowManagerPolicyConstants
import androidx.test.filters.RequiresDevice
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
@@ -31,12 +29,12 @@ import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -60,7 +58,7 @@ class EnterSplitScreenByDragFromNotification(
testSpec: FlickerTestParameter
) : SplitScreenBase(testSpec) {
- private val sendNotificationApp = SplitScreenHelper.getSendNotification(instrumentation)
+ private val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
@Before
fun before() {
@@ -74,18 +72,13 @@ class EnterSplitScreenByDragFromNotification(
setup {
// Send a notification
sendNotificationApp.launchViaIntent(wmHelper)
- val sendNotification = device.wait(
- Until.findObject(By.text("Send Notification")),
- SplitScreenHelper.TIMEOUT_MS
- )
- sendNotification?.click() ?: error("Send notification button not found")
-
+ sendNotificationApp.postNotification(wmHelper)
tapl.goHome()
primaryApp.launchViaIntent(wmHelper)
}
transitions {
- SplitScreenHelper.dragFromNotificationToSplit(instrumentation, device, wmHelper)
- SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
+ SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
}
teardown {
sendNotificationApp.exit(wmHelper)
@@ -95,13 +88,16 @@ class EnterSplitScreenByDragFromNotification(
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() {
Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.splitScreenDividerBecomesVisible()
}
// TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
@@ -111,12 +107,10 @@ class EnterSplitScreenByDragFromNotification(
}
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() {
@@ -140,24 +134,20 @@ class EnterSplitScreenByDragFromNotification(
testSpec.layerBecomesVisible(sendNotificationApp)
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = false, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisibleByDrag(
sendNotificationApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(sendNotificationApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
index bfd8a3a53437..84d2e6a42d27 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenByDragFromTaskbar.kt
@@ -30,12 +30,12 @@ import com.android.server.wm.flicker.helpers.isShellTransitionsEnabled
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowBecomesVisible
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisibleByDrag
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
@@ -70,7 +70,7 @@ class EnterSplitScreenByDragFromTaskbar(
super.transition(this)
setup {
tapl.goHome()
- SplitScreenHelper.createShortcutOnHotseatIfNotExist(
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(
tapl, secondaryApp.appName
)
primaryApp.launchViaIntent(wmHelper)
@@ -79,20 +79,23 @@ class EnterSplitScreenByDragFromTaskbar(
tapl.launchedAppState.taskbar
.getAppIcon(secondaryApp.appName)
.dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
- SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() {
Assume.assumeFalse(isShellTransitionsEnabled)
testSpec.splitScreenDividerBecomesVisible()
}
// TODO(b/245472831): Back to splitScreenDividerBecomesVisible after shell transition ready.
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun splitScreenDividerIsVisibleAtEnd_ShellTransit() {
@@ -102,12 +105,10 @@ class EnterSplitScreenByDragFromTaskbar(
}
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() {
@@ -131,24 +132,20 @@ class EnterSplitScreenByDragFromTaskbar(
testSpec.layerBecomesVisible(secondaryApp)
}
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = false, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisibleByDrag(
secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
index cefb9f52d4fe..23623aaf2d00 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/EnterSplitScreenFromOverview.kt
@@ -26,12 +26,12 @@ 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.appWindowBecomesVisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.splitAppLayerBoundsBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -64,44 +64,42 @@ class EnterSplitScreenFromOverview(testSpec: FlickerTestParameter) : SplitScreen
.waitForAndVerify()
}
transitions {
- SplitScreenHelper.splitFromOverview(tapl)
- SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ SplitScreenUtils.splitFromOverview(tapl)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsBecomesVisible() = testSpec.splitAppLayerBoundsBecomesVisible(
secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
index eab473ca55a1..e6d6379e750c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
@@ -21,12 +21,11 @@ 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.wm.shell.flicker.BaseTest
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
abstract class SplitScreenBase(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
protected val context: Context = instrumentation.context
- protected val primaryApp = SplitScreenHelper.getPrimary(instrumentation)
- protected val secondaryApp = SplitScreenHelper.getSecondary(instrumentation)
+ protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
/** {@inheritDoc} */
override val transition: FlickerBuilder.() -> Unit
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
new file mode 100644
index 000000000000..e57ac91617b7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.splitscreen
+
+import android.app.Instrumentation
+import android.graphics.Point
+import android.os.SystemClock
+import android.view.InputDevice
+import android.view.MotionEvent
+import android.view.ViewConfiguration
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.BySelector
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import com.android.launcher3.tapl.LauncherInstrumentation
+import com.android.server.wm.flicker.helpers.ImeAppHelper
+import com.android.server.wm.flicker.helpers.NonResizeableAppHelper
+import com.android.server.wm.flicker.helpers.NotificationAppHelper
+import com.android.server.wm.flicker.helpers.SimpleAppHelper
+import com.android.server.wm.flicker.helpers.StandardAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.IComponentMatcher
+import com.android.server.wm.traces.common.IComponentNameMatcher
+import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
+
+internal object SplitScreenUtils {
+ private const val TIMEOUT_MS = 3_000L
+ private const val DRAG_DURATION_MS = 1_000L
+ private const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
+ private const val DIVIDER_BAR = "docked_divider_handle"
+ private const val GESTURE_STEP_MS = 16L
+ private const val LONG_PRESS_TIME_MS = 100L
+ private val SPLIT_DECOR_MANAGER = ComponentNameMatcher("", "SplitDecorManager#")
+
+ private val notificationScrollerSelector: BySelector
+ get() = By.res(SYSTEM_UI_PACKAGE_NAME, NOTIFICATION_SCROLLER)
+ private val notificationContentSelector: BySelector
+ get() = By.text("Flicker Test Notification")
+ private val dividerBarSelector: BySelector
+ get() = By.res(SYSTEM_UI_PACKAGE_NAME, DIVIDER_BAR)
+
+ fun getPrimary(instrumentation: Instrumentation): StandardAppHelper =
+ SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Primary.LABEL,
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
+ )
+
+ fun getSecondary(instrumentation: Instrumentation): StandardAppHelper =
+ SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Secondary.LABEL,
+ ActivityOptions.SplitScreen.Secondary.COMPONENT.toFlickerComponent()
+ )
+
+ fun getNonResizeable(instrumentation: Instrumentation): NonResizeableAppHelper =
+ NonResizeableAppHelper(instrumentation)
+
+ fun getSendNotification(instrumentation: Instrumentation): NotificationAppHelper =
+ NotificationAppHelper(instrumentation)
+
+ fun getIme(instrumentation: Instrumentation): ImeAppHelper =
+ ImeAppHelper(instrumentation)
+
+ fun waitForSplitComplete(
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: IComponentMatcher,
+ secondaryApp: IComponentMatcher,
+ ) {
+ wmHelper.StateSyncBuilder()
+ .withWindowSurfaceAppeared(primaryApp)
+ .withWindowSurfaceAppeared(secondaryApp)
+ .withSplitDividerVisible()
+ .waitForAndVerify()
+ }
+
+ fun enterSplit(
+ wmHelper: WindowManagerStateHelper,
+ tapl: LauncherInstrumentation,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
+ ) {
+ tapl.workspace.switchToOverview().dismissAllTasks()
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper.StateSyncBuilder()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ splitFromOverview(tapl)
+ waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ fun splitFromOverview(tapl: LauncherInstrumentation) {
+ // Note: The initial split position in landscape is different between tablet and phone.
+ // In landscape, tablet will let the first app split to right side, and phone will
+ // split to left side.
+ if (tapl.isTablet) {
+ tapl.workspace.switchToOverview().overviewActions
+ .clickSplit()
+ .currentTask
+ .open()
+ } else {
+ tapl.workspace.switchToOverview().currentTask
+ .tapMenu()
+ .tapSplitMenuItem()
+ .currentTask
+ .open()
+ }
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
+ fun dragFromNotificationToSplit(
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper
+ ) {
+ val displayBounds = wmHelper.currentState.layerState
+ .displays.firstOrNull { !it.isVirtual }
+ ?.layerStackSpace
+ ?: error("Display not found")
+
+ // Pull down the notifications
+ device.swipe(
+ displayBounds.centerX(), 5,
+ displayBounds.centerX(), displayBounds.bottom, 50 /* steps */
+ )
+ SystemClock.sleep(TIMEOUT_MS)
+
+ // Find the target notification
+ val notificationScroller = device.wait(
+ Until.findObject(notificationScrollerSelector), TIMEOUT_MS
+ ) ?: error ("Unable to find view $notificationScrollerSelector")
+ var notificationContent = notificationScroller.findObject(notificationContentSelector)
+
+ while (notificationContent == null) {
+ device.swipe(
+ displayBounds.centerX(), displayBounds.centerY(),
+ displayBounds.centerX(), displayBounds.centerY() - 150, 20 /* steps */
+ )
+ notificationContent = notificationScroller.findObject(notificationContentSelector)
+ }
+
+ // Drag to split
+ val dragStart = notificationContent.visibleCenter
+ val dragMiddle = Point(dragStart.x + 50, dragStart.y)
+ val dragEnd = Point(displayBounds.width / 4, displayBounds.width / 4)
+ val downTime = SystemClock.uptimeMillis()
+
+ touch(
+ instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime,
+ TIMEOUT_MS, dragStart
+ )
+ // It needs a horizontal movement to trigger the drag
+ touchMove(
+ instrumentation, downTime, SystemClock.uptimeMillis(),
+ DRAG_DURATION_MS, dragStart, dragMiddle
+ )
+ touchMove(
+ instrumentation, downTime, SystemClock.uptimeMillis(),
+ DRAG_DURATION_MS, dragMiddle, dragEnd
+ )
+ // Wait for a while to start splitting
+ SystemClock.sleep(TIMEOUT_MS)
+ touch(
+ instrumentation, MotionEvent.ACTION_UP, downTime, SystemClock.uptimeMillis(),
+ GESTURE_STEP_MS, dragEnd
+ )
+ SystemClock.sleep(TIMEOUT_MS)
+ }
+
+ fun touch(
+ instrumentation: Instrumentation,
+ action: Int,
+ downTime: Long,
+ eventTime: Long,
+ duration: Long,
+ point: Point
+ ) {
+ val motionEvent = MotionEvent.obtain(
+ downTime, eventTime, action, point.x.toFloat(), point.y.toFloat(), 0
+ )
+ motionEvent.source = InputDevice.SOURCE_TOUCHSCREEN
+ instrumentation.uiAutomation.injectInputEvent(motionEvent, true)
+ motionEvent.recycle()
+ SystemClock.sleep(duration)
+ }
+
+ fun touchMove(
+ instrumentation: Instrumentation,
+ downTime: Long,
+ eventTime: Long,
+ duration: Long,
+ from: Point,
+ to: Point
+ ) {
+ val steps: Long = duration / GESTURE_STEP_MS
+ var currentTime = eventTime
+ var currentX = from.x.toFloat()
+ var currentY = from.y.toFloat()
+ val stepX = (to.x.toFloat() - from.x.toFloat()) / steps.toFloat()
+ val stepY = (to.y.toFloat() - from.y.toFloat()) / steps.toFloat()
+
+ for (i in 1..steps) {
+ val motionMove = MotionEvent.obtain(
+ downTime, currentTime, MotionEvent.ACTION_MOVE, currentX, currentY, 0
+ )
+ motionMove.source = InputDevice.SOURCE_TOUCHSCREEN
+ instrumentation.uiAutomation.injectInputEvent(motionMove, true)
+ motionMove.recycle()
+
+ currentTime += GESTURE_STEP_MS
+ if (i == steps - 1) {
+ currentX = to.x.toFloat()
+ currentY = to.y.toFloat()
+ } else {
+ currentX += stepX
+ currentY += stepY
+ }
+ SystemClock.sleep(GESTURE_STEP_MS)
+ }
+ }
+
+ fun longPress(
+ instrumentation: Instrumentation,
+ point: Point
+ ) {
+ val downTime = SystemClock.uptimeMillis()
+ touch(instrumentation, MotionEvent.ACTION_DOWN, downTime, downTime, TIMEOUT_MS, point)
+ SystemClock.sleep(LONG_PRESS_TIME_MS)
+ touch(instrumentation, MotionEvent.ACTION_UP, downTime, downTime, TIMEOUT_MS, point)
+ }
+
+ fun createShortcutOnHotseatIfNotExist(
+ tapl: LauncherInstrumentation,
+ appName: String
+ ) {
+ tapl.workspace.deleteAppIcon(tapl.workspace.getHotseatAppIcon(0))
+ val allApps = tapl.workspace.switchToAllApps()
+ allApps.freeze()
+ try {
+ allApps.getAppIcon(appName).dragToHotseat(0)
+ } finally {
+ allApps.unfreeze()
+ }
+ }
+
+ fun dragDividerToResizeAndWait(
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper
+ ) {
+ val displayBounds = wmHelper.currentState.layerState
+ .displays.firstOrNull { !it.isVirtual }
+ ?.layerStackSpace
+ ?: error("Display not found")
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ dividerBar.drag(Point(displayBounds.width * 1 / 3, displayBounds.height * 2 / 3))
+
+ wmHelper.StateSyncBuilder()
+ .withWindowSurfaceDisappeared(SPLIT_DECOR_MANAGER)
+ .waitForAndVerify()
+ }
+
+ fun dragDividerToDismissSplit(
+ device: UiDevice,
+ wmHelper: WindowManagerStateHelper,
+ dragToRight: Boolean,
+ dragToBottom: Boolean
+ ) {
+ val displayBounds = wmHelper.currentState.layerState
+ .displays.firstOrNull { !it.isVirtual }
+ ?.layerStackSpace
+ ?: error("Display not found")
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ dividerBar.drag(Point(
+ if (dragToRight) {
+ displayBounds.width * 4 / 5
+ } else {
+ displayBounds.width * 1 / 5
+ },
+ if (dragToBottom) {
+ displayBounds.height * 4 / 5
+ } else {
+ displayBounds.height * 1 / 5
+ }))
+ }
+
+ fun doubleTapDividerToSwitch(device: UiDevice) {
+ val dividerBar = device.wait(Until.findObject(dividerBarSelector), TIMEOUT_MS)
+ val interval = (ViewConfiguration.getDoubleTapTimeout() +
+ ViewConfiguration.getDoubleTapMinTime()) / 2
+ dividerBar.click()
+ SystemClock.sleep(interval.toLong())
+ dividerBar.click()
+ }
+
+ fun copyContentInSplit(
+ instrumentation: Instrumentation,
+ device: UiDevice,
+ sourceApp: IComponentNameMatcher,
+ destinationApp: IComponentNameMatcher,
+ ) {
+ // Copy text from sourceApp
+ val textView = device.wait(Until.findObject(
+ By.res(sourceApp.packageName, "SplitScreenTest")), TIMEOUT_MS)
+ longPress(instrumentation, textView.visibleCenter)
+
+ val copyBtn = device.wait(Until.findObject(By.text("Copy")), TIMEOUT_MS)
+ copyBtn.click()
+
+ // Paste text to destinationApp
+ val editText = device.wait(Until.findObject(
+ By.res(destinationApp.packageName, "plain_text_input")), TIMEOUT_MS)
+ longPress(instrumentation, editText.visibleCenter)
+
+ val pasteBtn = device.wait(Until.findObject(By.text("Paste")), TIMEOUT_MS)
+ pasteBtn.click()
+
+ // Verify text
+ if (!textView.text.contentEquals(editText.text)) {
+ error("Fail to copy content in split")
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
index e174f5ef7917..025bb4085624 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchAppByDoubleTapDivider.kt
@@ -16,6 +16,7 @@
package com.android.wm.shell.flicker.splitscreen
+import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.IwTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
@@ -26,12 +27,16 @@ 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.helpers.isRotated
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.SPLIT_SCREEN_DIVIDER_COMPONENT
import com.android.wm.shell.flicker.appWindowIsVisibleAtEnd
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import com.android.wm.shell.flicker.appWindowIsVisibleAtStart
import com.android.wm.shell.flicker.layerIsVisibleAtEnd
import com.android.wm.shell.flicker.layerKeepVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtEnd
+import com.android.wm.shell.flicker.splitScreenDividerIsVisibleAtStart
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,55 +53,108 @@ import org.junit.runners.Parameterized
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
-class SwitchAppByDoubleTapDivider (testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
+class SwitchAppByDoubleTapDivider(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
}
transitions {
- SplitScreenHelper.doubleTapDividerToSwitch(device)
+ SplitScreenUtils.doubleTapDividerToSwitch(device)
wmHelper.StateSyncBuilder()
.withAppTransitionIdle()
.waitForAndVerify()
+
+ waitForLayersToSwitch(wmHelper)
+ waitForWindowsToSwitch(wmHelper)
}
}
+ private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper.StateSyncBuilder().add("appWindowsSwitched") {
+ val primaryAppWindow = it.wmState.visibleWindows.firstOrNull { window ->
+ primaryApp.windowMatchesAnyOf(window)
+ } ?: return@add false
+ val secondaryAppWindow = it.wmState.visibleWindows.firstOrNull { window ->
+ secondaryApp.windowMatchesAnyOf(window)
+ } ?: return@add false
+
+ if (testSpec.startRotation.isRotated()) {
+ return@add primaryAppWindow.frame.right <= secondaryAppWindow.frame.left
+ } else {
+ return@add primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ }
+ }.waitForAndVerify()
+ }
+
+ private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper.StateSyncBuilder().add("appLayersSwitched") {
+ val primaryAppLayer = it.layerState.visibleLayers.firstOrNull { window ->
+ primaryApp.layerMatchesAnyOf(window)
+ } ?: return@add false
+ val secondaryAppLayer = it.layerState.visibleLayers.firstOrNull { window ->
+ secondaryApp.layerMatchesAnyOf(window)
+ } ?: return@add false
+
+ val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds
+ ?: return@add false
+ val secondaryVisibleRegion = secondaryAppLayer.visibleRegion?.bounds
+ ?: return@add false
+
+ if (testSpec.startRotation.isRotated()) {
+ return@add primaryVisibleRegion.right <= secondaryVisibleRegion.left
+ } else {
+ return@add primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ }
+ }.waitForAndVerify()
+ }
+
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() {
+ testSpec.appWindowIsVisibleAtStart(primaryApp)
+ testSpec.appWindowIsVisibleAtStart(secondaryApp)
+ testSpec.splitScreenDividerIsVisibleAtStart()
+
+ testSpec.appWindowIsVisibleAtEnd(primaryApp)
+ testSpec.appWindowIsVisibleAtEnd(secondaryApp)
+ testSpec.splitScreenDividerIsVisibleAtEnd()
+
+ // TODO(b/246490534): Add validation for switched app after withAppTransitionIdle is
+ // robust enough to get the correct end state.
+ }
+
+ @Presubmit
+ @Test
fun splitScreenDividerKeepVisible() = testSpec.layerKeepVisible(SPLIT_SCREEN_DIVIDER_COMPONENT)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerIsVisibleAtEnd() = testSpec.layerIsVisibleAtEnd(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
- @Presubmit
+ // TODO(b/246490534): Move back to presubmit after withAppTransitionIdle is robust enough to
+ // get the correct end state.
+ @FlakyTest(bugId = 246490534)
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
secondaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowIsVisibleAtEnd() = testSpec.appWindowIsVisibleAtEnd(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
index 20c6af7c0530..9947a53dda42 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromAnotherApp.kt
@@ -27,10 +27,10 @@ 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.appWindowBecomesVisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -48,13 +48,13 @@ import org.junit.runners.Parameterized
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@Group1
class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScreenBase(testSpec) {
- val thirdApp = SplitScreenHelper.getNonResizeable(instrumentation)
+ val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
override val transition: FlickerBuilder.() -> Unit
get() = {
super.transition(this)
setup {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
thirdApp.launchViaIntent(wmHelper)
wmHelper.StateSyncBuilder()
@@ -63,43 +63,41 @@ class SwitchBackToSplitFromAnotherApp(testSpec: FlickerTestParameter) : SplitScr
}
transitions {
tapl.launchedAppState.quickSwitchToPreviousApp()
- SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
index cb9ca9f13849..3716dc98714c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromHome.kt
@@ -27,10 +27,10 @@ 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.appWindowBecomesVisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,7 +53,7 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas
get() = {
super.transition(this)
setup {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
tapl.goHome()
wmHelper.StateSyncBuilder()
@@ -62,43 +62,41 @@ class SwitchBackToSplitFromHome(testSpec: FlickerTestParameter) : SplitScreenBas
}
transitions {
tapl.workspace.quickSwitchToPreviousApp()
- SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
index 266276749c13..db07f2132e95 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SwitchBackToSplitFromRecent.kt
@@ -27,10 +27,10 @@ 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.appWindowBecomesVisible
-import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import com.android.wm.shell.flicker.layerBecomesVisible
import com.android.wm.shell.flicker.splitAppLayerBoundsIsVisibleAtEnd
import com.android.wm.shell.flicker.splitScreenDividerBecomesVisible
+import com.android.wm.shell.flicker.splitScreenEntered
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -53,7 +53,7 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB
get() = {
super.transition(this)
setup {
- SplitScreenHelper.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, primaryApp, secondaryApp)
tapl.goHome()
wmHelper.StateSyncBuilder()
@@ -64,43 +64,41 @@ class SwitchBackToSplitFromRecent(testSpec: FlickerTestParameter) : SplitScreenB
tapl.workspace.switchToOverview()
.currentTask
.open()
- SplitScreenHelper.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
}
@IwTest(focusArea = "sysui")
@Presubmit
@Test
+ fun cujCompleted() = testSpec.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = true)
+
+ @Presubmit
+ @Test
fun splitScreenDividerBecomesVisible() = testSpec.splitScreenDividerBecomesVisible()
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppLayerBecomesVisible() = testSpec.layerBecomesVisible(secondaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
primaryApp, landscapePosLeft = tapl.isTablet, portraitPosTop = false)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppBoundsIsVisibleAtEnd() = testSpec.splitAppLayerBoundsIsVisibleAtEnd(
secondaryApp, landscapePosLeft = !tapl.isTablet, portraitPosTop = true)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun primaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(primaryApp)
- @IwTest(focusArea = "sysui")
@Presubmit
@Test
fun secondaryAppWindowBecomesVisible() = testSpec.appWindowBecomesVisible(secondaryApp)
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
deleted file mode 100644
index ea606df1536d..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/Android.bp
+++ /dev/null
@@ -1,35 +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 {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test {
- name: "WMShellFlickerTestApp",
- srcs: ["**/*.java"],
- sdk_version: "current",
- test_suites: ["device-tests"],
-}
-
-java_library {
- name: "wmshell-flicker-test-components",
- srcs: ["src/**/Components.java"],
- sdk_version: "test_current",
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
deleted file mode 100644
index bc0b0b6292b4..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/AndroidManifest.xml
+++ /dev/null
@@ -1,147 +0,0 @@
-<?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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.wm.shell.flicker.testapp">
-
- <uses-sdk android:minSdkVersion="29"
- android:targetSdkVersion="29"/>
- <application android:allowBackup="false"
- android:supportsRtl="true">
- <activity android:name=".FixedActivity"
- android:resizeableActivity="true"
- android:supportsPictureInPicture="true"
- android:launchMode="singleTop"
- android:theme="@style/CutoutShortEdges"
- android:label="FixedApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <activity android:name=".PipActivity"
- android:resizeableActivity="true"
- android:supportsPictureInPicture="true"
- android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.PipActivity"
- android:theme="@style/CutoutShortEdges"
- android:launchMode="singleTop"
- android:label="PipApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".ImeActivity"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.ImeActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="ImeApp"
- android:launchMode="singleTop"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".SplitScreenActivity"
- android:resizeableActivity="true"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="SplitScreenPrimaryApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".SplitScreenSecondaryActivity"
- android:resizeableActivity="true"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.SplitScreenSecondaryActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="SplitScreenSecondaryApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".SendNotificationActivity"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.SendNotificationActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="SendNotificationApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".NonResizeableActivity"
- android:resizeableActivity="false"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.NonResizeableActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="NonResizeableApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
-
- <activity android:name=".SimpleActivity"
- android:taskAffinity="com.android.wm.shell.flicker.testapp.SimpleActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="SimpleApp"
- android:exported="true">
- <intent-filter>
- <action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <activity
- android:name=".LaunchBubbleActivity"
- android:label="LaunchBubbleApp"
- android:exported="true"
- android:theme="@style/CutoutShortEdges"
- android:launchMode="singleTop">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <action android:name="android.intent.action.VIEW" />
- <category android:name="android.intent.category.LAUNCHER"/>
- </intent-filter>
- </activity>
- <activity
- android:name=".BubbleActivity"
- android:label="BubbleApp"
- android:exported="false"
- android:theme="@style/CutoutShortEdges"
- android:resizeableActivity="true" />
- </application>
-</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml
deleted file mode 100644
index 4708cfd48381..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_ime.xml
+++ /dev/null
@@ -1,27 +0,0 @@
-<?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:focusableInTouchMode="true"
- android:background="@android:color/holo_green_light">
- <EditText android:id="@+id/plain_text_input"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:inputType="text"/>
-</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml
deleted file mode 100644
index 45d5917f86d6..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_non_resizeable.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-<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>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml
deleted file mode 100644
index 8d59b567e59b..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_notification.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?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.
--->
-<RelativeLayout 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/black">
-
- <Button
- android:id="@+id/button_send_notification"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_centerVertical="true"
- android:text="Send Notification" />
-</RelativeLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_simple.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_simple.xml
deleted file mode 100644
index 5d94e5177dcc..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_simple.xml
+++ /dev/null
@@ -1,23 +0,0 @@
-<?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">
-
-</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
deleted file mode 100644
index 674bb70ad01e..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- 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.
--->
-<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_blue_light">
-
- <TextView
- android:id="@+id/SplitScreenTest"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:gravity="center_vertical|center_horizontal"
- android:text="SecondaryActivity"
- android:textAppearance="?android:attr/textAppearanceLarge"/>
-
-</LinearLayout>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java
deleted file mode 100644
index a2b580da5898..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/Components.java
+++ /dev/null
@@ -1,108 +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.testapp;
-
-import android.content.ComponentName;
-
-public class Components {
- public static final String PACKAGE_NAME = "com.android.wm.shell.flicker.testapp";
-
- public static class SimpleActivity {
- public static final String LABEL = "SimpleApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".SimpleActivity");
- }
-
- public static class FixedActivity {
- public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
- public static final String LABEL = "FixedApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".FixedActivity");
- }
-
- public static class NonResizeableActivity {
- public static final String LABEL = "NonResizeableApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".NonResizeableActivity");
- }
-
- public static class PipActivity {
- // Test App > Pip Activity
- public static final String LABEL = "PipApp";
- public static final String MENU_ACTION_NO_OP = "No-Op";
- public static final String MENU_ACTION_ON = "On";
- public static final String MENU_ACTION_OFF = "Off";
- public static final String MENU_ACTION_CLEAR = "Clear";
-
- // Intent action that this activity dynamically registers to enter picture-in-picture
- public static final String ACTION_ENTER_PIP = PACKAGE_NAME + ".PipActivity.ENTER_PIP";
- // Intent action that this activity dynamically registers to set requested orientation.
- // Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra.
- public static final String ACTION_SET_REQUESTED_ORIENTATION =
- PACKAGE_NAME + ".PipActivity.SET_REQUESTED_ORIENTATION";
-
- // Calls enterPictureInPicture() on creation
- public static final String EXTRA_ENTER_PIP = "enter_pip";
- // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
- public static final String EXTRA_PIP_ORIENTATION = "fixed_orientation";
- // Adds a click listener to finish this activity when it is clicked
- public static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
-
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".PipActivity");
- }
-
- public static class ImeActivity {
- public static final String LABEL = "ImeApp";
- public static final String ACTION_CLOSE_IME =
- PACKAGE_NAME + ".action.CLOSE_IME";
- public static final String ACTION_OPEN_IME =
- PACKAGE_NAME + ".action.OPEN_IME";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".ImeActivity");
- }
-
- public static class SplitScreenActivity {
- public static final String LABEL = "SplitScreenPrimaryApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".SplitScreenActivity");
- }
-
- public static class SplitScreenSecondaryActivity {
- public static final String LABEL = "SplitScreenSecondaryApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".SplitScreenSecondaryActivity");
- }
-
- public static class SendNotificationActivity {
- public static final String LABEL = "SendNotificationApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".SendNotificationActivity");
- }
-
- public static class LaunchBubbleActivity {
- public static final String LABEL = "LaunchBubbleApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".LaunchBubbleActivity");
- }
-
- public static class BubbleActivity {
- public static final String LABEL = "BubbleApp";
- public static final ComponentName COMPONENT = new ComponentName(PACKAGE_NAME,
- PACKAGE_NAME + ".BubbleActivity");
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java
deleted file mode 100644
index 59c64a1345ab..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/ImeActivity.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.wm.shell.flicker.testapp;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-import android.view.WindowManager;
-import android.view.inputmethod.InputMethodManager;
-
-public class ImeActivity extends Activity {
- private static final String ACTION_OPEN_IME =
- "com.android.wm.shell.flicker.testapp.action.OPEN_IME";
- private static final String ACTION_CLOSE_IME =
- "com.android.wm.shell.flicker.testapp.action.CLOSE_IME";
-
- private InputMethodManager mImm;
- private View mEditText;
-
- @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_ime);
-
- mEditText = findViewById(R.id.plain_text_input);
- mImm = getSystemService(InputMethodManager.class);
-
- handleIntent(getIntent());
- }
-
- @Override
- protected void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- handleIntent(intent);
- }
-
- private void handleIntent(Intent intent) {
- final String action = intent.getAction();
- if (ACTION_OPEN_IME.equals(action)) {
- mEditText.requestFocus();
- mImm.showSoftInput(mEditText, InputMethodManager.SHOW_FORCED);
- } else if (ACTION_CLOSE_IME.equals(action)) {
- mImm.hideSoftInputFromWindow(mEditText.getWindowToken(), 0);
- mEditText.clearFocus();
- }
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java
deleted file mode 100644
index 8020ef2270a0..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SendNotificationActivity.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.testapp;
-
-import android.app.Activity;
-import android.app.Notification;
-import android.app.NotificationChannel;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.View;
-
-public class SendNotificationActivity extends Activity {
- private NotificationManager mNotificationManager;
- private String mChannelId = "Channel id";
- private String mChannelName = "Channel name";
- private NotificationChannel mChannel;
- private int mNotifyId = 0;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_notification);
- findViewById(R.id.button_send_notification).setOnClickListener(this::sendNotification);
-
- mChannel = new NotificationChannel(mChannelId, mChannelName,
- NotificationManager.IMPORTANCE_DEFAULT);
- mNotificationManager = getSystemService(NotificationManager.class);
- mNotificationManager.createNotificationChannel(mChannel);
- }
-
- private void sendNotification(View v) {
- PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
- new Intent(this, SendNotificationActivity.class),
- PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
- Notification notification = new Notification.Builder(this, mChannelId)
- .setContentTitle("Notification App")
- .setContentText("Notification content")
- .setWhen(System.currentTimeMillis())
- .setSmallIcon(R.drawable.ic_message)
- .setContentIntent(pendingIntent)
- .build();
-
- mNotificationManager.notify(mNotifyId, notification);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SimpleActivity.java b/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SimpleActivity.java
deleted file mode 100644
index 5343c1893d4e..000000000000
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SimpleActivity.java
+++ /dev/null
@@ -1,33 +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.wm.shell.flicker.testapp;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-public class SimpleActivity 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_simple);
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
index c0720cf04028..3672ae386dc4 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java
@@ -44,6 +44,7 @@ public final class TestRunningTaskInfoBuilder {
private ActivityManager.TaskDescription.Builder mTaskDescriptionBuilder = null;
private final Point mPositionInParent = new Point();
private boolean mIsVisible = false;
+ private long mLastActiveTime;
public static WindowContainerToken createMockWCToken() {
final IWindowContainerToken itoken = mock(IWindowContainerToken.class);
@@ -52,6 +53,11 @@ public final class TestRunningTaskInfoBuilder {
return new WindowContainerToken(itoken);
}
+ public TestRunningTaskInfoBuilder setToken(WindowContainerToken token) {
+ mToken = token;
+ return this;
+ }
+
public TestRunningTaskInfoBuilder setBounds(Rect bounds) {
mBounds.set(bounds);
return this;
@@ -95,6 +101,11 @@ public final class TestRunningTaskInfoBuilder {
return this;
}
+ public TestRunningTaskInfoBuilder setLastActiveTime(long lastActiveTime) {
+ mLastActiveTime = lastActiveTime;
+ return this;
+ }
+
public ActivityManager.RunningTaskInfo build() {
final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo();
info.taskId = sNextTaskId++;
@@ -110,6 +121,7 @@ public final class TestRunningTaskInfoBuilder {
mTaskDescriptionBuilder != null ? mTaskDescriptionBuilder.build() : null;
info.positionInParent = mPositionInParent;
info.isVisible = mIsVisible;
+ info.lastActiveTime = mLastActiveTime;
return info;
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
index b2e45a6b3a5c..98b59126227c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationRunnerTests.java
@@ -17,8 +17,10 @@
package com.android.wm.shell.activityembedding;
import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
+import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
@@ -27,6 +29,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.animation.Animator;
import android.window.TransitionInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -57,7 +60,7 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
public void testStartAnimation() {
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
final TransitionInfo.Change embeddingChange = createChange();
- embeddingChange.setFlags(FLAG_IS_EMBEDDED);
+ embeddingChange.setFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
info.addChange(embeddingChange);
doReturn(mAnimator).when(mAnimRunner).createAnimator(any(), any(), any(), any());
@@ -76,4 +79,18 @@ public class ActivityEmbeddingAnimationRunnerTests extends ActivityEmbeddingAnim
verify(mController).onAnimationFinished(mTransition);
}
+
+ @Test
+ public void testChangesBehindStartingWindow() {
+ final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
+ final TransitionInfo.Change embeddingChange = createChange();
+ embeddingChange.setFlags(FLAG_IS_BEHIND_STARTING_WINDOW);
+ info.addChange(embeddingChange);
+ final Animator animator = mAnimRunner.createAnimator(
+ info, mStartTransaction, mFinishTransaction,
+ () -> mFinishCallback.onTransitionFinished(null /* wct */, null /* wctCB */));
+
+ // The animation should be empty when it is behind starting window.
+ assertEquals(0, animator.getDuration());
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
index 84befdddabdb..3792e8361284 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingAnimationTestBase.java
@@ -16,6 +16,9 @@
package com.android.wm.shell.activityembedding;
+import static android.window.TransitionInfo.FLAG_FILLS_TASK;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.junit.Assert.assertNotNull;
@@ -24,6 +27,8 @@ import static org.mockito.Mockito.mock;
import android.animation.Animator;
import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.graphics.Rect;
import android.os.IBinder;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
@@ -80,4 +85,23 @@ abstract class ActivityEmbeddingAnimationTestBase extends ShellTestCase {
return new TransitionInfo.Change(mock(WindowContainerToken.class),
mock(SurfaceControl.class));
}
+
+ /**
+ * Creates a mock {@link TransitionInfo.Change} with
+ * {@link TransitionInfo#FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY} flag.
+ */
+ static TransitionInfo.Change createEmbeddedChange(@NonNull Rect startBounds,
+ @NonNull Rect endBounds, @NonNull Rect taskBounds) {
+ final TransitionInfo.Change change = createChange();
+ change.setFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
+ change.setStartAbsBounds(startBounds);
+ change.setEndAbsBounds(endBounds);
+ if (taskBounds.width() == startBounds.width()
+ && taskBounds.height() == startBounds.height()
+ && taskBounds.width() == endBounds.width()
+ && taskBounds.height() == endBounds.height()) {
+ change.setFlags(FLAG_FILLS_TASK);
+ }
+ return change;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index cf43b0030d2a..baecf6fe6673 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -17,7 +17,6 @@
package com.android.wm.shell.activityembedding;
import static android.view.WindowManager.TRANSIT_OPEN;
-import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -29,6 +28,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import android.graphics.Rect;
import android.window.TransitionInfo;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -48,6 +48,10 @@ import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimationTestBase {
+ private static final Rect TASK_BOUNDS = new Rect(0, 0, 1000, 500);
+ private static final Rect EMBEDDED_LEFT_BOUNDS = new Rect(0, 0, 500, 500);
+ private static final Rect EMBEDDED_RIGHT_BOUNDS = new Rect(500, 0, 1000, 500);
+
@Before
public void setup() {
super.setUp();
@@ -77,13 +81,13 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
@Test
public void testStartAnimation_containsNonActivityEmbeddingChange() {
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
- final TransitionInfo.Change embeddingChange = createChange();
- embeddingChange.setFlags(FLAG_IS_EMBEDDED);
+ final TransitionInfo.Change embeddingChange = createEmbeddedChange(EMBEDDED_LEFT_BOUNDS,
+ EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS);
final TransitionInfo.Change nonEmbeddingChange = createChange();
info.addChange(embeddingChange);
info.addChange(nonEmbeddingChange);
- // No-op
+ // No-op because it contains non-embedded change.
assertFalse(mController.startAnimation(mTransition, info, mStartTransaction,
mFinishTransaction, mFinishCallback));
verify(mAnimRunner, never()).startAnimation(any(), any(), any(), any());
@@ -93,13 +97,65 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
}
@Test
- public void testStartAnimation_onlyActivityEmbeddingChange() {
+ public void testStartAnimation_containsOnlyFillTaskActivityEmbeddingChange() {
+ final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
+ final TransitionInfo.Change embeddingChange = createEmbeddedChange(TASK_BOUNDS, TASK_BOUNDS,
+ TASK_BOUNDS);
+ info.addChange(embeddingChange);
+
+ // No-op because it only contains embedded change that fills the Task. We will let the
+ // default handler to animate such transition.
+ assertFalse(mController.startAnimation(mTransition, info, mStartTransaction,
+ mFinishTransaction, mFinishCallback));
+ verify(mAnimRunner, never()).startAnimation(any(), any(), any(), any());
+ verifyNoMoreInteractions(mStartTransaction);
+ verifyNoMoreInteractions(mFinishTransaction);
+ verifyNoMoreInteractions(mFinishCallback);
+ }
+
+ @Test
+ public void testStartAnimation_containsActivityEmbeddingSplitChange() {
+ // Change that occupies only part of the Task.
+ final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
+ final TransitionInfo.Change embeddingChange = createEmbeddedChange(EMBEDDED_LEFT_BOUNDS,
+ EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS);
+ info.addChange(embeddingChange);
+
+ // ActivityEmbeddingController will handle such transition.
+ assertTrue(mController.startAnimation(mTransition, info, mStartTransaction,
+ mFinishTransaction, mFinishCallback));
+ verify(mAnimRunner).startAnimation(mTransition, info, mStartTransaction,
+ mFinishTransaction);
+ verify(mStartTransaction).apply();
+ verifyNoMoreInteractions(mFinishTransaction);
+ }
+
+ @Test
+ public void testStartAnimation_containsChangeEnterActivityEmbeddingSplit() {
+ // Change that is entering ActivityEmbedding split.
+ final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
+ final TransitionInfo.Change embeddingChange = createEmbeddedChange(TASK_BOUNDS,
+ EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS);
+ info.addChange(embeddingChange);
+
+ // ActivityEmbeddingController will handle such transition.
+ assertTrue(mController.startAnimation(mTransition, info, mStartTransaction,
+ mFinishTransaction, mFinishCallback));
+ verify(mAnimRunner).startAnimation(mTransition, info, mStartTransaction,
+ mFinishTransaction);
+ verify(mStartTransaction).apply();
+ verifyNoMoreInteractions(mFinishTransaction);
+ }
+
+ @Test
+ public void testStartAnimation_containsChangeExitActivityEmbeddingSplit() {
+ // Change that is exiting ActivityEmbedding split.
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
- final TransitionInfo.Change embeddingChange = createChange();
- embeddingChange.setFlags(FLAG_IS_EMBEDDED);
+ final TransitionInfo.Change embeddingChange = createEmbeddedChange(EMBEDDED_RIGHT_BOUNDS,
+ TASK_BOUNDS, TASK_BOUNDS);
info.addChange(embeddingChange);
- // No-op
+ // ActivityEmbeddingController will handle such transition.
assertTrue(mController.startAnimation(mTransition, info, mStartTransaction,
mFinishTransaction, mFinishCallback));
verify(mAnimRunner).startAnimation(mTransition, info, mStartTransaction,
@@ -115,8 +171,8 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation
() -> mController.onAnimationFinished(mTransition));
final TransitionInfo info = new TransitionInfo(TRANSIT_OPEN, 0);
- final TransitionInfo.Change embeddingChange = createChange();
- embeddingChange.setFlags(FLAG_IS_EMBEDDED);
+ final TransitionInfo.Change embeddingChange = createEmbeddedChange(EMBEDDED_LEFT_BOUNDS,
+ EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS);
info.addChange(embeddingChange);
mController.startAnimation(mTransition, info, mStartTransaction,
mFinishTransaction, mFinishCallback);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
index ef532e449bd6..dd23d97d9199 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeControllerTest.java
@@ -19,31 +19,43 @@ package com.android.wm.shell.desktopmode;
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.app.WindowConfiguration.WINDOW_CONFIG_BOUNDS;
+import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+
+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.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.app.WindowConfiguration;
+import android.app.ActivityManager;
import android.os.Handler;
import android.os.IBinder;
import android.testing.AndroidTestingRunner;
+import android.window.DisplayAreaInfo;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import android.window.WindowContainerTransaction.Change;
import androidx.test.filters.SmallTest;
-import com.android.wm.shell.RootDisplayAreaOrganizer;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TestRunningTaskInfoBuilder;
+import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.sysui.ShellInit;
+import com.android.wm.shell.transition.Transitions;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -58,23 +70,45 @@ public class DesktopModeControllerTest extends ShellTestCase {
@Mock
private ShellTaskOrganizer mShellTaskOrganizer;
@Mock
- private RootDisplayAreaOrganizer mRootDisplayAreaOrganizer;
+ private RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer;
@Mock
private ShellExecutor mTestExecutor;
@Mock
private Handler mMockHandler;
+ @Mock
+ private Transitions mMockTransitions;
+ private TestShellExecutor mExecutor;
private DesktopModeController mController;
+ private DesktopModeTaskRepository mDesktopModeTaskRepository;
private ShellInit mShellInit;
+ private StaticMockitoSession mMockitoSession;
@Before
public void setUp() {
+ mMockitoSession = mockitoSession().mockStatic(DesktopModeStatus.class).startMocking();
+ when(DesktopModeStatus.isActive(any())).thenReturn(true);
+
mShellInit = Mockito.spy(new ShellInit(mTestExecutor));
+ mExecutor = new TestShellExecutor();
+
+ mDesktopModeTaskRepository = new DesktopModeTaskRepository();
mController = new DesktopModeController(mContext, mShellInit, mShellTaskOrganizer,
- mRootDisplayAreaOrganizer, mMockHandler);
+ mRootTaskDisplayAreaOrganizer, mMockTransitions,
+ mDesktopModeTaskRepository, mMockHandler, mExecutor);
+
+ when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(anyInt())).thenReturn(
+ new WindowContainerTransaction());
mShellInit.init();
+ clearInvocations(mShellTaskOrganizer);
+ clearInvocations(mRootTaskDisplayAreaOrganizer);
+ }
+
+ @After
+ public void tearDown() {
+ mMockitoSession.finishMocking();
}
@Test
@@ -91,19 +125,19 @@ public class DesktopModeControllerTest extends ShellTestCase {
when(mShellTaskOrganizer.prepareClearFreeformForStandardTasks(
mContext.getDisplayId())).thenReturn(taskWct);
- // Create a fake WCT to simulate setting display windowing mode to freeform
- WindowContainerTransaction displayWct = new WindowContainerTransaction();
+ // Create a fake DisplayAreaInfo to check if windowing mode change is set correctly
MockToken displayMockToken = new MockToken();
- displayWct.setWindowingMode(displayMockToken.token(), WINDOWING_MODE_FREEFORM);
- when(mRootDisplayAreaOrganizer.prepareWindowingModeChange(mContext.getDisplayId(),
- WINDOWING_MODE_FREEFORM)).thenReturn(displayWct);
+ DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(displayMockToken.mToken,
+ mContext.getDisplayId(), 0);
+ when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId()))
+ .thenReturn(displayAreaInfo);
// The test
mController.updateDesktopModeActive(true);
ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
WindowContainerTransaction.class);
- verify(mRootDisplayAreaOrganizer).applyTransaction(arg.capture());
+ verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture());
// WCT should have 2 changes - clear task wm mode and set display wm mode
WindowContainerTransaction wct = arg.getValue();
@@ -115,7 +149,7 @@ public class DesktopModeControllerTest extends ShellTestCase {
assertThat(taskWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
// Verify executed WCT has a change for setting display windowing mode to freeform
- Change displayWmModeChange = wct.getChanges().get(displayMockToken.binder());
+ Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder());
assertThat(displayWmModeChange).isNotNull();
assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM);
}
@@ -136,43 +170,76 @@ public class DesktopModeControllerTest extends ShellTestCase {
when(mShellTaskOrganizer.prepareClearBoundsForStandardTasks(
mContext.getDisplayId())).thenReturn(taskBoundsWct);
- // Create a fake WCT to simulate setting display windowing mode to fullscreen
- WindowContainerTransaction displayWct = new WindowContainerTransaction();
+ // Create a fake DisplayAreaInfo to check if windowing mode change is set correctly
MockToken displayMockToken = new MockToken();
- displayWct.setWindowingMode(displayMockToken.token(), WINDOWING_MODE_FULLSCREEN);
- when(mRootDisplayAreaOrganizer.prepareWindowingModeChange(mContext.getDisplayId(),
- WINDOWING_MODE_FULLSCREEN)).thenReturn(displayWct);
+ DisplayAreaInfo displayAreaInfo = new DisplayAreaInfo(displayMockToken.mToken,
+ mContext.getDisplayId(), 0);
+ when(mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(mContext.getDisplayId()))
+ .thenReturn(displayAreaInfo);
// The test
mController.updateDesktopModeActive(false);
ArgumentCaptor<WindowContainerTransaction> arg = ArgumentCaptor.forClass(
WindowContainerTransaction.class);
- verify(mRootDisplayAreaOrganizer).applyTransaction(arg.capture());
+ verify(mRootTaskDisplayAreaOrganizer).applyTransaction(arg.capture());
// WCT should have 3 changes - clear task wm mode and bounds and set display wm mode
WindowContainerTransaction wct = arg.getValue();
assertThat(wct.getChanges()).hasSize(3);
// Verify executed WCT has a change for setting task windowing mode to undefined
- Change taskWmModeChange = wct.getChanges().get(taskWmMockToken.binder());
- assertThat(taskWmModeChange).isNotNull();
- assertThat(taskWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
+ Change taskWmMode = wct.getChanges().get(taskWmMockToken.binder());
+ assertThat(taskWmMode).isNotNull();
+ assertThat(taskWmMode.getWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED);
// Verify executed WCT has a change for clearing task bounds
- Change taskBoundsChange = wct.getChanges().get(taskBoundsMockToken.binder());
- assertThat(taskBoundsChange).isNotNull();
- assertThat(taskBoundsChange.getWindowSetMask()
- & WindowConfiguration.WINDOW_CONFIG_BOUNDS).isNotEqualTo(0);
- assertThat(taskBoundsChange.getConfiguration().windowConfiguration.getBounds().isEmpty())
- .isTrue();
+ Change bounds = wct.getChanges().get(taskBoundsMockToken.binder());
+ assertThat(bounds).isNotNull();
+ assertThat(bounds.getWindowSetMask() & WINDOW_CONFIG_BOUNDS).isNotEqualTo(0);
+ assertThat(bounds.getConfiguration().windowConfiguration.getBounds().isEmpty()).isTrue();
// Verify executed WCT has a change for setting display windowing mode to fullscreen
- Change displayWmModeChange = wct.getChanges().get(displayMockToken.binder());
+ Change displayWmModeChange = wct.getChanges().get(displayAreaInfo.token.asBinder());
assertThat(displayWmModeChange).isNotNull();
assertThat(displayWmModeChange.getWindowingMode()).isEqualTo(WINDOWING_MODE_FULLSCREEN);
}
+ @Test
+ public void testShowDesktopApps() {
+ // Set up two active tasks on desktop
+ mDesktopModeTaskRepository.addActiveTask(1);
+ mDesktopModeTaskRepository.addActiveTask(2);
+ MockToken token1 = new MockToken();
+ MockToken token2 = new MockToken();
+ ActivityManager.RunningTaskInfo taskInfo1 = new TestRunningTaskInfoBuilder().setToken(
+ token1.token()).setLastActiveTime(100).build();
+ ActivityManager.RunningTaskInfo taskInfo2 = new TestRunningTaskInfoBuilder().setToken(
+ token2.token()).setLastActiveTime(200).build();
+ when(mShellTaskOrganizer.getRunningTaskInfo(1)).thenReturn(taskInfo1);
+ when(mShellTaskOrganizer.getRunningTaskInfo(2)).thenReturn(taskInfo2);
+
+ // Run show desktop apps logic
+ mController.showDesktopApps();
+ ArgumentCaptor<WindowContainerTransaction> wctCaptor = ArgumentCaptor.forClass(
+ WindowContainerTransaction.class);
+ verify(mShellTaskOrganizer).applyTransaction(wctCaptor.capture());
+ WindowContainerTransaction wct = wctCaptor.getValue();
+
+ // Check wct has reorder calls
+ assertThat(wct.getHierarchyOps()).hasSize(2);
+
+ // Task 2 has activity later, must be first
+ WindowContainerTransaction.HierarchyOp op1 = wct.getHierarchyOps().get(0);
+ assertThat(op1.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
+ assertThat(op1.getContainer()).isEqualTo(token2.binder());
+
+ // Task 1 should be second
+ WindowContainerTransaction.HierarchyOp op2 = wct.getHierarchyOps().get(0);
+ assertThat(op2.getType()).isEqualTo(HIERARCHY_OP_TYPE_REORDER);
+ assertThat(op2.getContainer()).isEqualTo(token2.binder());
+ }
+
private static class MockToken {
private final WindowContainerToken mToken;
private final IBinder mBinder;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
new file mode 100644
index 000000000000..9b28d11f6a9d
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.desktopmode
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.wm.shell.ShellTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeTaskRepositoryTest : ShellTestCase() {
+
+ private lateinit var repo: DesktopModeTaskRepository
+
+ @Before
+ fun setUp() {
+ repo = DesktopModeTaskRepository()
+ }
+
+ @Test
+ fun addActiveTask_listenerNotifiedAndTaskIsActive() {
+ val listener = TestListener()
+ repo.addListener(listener)
+
+ repo.addActiveTask(1)
+ assertThat(listener.activeTaskChangedCalls).isEqualTo(1)
+ assertThat(repo.isActiveTask(1)).isTrue()
+ }
+
+ @Test
+ fun addActiveTask_sameTaskDoesNotNotify() {
+ val listener = TestListener()
+ repo.addListener(listener)
+
+ repo.addActiveTask(1)
+ repo.addActiveTask(1)
+ assertThat(listener.activeTaskChangedCalls).isEqualTo(1)
+ }
+
+ @Test
+ fun addActiveTask_multipleTasksAddedNotifiesForEach() {
+ val listener = TestListener()
+ repo.addListener(listener)
+
+ repo.addActiveTask(1)
+ repo.addActiveTask(2)
+ assertThat(listener.activeTaskChangedCalls).isEqualTo(2)
+ }
+
+ @Test
+ fun removeActiveTask_listenerNotifiedAndTaskNotActive() {
+ val listener = TestListener()
+ repo.addListener(listener)
+
+ repo.addActiveTask(1)
+ repo.removeActiveTask(1)
+ // Notify once for add and once for remove
+ assertThat(listener.activeTaskChangedCalls).isEqualTo(2)
+ assertThat(repo.isActiveTask(1)).isFalse()
+ }
+
+ @Test
+ fun removeActiveTask_removeNotExistingTaskDoesNotNotify() {
+ val listener = TestListener()
+ repo.addListener(listener)
+ repo.removeActiveTask(99)
+ assertThat(listener.activeTaskChangedCalls).isEqualTo(0)
+ }
+
+ @Test
+ fun isActiveTask_notExistingTaskReturnsFalse() {
+ assertThat(repo.isActiveTask(99)).isFalse()
+ }
+
+ class TestListener : DesktopModeTaskRepository.Listener {
+ var activeTaskChangedCalls = 0
+ override fun onActiveTasksChanged() {
+ activeTaskChangedCalls++
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java
new file mode 100644
index 000000000000..a88c83779f25
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/floating/FloatingTasksControllerTest.java
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.floating;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.wm.shell.floating.FloatingTasksController.SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.view.WindowInsets;
+import android.view.WindowManager;
+import android.view.WindowMetrics;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.TaskViewTransitions;
+import com.android.wm.shell.TestShellExecutor;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.common.SyncTransactionQueue;
+import com.android.wm.shell.floating.views.FloatingTaskLayer;
+import com.android.wm.shell.sysui.ShellCommandHandler;
+import com.android.wm.shell.sysui.ShellController;
+import com.android.wm.shell.sysui.ShellInit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+/**
+ * Tests for the floating tasks controller.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FloatingTasksControllerTest extends ShellTestCase {
+ // Some behavior in the controller constructor is dependent on this so we can only
+ // validate if it's working for the real value for those things.
+ private static final boolean FLOATING_TASKS_ACTUALLY_ENABLED =
+ SystemProperties.getBoolean("persist.wm.debug.floating_tasks", false);
+
+ @Mock private ShellInit mShellInit;
+ @Mock private ShellController mShellController;
+ @Mock private WindowManager mWindowManager;
+ @Mock private ShellTaskOrganizer mTaskOrganizer;
+ @Captor private ArgumentCaptor<FloatingTaskLayer> mFloatingTaskLayerCaptor;
+
+ private FloatingTasksController mController;
+
+ @Before
+ public void setUp() throws RemoteException {
+ MockitoAnnotations.initMocks(this);
+
+ WindowMetrics windowMetrics = mock(WindowMetrics.class);
+ WindowInsets windowInsets = mock(WindowInsets.class);
+ Insets insets = Insets.of(0, 0, 0, 0);
+ when(mWindowManager.getCurrentWindowMetrics()).thenReturn(windowMetrics);
+ when(windowMetrics.getWindowInsets()).thenReturn(windowInsets);
+ when(windowMetrics.getBounds()).thenReturn(new Rect(0, 0, 1000, 1000));
+ when(windowInsets.getInsetsIgnoringVisibility(anyInt())).thenReturn(insets);
+
+ // For the purposes of this test, just run everything synchronously
+ ShellExecutor shellExecutor = new TestShellExecutor();
+ when(mTaskOrganizer.getExecutor()).thenReturn(shellExecutor);
+ }
+
+ @After
+ public void tearDown() {
+ if (mController != null) {
+ mController.removeTask();
+ mController = null;
+ }
+ }
+
+ private void setUpTabletConfig() {
+ Configuration config = mock(Configuration.class);
+ config.smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET;
+ mController.setConfig(config);
+ }
+
+ private void setUpPhoneConfig() {
+ Configuration config = mock(Configuration.class);
+ config.smallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_TO_BE_TABLET - 1;
+ mController.setConfig(config);
+ }
+
+ private void createController() {
+ mController = new FloatingTasksController(mContext,
+ mShellInit,
+ mShellController,
+ mock(ShellCommandHandler.class),
+ Optional.empty(),
+ mWindowManager,
+ mTaskOrganizer,
+ mock(TaskViewTransitions.class),
+ mock(ShellExecutor.class),
+ mock(ShellExecutor.class),
+ mock(SyncTransactionQueue.class));
+ spyOn(mController);
+ }
+
+ //
+ // Shell specific
+ //
+ @Test
+ public void instantiateController_addInitCallback() {
+ if (FLOATING_TASKS_ACTUALLY_ENABLED) {
+ createController();
+ setUpTabletConfig();
+
+ verify(mShellInit, times(1)).addInitCallback(any(), any());
+ }
+ }
+
+ @Test
+ public void instantiateController_doesntAddInitCallback() {
+ if (!FLOATING_TASKS_ACTUALLY_ENABLED) {
+ createController();
+
+ verify(mShellInit, never()).addInitCallback(any(), any());
+ }
+ }
+
+ @Test
+ public void onInit_registerConfigChangeListener() {
+ if (FLOATING_TASKS_ACTUALLY_ENABLED) {
+ createController();
+ setUpTabletConfig();
+ mController.onInit();
+
+ verify(mShellController, times(1)).addConfigurationChangeListener(any());
+ }
+ }
+
+ //
+ // Tests for floating layer, which is only available for tablets.
+ //
+
+ @Test
+ public void testIsFloatingLayerAvailable_true() {
+ createController();
+ setUpTabletConfig();
+ assertThat(mController.isFloatingLayerAvailable()).isTrue();
+ }
+
+ @Test
+ public void testIsFloatingLayerAvailable_false() {
+ createController();
+ setUpPhoneConfig();
+ assertThat(mController.isFloatingLayerAvailable()).isFalse();
+ }
+
+ //
+ // Tests for floating tasks being enabled, guarded by sysprop flag.
+ //
+
+ @Test
+ public void testIsFloatingTasksEnabled_true() {
+ createController();
+ mController.setFloatingTasksEnabled(true);
+ setUpTabletConfig();
+ assertThat(mController.isFloatingTasksEnabled()).isTrue();
+ }
+
+ @Test
+ public void testIsFloatingTasksEnabled_false() {
+ createController();
+ mController.setFloatingTasksEnabled(false);
+ setUpTabletConfig();
+ assertThat(mController.isFloatingTasksEnabled()).isFalse();
+ }
+
+ //
+ // Tests for behavior depending on flags
+ //
+
+ @Test
+ public void testShowTaskIntent_enabled() {
+ createController();
+ mController.setFloatingTasksEnabled(true);
+ setUpTabletConfig();
+
+ mController.showTask(mock(Intent.class));
+ verify(mWindowManager).addView(mFloatingTaskLayerCaptor.capture(), any());
+ assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(1);
+ }
+
+ @Test
+ public void testShowTaskIntent_notEnabled() {
+ createController();
+ mController.setFloatingTasksEnabled(false);
+ setUpTabletConfig();
+
+ mController.showTask(mock(Intent.class));
+ verify(mWindowManager, never()).addView(any(), any());
+ }
+
+ @Test
+ public void testRemoveTask() {
+ createController();
+ mController.setFloatingTasksEnabled(true);
+ setUpTabletConfig();
+
+ mController.showTask(mock(Intent.class));
+ verify(mWindowManager).addView(mFloatingTaskLayerCaptor.capture(), any());
+ assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(1);
+
+ mController.removeTask();
+ verify(mWindowManager).removeView(mFloatingTaskLayerCaptor.capture());
+ assertThat(mFloatingTaskLayerCaptor.getValue().getTaskViewCount()).isEqualTo(0);
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index a8d3bdcb7c96..1e08f1e55797 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -48,6 +48,7 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.onehanded.OneHandedController;
+import com.android.wm.shell.pip.PipAnimationController;
import com.android.wm.shell.pip.PipAppOpsListener;
import com.android.wm.shell.pip.PipBoundsAlgorithm;
import com.android.wm.shell.pip.PipBoundsState;
@@ -85,6 +86,7 @@ public class PipControllerTest extends ShellTestCase {
@Mock private ShellCommandHandler mMockShellCommandHandler;
@Mock private DisplayController mMockDisplayController;
@Mock private PhonePipMenuController mMockPhonePipMenuController;
+ @Mock private PipAnimationController mMockPipAnimationController;
@Mock private PipAppOpsListener mMockPipAppOpsListener;
@Mock private PipBoundsAlgorithm mMockPipBoundsAlgorithm;
@Mock private PhonePipKeepClearAlgorithm mMockPipKeepClearAlgorithm;
@@ -117,8 +119,8 @@ public class PipControllerTest extends ShellTestCase {
mShellController = spy(new ShellController(mShellInit, mMockShellCommandHandler,
mMockExecutor));
mPipController = new PipController(mContext, mShellInit, mMockShellCommandHandler,
- mShellController, mMockDisplayController, mMockPipAppOpsListener,
- mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
+ mShellController, mMockDisplayController, mMockPipAnimationController,
+ mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
@@ -183,8 +185,8 @@ public class PipControllerTest extends ShellTestCase {
ShellInit shellInit = new ShellInit(mMockExecutor);
assertNull(PipController.create(spyContext, shellInit, mMockShellCommandHandler,
- mShellController, mMockDisplayController, mMockPipAppOpsListener,
- mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
+ mShellController, mMockDisplayController, mMockPipAnimationController,
+ mMockPipAppOpsListener, mMockPipBoundsAlgorithm, mMockPipKeepClearAlgorithm,
mMockPipBoundsState, mMockPipMotionHelper, mMockPipMediaController,
mMockPhonePipMenuController, mMockPipTaskOrganizer, mMockPipTransitionState,
mMockPipTouchHandler, mMockPipTransitionController, mMockWindowManagerShellWrapper,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
index e9a1e2523a86..b8aaaa76e3c7 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java
@@ -40,6 +40,7 @@ import static org.mockito.Mockito.when;
import static java.lang.Integer.MAX_VALUE;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Rect;
@@ -52,9 +53,9 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
-import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerImpl;
-import com.android.wm.shell.desktopmode.DesktopMode;
+import com.android.wm.shell.desktopmode.DesktopModeStatus;
+import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.sysui.ShellCommandHandler;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.util.GroupedRecentTaskInfo;
@@ -67,7 +68,9 @@ import org.mockito.Mock;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.Optional;
+import java.util.function.Consumer;
/**
* Tests for {@link RecentTasksController}.
@@ -82,11 +85,15 @@ public class RecentTasksControllerTest extends ShellTestCase {
private TaskStackListenerImpl mTaskStackListener;
@Mock
private ShellCommandHandler mShellCommandHandler;
+ @Mock
+ private DesktopModeTaskRepository mDesktopModeTaskRepository;
+ @Mock
+ private ActivityTaskManager mActivityTaskManager;
private ShellTaskOrganizer mShellTaskOrganizer;
private RecentTasksController mRecentTasksController;
private ShellInit mShellInit;
- private ShellExecutor mMainExecutor;
+ private TestShellExecutor mMainExecutor;
@Before
public void setUp() {
@@ -94,7 +101,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
when(mContext.getPackageManager()).thenReturn(mock(PackageManager.class));
mShellInit = spy(new ShellInit(mMainExecutor));
mRecentTasksController = spy(new RecentTasksController(mContext, mShellInit,
- mShellCommandHandler, mTaskStackListener, mMainExecutor));
+ mShellCommandHandler, mTaskStackListener, mActivityTaskManager,
+ Optional.of(mDesktopModeTaskRepository), mMainExecutor));
mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler,
null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController),
mMainExecutor);
@@ -184,10 +192,41 @@ public class RecentTasksControllerTest extends ShellTestCase {
}
@Test
+ public void testGetRecentTasks_ReturnsRecentTasksAsynchronously() {
+ @SuppressWarnings("unchecked")
+ final List<GroupedRecentTaskInfo>[] recentTasks = new List[1];
+ Consumer<List<GroupedRecentTaskInfo>> consumer = argument -> recentTasks[0] = argument;
+ ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
+ ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
+ ActivityManager.RecentTaskInfo t3 = makeTaskInfo(3);
+ ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
+ ActivityManager.RecentTaskInfo t5 = makeTaskInfo(5);
+ ActivityManager.RecentTaskInfo t6 = makeTaskInfo(6);
+ setRawList(t1, t2, t3, t4, t5, t6);
+
+ // Mark a couple pairs [t2, t4], [t3, t5]
+ SplitBounds pair1Bounds = new SplitBounds(new Rect(), new Rect(), 2, 4);
+ SplitBounds pair2Bounds = new SplitBounds(new Rect(), new Rect(), 3, 5);
+
+ mRecentTasksController.addSplitPair(t2.taskId, t4.taskId, pair1Bounds);
+ mRecentTasksController.addSplitPair(t3.taskId, t5.taskId, pair2Bounds);
+
+ mRecentTasksController.asRecentTasks()
+ .getRecentTasks(MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0, Runnable::run, consumer);
+ mMainExecutor.flushAll();
+
+ assertGroupedTasksListEquals(recentTasks[0],
+ t1.taskId, -1,
+ t2.taskId, t4.taskId,
+ t3.taskId, t5.taskId,
+ t6.taskId, -1);
+ }
+
+ @Test
public void testGetRecentTasks_groupActiveFreeformTasks() {
StaticMockitoSession mockitoSession = mockitoSession().mockStatic(
- DesktopMode.class).startMocking();
- when(DesktopMode.isActive(any())).thenReturn(true);
+ DesktopModeStatus.class).startMocking();
+ when(DesktopModeStatus.isActive(any())).thenReturn(true);
ActivityManager.RecentTaskInfo t1 = makeTaskInfo(1);
ActivityManager.RecentTaskInfo t2 = makeTaskInfo(2);
@@ -195,8 +234,8 @@ public class RecentTasksControllerTest extends ShellTestCase {
ActivityManager.RecentTaskInfo t4 = makeTaskInfo(4);
setRawList(t1, t2, t3, t4);
- mRecentTasksController.addActiveFreeformTask(1);
- mRecentTasksController.addActiveFreeformTask(3);
+ when(mDesktopModeTaskRepository.isActiveTask(1)).thenReturn(true);
+ when(mDesktopModeTaskRepository.isActiveTask(3)).thenReturn(true);
ArrayList<GroupedRecentTaskInfo> recentTasks = mRecentTasksController.getRecentTasks(
MAX_VALUE, RECENT_IGNORE_UNAVAILABLE, 0);
@@ -292,7 +331,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
for (ActivityManager.RecentTaskInfo task : tasks) {
rawList.add(task);
}
- doReturn(rawList).when(mRecentTasksController).getRawRecentTasks(anyInt(), anyInt(),
+ doReturn(rawList).when(mActivityTaskManager).getRecentTasks(anyInt(), anyInt(),
anyInt());
return rawList;
}
@@ -303,7 +342,7 @@ public class RecentTasksControllerTest extends ShellTestCase {
* @param expectedTaskIds list of task ids that map to the flattened task ids of the tasks in
* the grouped task list
*/
- private void assertGroupedTasksListEquals(ArrayList<GroupedRecentTaskInfo> recentTasks,
+ private void assertGroupedTasksListEquals(List<GroupedRecentTaskInfo> recentTasks,
int... expectedTaskIds) {
int[] flattenedTaskIds = new int[recentTasks.size() * 2];
for (int i = 0; i < recentTasks.size(); i++) {
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 ea9390ee8efd..835087007b30 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
@@ -16,7 +16,10 @@
package com.android.wm.shell.splitscreen;
+import static android.app.ActivityOptions.KEY_LAUNCH_ROOT_TASK_TOKEN;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED;
+import static android.app.ComponentOptions.KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
@@ -28,11 +31,10 @@ import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -42,8 +44,10 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.content.res.Configuration;
import android.graphics.Rect;
+import android.os.Bundle;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -115,7 +119,6 @@ public class StageCoordinatorTests extends ShellTestCase {
mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
mMainExecutor, Optional.empty()));
- doNothing().when(mStageCoordinator).updateActivityOptions(any(), anyInt());
when(mSplitLayout.getBounds1()).thenReturn(mBounds1);
when(mSplitLayout.getBounds2()).thenReturn(mBounds2);
@@ -225,7 +228,6 @@ public class StageCoordinatorTests extends ShellTestCase {
mStageCoordinator.exitSplitScreen(testTaskId, EXIT_REASON_RETURN_HOME);
verify(mMainStage).reorderChild(eq(testTaskId), eq(true),
any(WindowContainerTransaction.class));
- verify(mSideStage).dismiss(any(WindowContainerTransaction.class), eq(false));
verify(mMainStage).resetBounds(any(WindowContainerTransaction.class));
}
@@ -239,7 +241,6 @@ public class StageCoordinatorTests extends ShellTestCase {
verify(mSideStage).reorderChild(eq(testTaskId), eq(true),
any(WindowContainerTransaction.class));
verify(mSideStage).resetBounds(any(WindowContainerTransaction.class));
- verify(mMainStage).dismiss(any(WindowContainerTransaction.class), eq(false));
}
@Test
@@ -305,4 +306,16 @@ public class StageCoordinatorTests extends ShellTestCase {
verify(mSplitLayout).applySurfaceChanges(any(), any(), any(), any(), any(), eq(false));
}
+
+ @Test
+ public void testAddActivityOptions_addsBackgroundActivitiesFlags() {
+ Bundle options = mStageCoordinator.resolveStartStage(STAGE_TYPE_MAIN,
+ SPLIT_POSITION_UNDEFINED, null /* options */, null /* wct */);
+
+ assertEquals(options.getParcelable(KEY_LAUNCH_ROOT_TASK_TOKEN, WindowContainerToken.class),
+ mMainStage.mRootTaskInfo.token);
+ assertTrue(options.getBoolean(KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED));
+ assertTrue(options.getBoolean(
+ KEY_PENDING_INTENT_BACKGROUND_ACTIVITY_ALLOWED_BY_PERMISSION));
+ }
}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 779c4b75efc4..eb8d26adc7d7 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -71,6 +71,7 @@ cc_library {
"misc.cpp",
"ObbFile.cpp",
"PosixUtils.cpp",
+ "ResourceTimer.cpp",
"ResourceTypes.cpp",
"ResourceUtils.cpp",
"StreamingZipInflater.cpp",
@@ -173,6 +174,7 @@ cc_test {
"tests/Idmap_test.cpp",
"tests/LoadedArsc_test.cpp",
"tests/Locale_test.cpp",
+ "tests/ResourceTimer_test.cpp",
"tests/ResourceUtils_test.cpp",
"tests/ResTable_test.cpp",
"tests/Split_test.cpp",
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 39c7d198fe5b..235700b27c25 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -601,10 +601,6 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
return base::unexpected(result.error());
}
- if (type_idx == 0x1c) {
- LOG(ERROR) << base::StringPrintf("foobar first result %s", result->package_name->c_str());
- }
-
bool overlaid = false;
if (!stop_at_first_match && !ignore_configuration && !apk_assets_[result->cookie]->IsLoader()) {
for (const auto& id_map : package_group.overlays_) {
@@ -615,7 +611,21 @@ base::expected<FindEntryResult, NullOrIOError> AssetManager2::FindEntry(
}
if (overlay_entry.IsInlineValue()) {
// The target resource is overlaid by an inline value not represented by a resource.
- result->entry = overlay_entry.GetInlineValue();
+ ConfigDescription best_frro_config;
+ Res_value best_frro_value;
+ bool frro_found = false;
+ for( const auto& [config, value] : overlay_entry.GetInlineValue()) {
+ if ((!frro_found || config.isBetterThan(best_frro_config, desired_config))
+ && config.match(*desired_config)) {
+ frro_found = true;
+ best_frro_config = config;
+ best_frro_value = value;
+ }
+ }
+ if (!frro_found) {
+ continue;
+ }
+ result->entry = best_frro_value;
result->dynamic_ref_table = id_map.overlay_res_maps_.GetOverlayDynamicRefTable();
result->cookie = id_map.cookie;
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index efd1f6a25786..e122d4844ee9 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -56,6 +56,8 @@ struct Idmap_header {
struct Idmap_data_header {
uint32_t target_entry_count;
uint32_t target_inline_entry_count;
+ uint32_t target_inline_entry_value_count;
+ uint32_t configuration_count;
uint32_t overlay_entry_count;
uint32_t string_pool_index_offset;
@@ -68,6 +70,12 @@ struct Idmap_target_entry {
struct Idmap_target_entry_inline {
uint32_t target_id;
+ uint32_t start_value_index;
+ uint32_t value_count;
+};
+
+struct Idmap_target_entry_inline_value {
+ uint32_t config_index;
Res_value value;
};
@@ -138,11 +146,15 @@ status_t OverlayDynamicRefTable::lookupResourceIdNoRewrite(uint32_t* resId) cons
IdmapResMap::IdmapResMap(const Idmap_data_header* data_header,
const Idmap_target_entry* entries,
const Idmap_target_entry_inline* inline_entries,
+ const Idmap_target_entry_inline_value* inline_entry_values,
+ const ConfigDescription* configs,
uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table)
: data_header_(data_header),
entries_(entries),
inline_entries_(inline_entries),
+ inline_entry_values_(inline_entry_values),
+ configurations_(configs),
target_assigned_package_id_(target_assigned_package_id),
overlay_ref_table_(overlay_ref_table) { }
@@ -183,7 +195,13 @@ IdmapResMap::Result IdmapResMap::Lookup(uint32_t target_res_id) const {
if (inline_entry != end_inline_entry &&
(0x00FFFFFFU & dtohl(inline_entry->target_id)) == target_res_id) {
- return Result(inline_entry->value);
+ std::map<ConfigDescription, Res_value> values_map;
+ for (int i = 0; i < inline_entry->value_count; i++) {
+ const auto& value = inline_entry_values_[inline_entry->start_value_index + i];
+ const auto& config = configurations_[value.config_index];
+ values_map[config] = value.value;
+ }
+ return Result(values_map);
}
return {};
}
@@ -237,6 +255,8 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
+ const Idmap_target_entry_inline_value* inline_entry_values,
+ const ConfigDescription* configs,
const Idmap_overlay_entry* overlay_entries,
std::unique_ptr<ResStringPool>&& string_pool,
std::string_view overlay_apk_path,
@@ -245,6 +265,8 @@ LoadedIdmap::LoadedIdmap(std::string&& idmap_path,
data_header_(data_header),
target_entries_(target_entries),
target_inline_entries_(target_inline_entries),
+ inline_entry_values_(inline_entry_values),
+ configurations_(configs),
overlay_entries_(overlay_entries),
string_pool_(std::move(string_pool)),
idmap_path_(std::move(idmap_path)),
@@ -303,6 +325,21 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
if (target_inline_entries == nullptr) {
return {};
}
+
+ auto target_inline_entry_values = ReadType<Idmap_target_entry_inline_value>(
+ &data_ptr, &data_size, "target inline values",
+ dtohl(data_header->target_inline_entry_value_count));
+ if (target_inline_entry_values == nullptr) {
+ return {};
+ }
+
+ auto configurations = ReadType<ConfigDescription>(
+ &data_ptr, &data_size, "configurations",
+ dtohl(data_header->configuration_count));
+ if (configurations == nullptr) {
+ return {};
+ }
+
auto overlay_entries = ReadType<Idmap_overlay_entry>(&data_ptr, &data_size, "target inline",
dtohl(data_header->overlay_entry_count));
if (overlay_entries == nullptr) {
@@ -329,8 +366,8 @@ std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(const StringPiece& idmap_path,
// Can't use make_unique because LoadedIdmap constructor is private.
return std::unique_ptr<LoadedIdmap>(
new LoadedIdmap(idmap_path.to_string(), header, data_header, target_entries,
- target_inline_entries, overlay_entries, std::move(idmap_string_pool),
- *target_path, *overlay_path));
+ target_inline_entries, target_inline_entry_values, configurations,
+ overlay_entries, std::move(idmap_string_pool), *target_path, *overlay_path));
}
bool LoadedIdmap::IsUpToDate() const {
diff --git a/libs/androidfw/ResourceTimer.cpp b/libs/androidfw/ResourceTimer.cpp
new file mode 100644
index 000000000000..44128d9e4e3d
--- /dev/null
+++ b/libs/androidfw/ResourceTimer.cpp
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 <unistd.h>
+#include <string.h>
+
+#include <map>
+#include <atomic>
+
+#include <utils/Log.h>
+#include <androidfw/ResourceTimer.h>
+
+// The following block allows compilation on windows, which does not have getuid().
+#ifdef _WIN32
+#ifdef ERROR
+#undef ERROR
+#endif
+#define getuid() (getUidWindows_)
+#endif
+
+namespace android {
+
+namespace {
+
+#ifdef _WIN32
+// A temporary to confuse lint into thinking that getuid() on windows might return something other
+// than zero.
+int getUidWindows_ = 0;
+#endif
+
+// The number of nanoseconds in a microsecond.
+static const unsigned int US = 1000;
+// The number of nanoseconds in a second.
+static const unsigned int S = 1000 * 1000 * 1000;
+
+// Return the difference between two timespec values. The difference is in nanoseconds. If the
+// return value would exceed 2s (2^31 nanoseconds) then UINT_MAX is returned.
+unsigned int diffInNs(timespec const &a, timespec const &b) {
+ timespec r = { 0, 0 };
+ r.tv_nsec = a.tv_nsec - b.tv_nsec;
+ if (r.tv_nsec < 0) {
+ r.tv_sec = -1;
+ r.tv_nsec += S;
+ }
+ r.tv_sec = r.tv_sec + (a.tv_sec - b.tv_sec);
+ if (r.tv_sec > 2) return UINT_MAX;
+ unsigned int result = (r.tv_sec * S) + r.tv_nsec;
+ if (result > 2 * S) return UINT_MAX;
+ return result;
+}
+
+}
+
+ResourceTimer::ResourceTimer(Counter api)
+ : active_(enabled_.load()),
+ api_(api) {
+ if (active_) {
+ clock_gettime(CLOCK_MONOTONIC, &start_);
+ }
+}
+
+ResourceTimer::~ResourceTimer() {
+ record();
+}
+
+void ResourceTimer::enable() {
+ if (!enabled_.load()) counter_ = new GuardedTimer[ResourceTimer::counterSize];
+ enabled_.store(true);
+}
+
+void ResourceTimer::cancel() {
+ active_ = false;
+}
+
+void ResourceTimer::record() {
+ if (!active_) return;
+
+ struct timespec end;
+ clock_gettime(CLOCK_MONOTONIC, &end);
+ // Get the difference in microseconds.
+ const unsigned int ticks = diffInNs(end, start_);
+ ScopedTimer t(counter_[toIndex(api_)]);
+ t->record(ticks);
+ active_ = false;
+}
+
+bool ResourceTimer::copy(int counter, Timer &dst, bool reset) {
+ ScopedTimer t(counter_[counter]);
+ if (t->count == 0) {
+ dst.reset();
+ if (reset) t->reset();
+ return false;
+ }
+ Timer::copy(dst, *t, reset);
+ return true;
+}
+
+void ResourceTimer::reset() {
+ for (int i = 0; i < counterSize; i++) {
+ ScopedTimer t(counter_[i]);
+ t->reset();
+ }
+}
+
+ResourceTimer::Timer::Timer() {
+ // Ensure newly-created objects are zeroed.
+ memset(buckets, 0, sizeof(buckets));
+ reset();
+}
+
+ResourceTimer::Timer::~Timer() {
+ for (int d = 0; d < MaxDimension; d++) {
+ delete[] buckets[d];
+ }
+}
+
+void ResourceTimer::Timer::freeBuckets() {
+ for (int d = 0; d < MaxDimension; d++) {
+ delete[] buckets[d];
+ buckets[d] = 0;
+ }
+}
+
+void ResourceTimer::Timer::reset() {
+ count = total = mintime = maxtime = 0;
+ memset(largest, 0, sizeof(largest));
+ memset(&pvalues, 0, sizeof(pvalues));
+ // Zero the histogram, keeping any allocated dimensions.
+ for (int d = 0; d < MaxDimension; d++) {
+ if (buckets[d] != 0) memset(buckets[d], 0, sizeof(int) * MaxBuckets);
+ }
+}
+
+void ResourceTimer::Timer::copy(Timer &dst, Timer &src, bool reset) {
+ dst.freeBuckets();
+ dst = src;
+ // Clean up the histograms.
+ if (reset) {
+ // Do NOT free the src buckets because they being used by dst.
+ memset(src.buckets, 0, sizeof(src.buckets));
+ src.reset();
+ } else {
+ for (int d = 0; d < MaxDimension; d++) {
+ if (src.buckets[d] != nullptr) {
+ dst.buckets[d] = new int[MaxBuckets];
+ memcpy(dst.buckets[d], src.buckets[d], sizeof(int) * MaxBuckets);
+ }
+ }
+ }
+}
+
+void ResourceTimer::Timer::record(int ticks) {
+ // Record that the event happened.
+ count++;
+
+ total += ticks;
+ if (mintime == 0 || ticks < mintime) mintime = ticks;
+ if (ticks > maxtime) maxtime = ticks;
+
+ // Do not add oversized events to the histogram.
+ if (ticks != UINT_MAX) {
+ for (int d = 0; d < MaxDimension; d++) {
+ if (ticks < range[d]) {
+ if (buckets[d] == 0) {
+ buckets[d] = new int[MaxBuckets];
+ memset(buckets[d], 0, sizeof(int) * MaxBuckets);
+ }
+ if (ticks < width[d]) {
+ // Special case: never write to bucket 0 because it complicates the percentile logic.
+ // However, this is always the smallest possible value to it is very unlikely to ever
+ // affect any of the percentile results.
+ buckets[d][1]++;
+ } else {
+ buckets[d][ticks / width[d]]++;
+ }
+ break;
+ }
+ }
+ }
+
+ // The list of largest times is sorted with the biggest value at index 0 and the smallest at
+ // index MaxLargest-1. The incoming tick count should be added to the array only if it is
+ // larger than the current value at MaxLargest-1.
+ if (ticks > largest[Timer::MaxLargest-1]) {
+ for (size_t i = 0; i < Timer::MaxLargest; i++) {
+ if (ticks > largest[i]) {
+ if (i < Timer::MaxLargest-1) {
+ for (size_t j = Timer::MaxLargest - 1; j > i; j--) {
+ largest[j] = largest[j-1];
+ }
+ }
+ largest[i] = ticks;
+ break;
+ }
+ }
+ }
+}
+
+void ResourceTimer::Timer::Percentile::compute(
+ int cumulative, int current, int count, int width, int time) {
+ nominal = time;
+ nominal_actual = (cumulative * 100) / count;
+ floor = nominal - width;
+ floor_actual = ((cumulative - current) * 100) / count;
+}
+
+void ResourceTimer::Timer::compute() {
+ memset(&pvalues, 0, sizeof(pvalues));
+
+ float l50 = count / 2.0;
+ float l90 = (count * 9.0) / 10.0;
+ float l95 = (count * 95.0) / 100.0;
+ float l99 = (count * 99.0) / 100.0;
+
+ int sum = 0;
+ for (int d = 0; d < MaxDimension; d++) {
+ if (buckets[d] == 0) continue;
+ for (int j = 0; j < MaxBuckets && sum < count; j++) {
+ // Empty buckets don't contribute to the answers. Skip them.
+ if (buckets[d][j] == 0) continue;
+ sum += buckets[d][j];
+ // A word on indexing. j is never zero in the following lines. buckets[0][0] corresponds
+ // to a delay of 0us, which cannot happen. buckets[n][0], for n > 0 overlaps a value in
+ // buckets[n-1], and the code would have stopped there.
+ if (sum >= l50 && pvalues.p50.nominal == 0) {
+ pvalues.p50.compute(sum, buckets[d][j], count, width[d], j * width[d]);
+ }
+ if (sum >= l90 && pvalues.p90.nominal == 0) {
+ pvalues.p90.compute(sum, buckets[d][j], count, width[d], j * width[d]);
+ }
+ if (sum >= l95 && pvalues.p95.nominal == 0) {
+ pvalues.p95.compute(sum, buckets[d][j], count, width[d], j * width[d]);
+ }
+ if (sum >= l99 && pvalues.p99.nominal == 0) {
+ pvalues.p99.compute(sum, buckets[d][j], count, width[d], j * width[d]);
+ }
+ }
+ }
+}
+
+char const *ResourceTimer::toString(ResourceTimer::Counter counter) {
+ switch (counter) {
+ case Counter::GetResourceValue:
+ return "GetResourceValue";
+ case Counter::RetrieveAttributes:
+ return "RetrieveAttributes";
+ };
+ return "Unknown";
+}
+
+std::atomic<bool> ResourceTimer::enabled_(false);
+std::atomic<ResourceTimer::GuardedTimer *> ResourceTimer::counter_(nullptr);
+
+const int ResourceTimer::Timer::range[] = { 100 * US, 1000 * US, 10*1000 * US, 100*1000 * US };
+const int ResourceTimer::Timer::width[] = { 1 * US, 10 * US, 100 * US, 1000 * US };
+
+
+} // namespace android
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index 6804472b3d17..a1cbbbf2271b 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -23,6 +23,7 @@
#include <variant>
#include "android-base/macros.h"
+#include "androidfw/ConfigDescription.h"
#include "androidfw/StringPiece.h"
#include "androidfw/ResourceTypes.h"
#include "utils/ByteOrder.h"
@@ -35,6 +36,7 @@ struct Idmap_header;
struct Idmap_data_header;
struct Idmap_target_entry;
struct Idmap_target_entry_inline;
+struct Idmap_target_entry_inline_value;
struct Idmap_overlay_entry;
// A string pool for overlay apk assets. The string pool holds the strings of the overlay resources
@@ -91,7 +93,8 @@ class IdmapResMap {
public:
Result() = default;
explicit Result(uint32_t value) : data_(value) {};
- explicit Result(const Res_value& value) : data_(value) { };
+ explicit Result(const std::map<ConfigDescription, Res_value> &value)
+ : data_(value) { };
// Returns `true` if the resource is overlaid.
explicit operator bool() const {
@@ -107,15 +110,16 @@ class IdmapResMap {
}
bool IsInlineValue() const {
- return std::get_if<Res_value>(&data_) != nullptr;
+ return std::get_if<2>(&data_) != nullptr;
}
- const Res_value& GetInlineValue() const {
- return std::get<Res_value>(data_);
+ const std::map<ConfigDescription, Res_value>& GetInlineValue() const {
+ return std::get<2>(data_);
}
private:
- std::variant<std::monostate, uint32_t, Res_value> data_;
+ std::variant<std::monostate, uint32_t,
+ std::map<ConfigDescription, Res_value> > data_;
};
// Looks up the value that overlays the target resource id.
@@ -129,12 +133,16 @@ class IdmapResMap {
explicit IdmapResMap(const Idmap_data_header* data_header,
const Idmap_target_entry* entries,
const Idmap_target_entry_inline* inline_entries,
+ const Idmap_target_entry_inline_value* inline_entry_values,
+ const ConfigDescription* configs,
uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table);
const Idmap_data_header* data_header_;
const Idmap_target_entry* entries_;
const Idmap_target_entry_inline* inline_entries_;
+ const Idmap_target_entry_inline_value* inline_entry_values_;
+ const ConfigDescription* configurations_;
const uint8_t target_assigned_package_id_;
const OverlayDynamicRefTable* overlay_ref_table_;
@@ -170,8 +178,8 @@ class LoadedIdmap {
// Returns a mapping from target resource ids to overlay values.
const IdmapResMap GetTargetResourcesMap(uint8_t target_assigned_package_id,
const OverlayDynamicRefTable* overlay_ref_table) const {
- return IdmapResMap(data_header_, target_entries_, target_inline_entries_,
- target_assigned_package_id, overlay_ref_table);
+ return IdmapResMap(data_header_, target_entries_, target_inline_entries_, inline_entry_values_,
+ configurations_, target_assigned_package_id, overlay_ref_table);
}
// Returns a dynamic reference table for a loaded overlay package.
@@ -191,6 +199,8 @@ class LoadedIdmap {
const Idmap_data_header* data_header_;
const Idmap_target_entry* target_entries_;
const Idmap_target_entry_inline* target_inline_entries_;
+ const Idmap_target_entry_inline_value* inline_entry_values_;
+ const ConfigDescription* configurations_;
const Idmap_overlay_entry* overlay_entries_;
const std::unique_ptr<ResStringPool> string_pool_;
@@ -207,6 +217,8 @@ class LoadedIdmap {
const Idmap_data_header* data_header,
const Idmap_target_entry* target_entries,
const Idmap_target_entry_inline* target_inline_entries,
+ const Idmap_target_entry_inline_value* inline_entry_values_,
+ const ConfigDescription* configs,
const Idmap_overlay_entry* overlay_entries,
std::unique_ptr<ResStringPool>&& string_pool,
std::string_view overlay_apk_path,
diff --git a/libs/androidfw/include/androidfw/ResourceTimer.h b/libs/androidfw/include/androidfw/ResourceTimer.h
new file mode 100644
index 000000000000..74613519a920
--- /dev/null
+++ b/libs/androidfw/include/androidfw/ResourceTimer.h
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 ANDROIDFW_RESOURCETIMER_H_
+#define ANDROIDFW_RESOURCETIMER_H_
+
+#include <time.h>
+#include <atomic>
+#include <vector>
+
+#include <utils/Mutex.h>
+#include <android-base/macros.h>
+#include <androidfw/Util.h>
+
+namespace android {
+
+// ResourceTimer captures the duration of short functions. Durations are accumulated in registers
+// and statistics are pulled back to the Java layer as needed.
+// To monitor an API, first add it to the Counter enumeration. Then, inside the API, create an
+// instance of ResourceTimer with the appropriate enumeral. The corresponding counter will be
+// updated when the ResourceTimer destructor is called, normally at the end of the enclosing block.
+class ResourceTimer {
+ public:
+ enum class Counter {
+ GetResourceValue,
+ RetrieveAttributes,
+
+ LastCounter = RetrieveAttributes,
+ };
+ static const int counterSize = static_cast<int>(Counter::LastCounter) + 1;
+ static char const *toString(Counter);
+
+ // Start a timer for the specified counter.
+ ResourceTimer(Counter);
+ // The block is exiting. If the timer is active, record it.
+ ~ResourceTimer();
+ // This records the elapsed time and disables further recording. Use this if the containing
+ // block includes extra processing that should not be included in the timer. The method is
+ // destructive in that the timer is no longer valid and further calls to record() will be
+ // ignored.
+ void record();
+ // This cancels a timer. Elapsed time will neither be computed nor recorded.
+ void cancel();
+
+ // A single timer contains the count of events and the cumulative time spent handling the
+ // events. It also includes the smallest value seen and 10 largest values seen. Finally, it
+ // includes a histogram of values that approximates a semi-log.
+
+ // The timer can compute percentiles of recorded events. For example, the p50 value is a time
+ // such that 50% of the readings are below the value and 50% are above the value. The
+ // granularity in the readings means that a percentile cannot always be computed. In this case,
+ // the percentile is reported as zero. (The simplest example is when there is a single
+ // reading.) Even if the value can be computed, it will not be exact. Therefore, a percentile
+ // is actually reported as two values: the lowest time at which it might be valid and the
+ // highest time at which it might be valid.
+ struct Timer {
+ static const size_t MaxLargest = 5;
+
+ // The construct zeros all the fields. The destructor releases memory allocated to the
+ // buckets.
+ Timer();
+ ~Timer();
+
+ // The following summary values are set to zero on a reset. All times are in ns.
+
+ // The total number of events recorded.
+ int count;
+ // The total duration of events.
+ int64_t total;
+ // The smallest event duration seen. This is guaranteed to be non-zero if count is greater
+ // than 0.
+ int mintime;
+ // The largest event duration seen.
+ int maxtime;
+
+ // The largest values seen. Element 0 is the largest value seen (and is the same as maxtime,
+ // above). Element 1 is the next largest, and so on. If count is less than MaxLargest,
+ // unused elements will be zero.
+ int largest[MaxLargest];
+
+ // The p50 value is a time such that 50% of the readings are below that time and 50% of the
+ // readings.
+
+ // A single percentile is defined by the lowest value supported by the readings and the
+ // highest value supported by the readings.
+ struct Percentile {
+ // The nominal time (in ns) of the percentile. The true percentile is guaranteed to be less
+ // than or equal to this time.
+ int nominal;
+ // The actual percentile of the nominal time.
+ int nominal_actual;
+ // The time of the next lower bin. The true percentile is guaranteed to be greater than
+ // this time.
+ int floor;
+ // The actual percentile of the floor time.
+ int floor_actual;
+
+ // Fill in a percentile given the cumulative to the bin, the count in the current bin, the
+ // total count, the width of the bin, and the time of the bin.
+ void compute(int cumulative, int current, int count, int width, int time);
+ };
+
+ // The structure that holds the percentiles.
+ struct {
+ Percentile p50;
+ Percentile p90;
+ Percentile p95;
+ Percentile p99;
+ } pvalues;
+
+ // Set all counters to zero.
+ void reset();
+ // Record an event. The input time is in ns.
+ void record(int);
+ // Compute the percentiles. Percentiles are computed on demand, as the computation is too
+ // expensive to be done inline.
+ void compute();
+
+ // Copy one timer to another. If reset is true then the src is reset immediately after the
+ // copy. The reset flag is exploited to make the copy faster. Any data in dst is lost.
+ static void copy(Timer &dst, Timer &src, bool reset);
+
+ private:
+ // Free any buckets.
+ void freeBuckets();
+
+ // Readings are placed in bins, which are orgzanized into decades. The decade 0 covers
+ // [0,100) in steps of 1us. Decade 1 covers [0,1000) in steps of 10us. Decade 2 covers
+ // [0,10000) in steps of 100us. And so on.
+
+ // An event is placed in the first bin that can hold it. This means that events in the range
+ // of [0,100) are placed in the first decade, events in the range of [0,1000) are placed in
+ // the second decade, and so on. This also means that the first 10% of the bins are unused
+ // in each decade after the first.
+
+ // The design provides at least two significant digits across the range of [0,10000).
+
+ static const size_t MaxDimension = 4;
+ static const size_t MaxBuckets = 100;
+
+ // The range of each dimension. The lower value is always zero.
+ static const int range[MaxDimension];
+ // The width of each bin, by dimension
+ static const int width[MaxDimension];
+
+ // A histogram of the values seen. Centuries are allocated as needed, to minimize the memory
+ // impact.
+ int *buckets[MaxDimension];
+ };
+
+ // Fetch one Timer. The function has a short-circuit behavior: if the count is zero then
+ // destination count is set to zero and the function returns false. Otherwise, the destination
+ // is a copy of the source and the function returns true. This behavior lowers the cost of
+ // handling unused timers.
+ static bool copy(int src, Timer &dst, bool reset);
+
+ // Enable the timers. Timers are initially disabled. Enabling timers allocates memory for the
+ // counters. Timers cannot be disabled.
+ static void enable();
+
+ private:
+ // An internal reset method. This does not take a lock.
+ static void reset();
+
+ // Helper method to convert a counter into an enum. Presumably, this will be inlined into zero
+ // actual cpu instructions.
+ static inline std::vector<unsigned int>::size_type toIndex(Counter c) {
+ return static_cast<std::vector<unsigned int>::size_type>(c);
+ }
+
+ // Every counter has an associated lock. The lock has been factored into a separate class to
+ // keep the Timer class a POD.
+ struct GuardedTimer {
+ Mutex lock_;
+ Timer timer_;
+ };
+
+ // Scoped timer
+ struct ScopedTimer {
+ AutoMutex _l;
+ Timer &t;
+ ScopedTimer(GuardedTimer &g) :
+ _l(g.lock_), t(g.timer_) {
+ }
+ Timer *operator->() {
+ return &t;
+ }
+ Timer& operator*() {
+ return t;
+ }
+ };
+
+ // An individual timer is active (or not), is tracking a specific API, and has a start time.
+ // The api and the start time are undefined if the timer is not active.
+ bool active_;
+ Counter api_;
+ struct timespec start_;
+
+ // The global enable flag. This is initially false and may be set true by the java runtime.
+ static std::atomic<bool> enabled_;
+
+ // The global timers. The memory for the timers is not allocated until the timers are enabled.
+ static std::atomic<GuardedTimer *> counter_;
+};
+
+} // namespace android
+
+#endif /* ANDROIDFW_RESOURCETIMER_H_ */
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 8c614bcf7145..9309091f4124 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -45,7 +45,7 @@
namespace android {
constexpr const uint32_t kIdmapMagic = 0x504D4449u;
-constexpr const uint32_t kIdmapCurrentVersion = 0x00000008u;
+constexpr const uint32_t kIdmapCurrentVersion = 0x00000009u;
// This must never change.
constexpr const uint32_t kFabricatedOverlayMagic = 0x4f525246; // FRRO (big endian)
@@ -1098,7 +1098,7 @@ struct ResTable_config
SDKVERSION_ANY = 0
};
- enum {
+ enum {
MINORVERSION_ANY = 0
};
diff --git a/libs/androidfw/tests/ResourceTimer_test.cpp b/libs/androidfw/tests/ResourceTimer_test.cpp
new file mode 100644
index 000000000000..4a1e9735de7a
--- /dev/null
+++ b/libs/androidfw/tests/ResourceTimer_test.cpp
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <android-base/file.h>
+#include <android-base/test_utils.h>
+#include <androidfw/Util.h>
+
+#include "TestHelpers.h"
+
+#include <androidfw/ResourceTimer.h>
+
+namespace android {
+
+namespace {
+
+// Create a reading in us. This is a convenience function to avoid multiplying by 1000
+// everywhere.
+unsigned int US(int us) {
+ return us * 1000;
+}
+
+}
+
+TEST(ResourceTimerTest, TimerBasic) {
+ ResourceTimer::Timer timer;
+ ASSERT_THAT(timer.count, 0);
+ ASSERT_THAT(timer.total, 0);
+
+ for (int i = 1; i <= 100; i++) {
+ timer.record(US(i));
+ }
+ ASSERT_THAT(timer.count, 100);
+ ASSERT_THAT(timer.total, US((101 * 100)/2));
+ ASSERT_THAT(timer.mintime, US(1));
+ ASSERT_THAT(timer.maxtime, US(100));
+ ASSERT_THAT(timer.pvalues.p50.floor, 0);
+ ASSERT_THAT(timer.pvalues.p50.nominal, 0);
+ ASSERT_THAT(timer.largest[0], US(100));
+ ASSERT_THAT(timer.largest[1], US(99));
+ ASSERT_THAT(timer.largest[2], US(98));
+ ASSERT_THAT(timer.largest[3], US(97));
+ ASSERT_THAT(timer.largest[4], US(96));
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(49));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(50));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(89));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(90));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(94));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(95));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(98));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(99));
+
+ // Test reset functionality. All values should be zero after the reset. Computing pvalues
+ // after the result should also yield zeros.
+ timer.reset();
+ ASSERT_THAT(timer.count, 0);
+ ASSERT_THAT(timer.total, 0);
+ ASSERT_THAT(timer.mintime, US(0));
+ ASSERT_THAT(timer.maxtime, US(0));
+ ASSERT_THAT(timer.pvalues.p50.floor, US(0));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(0));
+ ASSERT_THAT(timer.largest[0], US(0));
+ ASSERT_THAT(timer.largest[1], US(0));
+ ASSERT_THAT(timer.largest[2], US(0));
+ ASSERT_THAT(timer.largest[3], US(0));
+ ASSERT_THAT(timer.largest[4], US(0));
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(0));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(0));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(0));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(0));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(0));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(0));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(0));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(0));
+
+ // Test again, adding elements in reverse.
+ for (int i = 100; i >= 1; i--) {
+ timer.record(US(i));
+ }
+ ASSERT_THAT(timer.count, 100);
+ ASSERT_THAT(timer.total, US((101 * 100)/2));
+ ASSERT_THAT(timer.mintime, US(1));
+ ASSERT_THAT(timer.maxtime, US(100));
+ ASSERT_THAT(timer.pvalues.p50.floor, 0);
+ ASSERT_THAT(timer.pvalues.p50.nominal, 0);
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(49));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(50));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(89));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(90));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(94));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(95));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(98));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(99));
+ ASSERT_THAT(timer.largest[0], US(100));
+ ASSERT_THAT(timer.largest[1], US(99));
+ ASSERT_THAT(timer.largest[2], US(98));
+ ASSERT_THAT(timer.largest[3], US(97));
+ ASSERT_THAT(timer.largest[4], US(96));
+}
+
+TEST(ResourceTimerTest, TimerLimit) {
+ ResourceTimer::Timer timer;
+
+ // Event truncation means that a time of 1050us will be stored in the 1000us
+ // bucket. Since there is a single event, all p-values lie in the same range.
+ timer.record(US(1050));
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(900));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(1000));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(900));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(1000));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(900));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(1000));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(900));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(1000));
+}
+
+TEST(ResourceTimerTest, TimerCopy) {
+ ResourceTimer::Timer source;
+ for (int i = 1; i <= 100; i++) {
+ source.record(US(i));
+ }
+ ResourceTimer::Timer timer;
+ ResourceTimer::Timer::copy(timer, source, true);
+ ASSERT_THAT(source.count, 0);
+ ASSERT_THAT(source.total, 0);
+ // compute() is not normally be called on a reset timer, but it should work and it should return
+ // all zeros.
+ source.compute();
+ ASSERT_THAT(source.pvalues.p50.floor, US(0));
+ ASSERT_THAT(source.pvalues.p50.nominal, US(0));
+ ASSERT_THAT(source.pvalues.p90.floor, US(0));
+ ASSERT_THAT(source.pvalues.p90.nominal, US(0));
+ ASSERT_THAT(source.pvalues.p95.floor, US(0));
+ ASSERT_THAT(source.pvalues.p95.nominal, US(0));
+ ASSERT_THAT(source.pvalues.p99.floor, US(0));
+ ASSERT_THAT(source.pvalues.p99.nominal, US(0));
+ ASSERT_THAT(source.largest[0], US(0));
+ ASSERT_THAT(source.largest[1], US(0));
+ ASSERT_THAT(source.largest[2], US(0));
+ ASSERT_THAT(source.largest[3], US(0));
+ ASSERT_THAT(source.largest[4], US(0));
+
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(49));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(50));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(89));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(90));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(94));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(95));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(98));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(99));
+ ASSERT_THAT(timer.largest[0], US(100));
+ ASSERT_THAT(timer.largest[1], US(99));
+ ASSERT_THAT(timer.largest[2], US(98));
+ ASSERT_THAT(timer.largest[3], US(97));
+ ASSERT_THAT(timer.largest[4], US(96));
+
+ // Call compute a second time. The values must be the same.
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(49));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(50));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(89));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(90));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(94));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(95));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(98));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(99));
+ ASSERT_THAT(timer.largest[0], US(100));
+ ASSERT_THAT(timer.largest[1], US(99));
+ ASSERT_THAT(timer.largest[2], US(98));
+ ASSERT_THAT(timer.largest[3], US(97));
+ ASSERT_THAT(timer.largest[4], US(96));
+
+ // Modify the source. If timer and source share histogram arrays, this will introduce an
+ // error.
+ for (int i = 1; i <= 100; i++) {
+ source.record(US(i));
+ }
+ // Call compute a third time. The values must be the same.
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(49));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(50));
+ ASSERT_THAT(timer.pvalues.p90.floor, US(89));
+ ASSERT_THAT(timer.pvalues.p90.nominal, US(90));
+ ASSERT_THAT(timer.pvalues.p95.floor, US(94));
+ ASSERT_THAT(timer.pvalues.p95.nominal, US(95));
+ ASSERT_THAT(timer.pvalues.p99.floor, US(98));
+ ASSERT_THAT(timer.pvalues.p99.nominal, US(99));
+ ASSERT_THAT(timer.largest[0], US(100));
+ ASSERT_THAT(timer.largest[1], US(99));
+ ASSERT_THAT(timer.largest[2], US(98));
+ ASSERT_THAT(timer.largest[3], US(97));
+ ASSERT_THAT(timer.largest[4], US(96));
+}
+
+// Verify that if too many oversize entries are reported, the percentile values cannot be computed
+// and are set to zero.
+TEST(ResourceTimerTest, TimerOversize) {
+ static const int oversize = US(2 * 1000 * 1000);
+
+ ResourceTimer::Timer timer;
+ for (int i = 1; i <= 100; i++) {
+ timer.record(US(i));
+ }
+
+ // Insert enough oversize values to invalidate the p90, p95, and p99 percentiles. The p50 is
+ // still computable.
+ for (int i = 1; i <= 50; i++) {
+ timer.record(oversize);
+ }
+ ASSERT_THAT(timer.largest[0], oversize);
+ ASSERT_THAT(timer.largest[1], oversize);
+ ASSERT_THAT(timer.largest[2], oversize);
+ ASSERT_THAT(timer.largest[3], oversize);
+ ASSERT_THAT(timer.largest[4], oversize);
+ timer.compute();
+ ASSERT_THAT(timer.pvalues.p50.floor, US(74));
+ ASSERT_THAT(timer.pvalues.p50.nominal, US(75));
+ ASSERT_THAT(timer.pvalues.p90.floor, 0);
+ ASSERT_THAT(timer.pvalues.p90.nominal, 0);
+ ASSERT_THAT(timer.pvalues.p95.floor, 0);
+ ASSERT_THAT(timer.pvalues.p95.nominal, 0);
+ ASSERT_THAT(timer.pvalues.p99.floor, 0);
+ ASSERT_THAT(timer.pvalues.p99.nominal, 0);
+}
+
+
+} // namespace android
diff --git a/libs/androidfw/tests/data/overlay/overlay.idmap b/libs/androidfw/tests/data/overlay/overlay.idmap
index 88eadccb38cf..8e847e81aa31 100644
--- a/libs/androidfw/tests/data/overlay/overlay.idmap
+++ b/libs/androidfw/tests/data/overlay/overlay.idmap
Binary files differ
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index b11e542472da..29f37737d433 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -521,6 +521,7 @@ cc_defaults {
"Interpolator.cpp",
"LightingInfo.cpp",
"Matrix.cpp",
+ "MemoryPolicy.cpp",
"PathParser.cpp",
"Properties.cpp",
"PropertyValuesAnimatorSet.cpp",
diff --git a/libs/hwui/DeviceInfo.cpp b/libs/hwui/DeviceInfo.cpp
index 07594715a84c..f06fa2418003 100644
--- a/libs/hwui/DeviceInfo.cpp
+++ b/libs/hwui/DeviceInfo.cpp
@@ -93,12 +93,14 @@ void DeviceInfo::setWideColorDataspace(ADataSpace dataspace) {
case ADATASPACE_SCRGB:
get()->mWideColorSpace = SkColorSpace::MakeSRGB();
break;
+ default:
+ ALOGW("Unknown dataspace %d", dataspace);
+ // Treat unknown dataspaces as sRGB, so fall through
+ [[fallthrough]];
case ADATASPACE_SRGB:
// when sRGB is returned, it means wide color gamut is not supported.
get()->mWideColorSpace = SkColorSpace::MakeSRGB();
break;
- default:
- LOG_ALWAYS_FATAL("Unreachable: unsupported wide color space.");
}
}
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 7291cab364e2..b7e99994355c 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -42,6 +42,8 @@
namespace android::uirenderer {
+static constexpr auto kThreadTimeout = 60000_ms;
+
class AHBUploader;
// This helper uploader classes allows us to upload using either EGL or Vulkan using the same
// interface.
@@ -80,7 +82,7 @@ public:
}
void postIdleTimeoutCheck() {
- mUploadThread->queue().postDelayed(5000_ms, [this](){ this->idleTimeoutCheck(); });
+ mUploadThread->queue().postDelayed(kThreadTimeout, [this]() { this->idleTimeoutCheck(); });
}
protected:
@@ -97,7 +99,7 @@ private:
bool shouldTimeOutLocked() {
nsecs_t durationSince = systemTime() - mLastUpload;
- return durationSince > 2000_ms;
+ return durationSince > kThreadTimeout;
}
void idleTimeoutCheck() {
diff --git a/libs/hwui/MemoryPolicy.cpp b/libs/hwui/MemoryPolicy.cpp
new file mode 100644
index 000000000000..ca1312e75f4c
--- /dev/null
+++ b/libs/hwui/MemoryPolicy.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "MemoryPolicy.h"
+
+#include <android-base/properties.h>
+
+#include <optional>
+#include <string_view>
+
+#include "Properties.h"
+
+namespace android::uirenderer {
+
+constexpr static MemoryPolicy sDefaultMemoryPolicy;
+constexpr static MemoryPolicy sPersistentOrSystemPolicy{
+ .contextTimeout = 10_s,
+ .useAlternativeUiHidden = true,
+};
+constexpr static MemoryPolicy sLowRamPolicy{
+ .useAlternativeUiHidden = true,
+ .purgeScratchOnly = false,
+};
+constexpr static MemoryPolicy sExtremeLowRam{
+ .initialMaxSurfaceAreaScale = 0.2f,
+ .surfaceSizeMultiplier = 5 * 4.0f,
+ .backgroundRetentionPercent = 0.2f,
+ .contextTimeout = 5_s,
+ .minimumResourceRetention = 1_s,
+ .useAlternativeUiHidden = true,
+ .purgeScratchOnly = false,
+ .releaseContextOnStoppedOnly = true,
+};
+
+const MemoryPolicy& loadMemoryPolicy() {
+ if (Properties::isSystemOrPersistent) {
+ return sPersistentOrSystemPolicy;
+ }
+ std::string memoryPolicy = base::GetProperty(PROPERTY_MEMORY_POLICY, "");
+ if (memoryPolicy == "default") {
+ return sDefaultMemoryPolicy;
+ }
+ if (memoryPolicy == "lowram") {
+ return sLowRamPolicy;
+ }
+ if (memoryPolicy == "extremelowram") {
+ return sExtremeLowRam;
+ }
+
+ if (Properties::isLowRam) {
+ return sLowRamPolicy;
+ }
+ return sDefaultMemoryPolicy;
+}
+
+} // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/hwui/MemoryPolicy.h b/libs/hwui/MemoryPolicy.h
new file mode 100644
index 000000000000..e86b338608d2
--- /dev/null
+++ b/libs/hwui/MemoryPolicy.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"),
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "utils/TimeUtils.h"
+
+namespace android::uirenderer {
+
+// Values mirror those from ComponentCallbacks2.java
+enum class TrimLevel {
+ COMPLETE = 80,
+ MODERATE = 60,
+ BACKGROUND = 40,
+ UI_HIDDEN = 20,
+ RUNNING_CRITICAL = 15,
+ RUNNING_LOW = 10,
+ RUNNING_MODERATE = 5,
+};
+
+struct MemoryPolicy {
+ // The initial scale factor applied to the display resolution. The default is 1, but
+ // lower values may be used to start with a smaller initial cache size. The cache will
+ // be adjusted if larger frames are actually rendered
+ float initialMaxSurfaceAreaScale = 1.0f;
+ // The foreground cache size multiplier. The surface area of the screen will be multiplied
+ // by this
+ float surfaceSizeMultiplier = 12.0f * 4.0f;
+ // How much of the foreground cache size should be preserved when going into the background
+ float backgroundRetentionPercent = 0.5f;
+ // How long after the last renderer goes away before the GPU context is released. A value
+ // of 0 means only drop the context on background TRIM signals
+ nsecs_t contextTimeout = 0_ms;
+ // The minimum amount of time to hold onto items in the resource cache
+ // The actual time used will be the max of this & when frames were actually rendered
+ nsecs_t minimumResourceRetention = 10_s;
+ // If false, use only TRIM_UI_HIDDEN to drive background cache limits;
+ // If true, use all signals (such as all contexts are stopped) to drive the limits
+ bool useAlternativeUiHidden = false;
+ // Whether or not to only purge scratch resources when triggering UI Hidden or background
+ // collection
+ bool purgeScratchOnly = true;
+ // EXPERIMENTAL: Whether or not to trigger releasing GPU context when all contexts are stopped
+ bool releaseContextOnStoppedOnly = false;
+};
+
+const MemoryPolicy& loadMemoryPolicy();
+
+} // namespace android::uirenderer \ No newline at end of file
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 5a67eb9935dd..277955e88dfe 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -87,6 +87,10 @@ int Properties::targetCpuTimePercentage = 70;
bool Properties::enableWebViewOverlays = true;
+bool Properties::isHighEndGfx = true;
+bool Properties::isLowRam = false;
+bool Properties::isSystemOrPersistent = false;
+
StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI;
DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized;
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 2f8c67903a8b..96a517629eaa 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -193,6 +193,8 @@ enum DebugLevel {
*/
#define PROPERTY_DRAWING_ENABLED "debug.hwui.drawing_enabled"
+#define PROPERTY_MEMORY_POLICY "debug.hwui.app_memory_policy"
+
///////////////////////////////////////////////////////////////////////////////
// Misc
///////////////////////////////////////////////////////////////////////////////
@@ -292,16 +294,27 @@ public:
static bool enableWebViewOverlays;
+ static bool isHighEndGfx;
+ static bool isLowRam;
+ static bool isSystemOrPersistent;
+
static StretchEffectBehavior getStretchEffectBehavior() {
return stretchEffectBehavior;
}
static void setIsHighEndGfx(bool isHighEndGfx) {
+ Properties::isHighEndGfx = isHighEndGfx;
stretchEffectBehavior = isHighEndGfx ?
StretchEffectBehavior::ShaderHWUI :
StretchEffectBehavior::UniformScale;
}
+ static void setIsLowRam(bool isLowRam) { Properties::isLowRam = isLowRam; }
+
+ static void setIsSystemOrPersistent(bool isSystemOrPersistent) {
+ Properties::isSystemOrPersistent = isSystemOrPersistent;
+ }
+
/**
* Used for testing. Typical configuration of stretch behavior is done
* through setIsHighEndGfx
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 397975dcb78f..473afbd2aa2f 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -18,6 +18,7 @@
#include "CanvasProperty.h"
#include "NinePatchUtils.h"
+#include "SkBlendMode.h"
#include "VectorDrawable.h"
#include "hwui/Bitmap.h"
#include "hwui/MinikinUtils.h"
@@ -251,10 +252,11 @@ const SkiaCanvas::SaveRec* SkiaCanvas::currentSaveRec() const {
return (rec && rec->saveCount == currentSaveCount) ? rec : nullptr;
}
-void SkiaCanvas::punchHole(const SkRRect& rect) {
+void SkiaCanvas::punchHole(const SkRRect& rect, float alpha) {
SkPaint paint = SkPaint();
- paint.setColor(0);
- paint.setBlendMode(SkBlendMode::kClear);
+ paint.setColor(SkColors::kBlack);
+ paint.setAlphaf(alpha);
+ paint.setBlendMode(SkBlendMode::kDstOut);
mCanvas->drawRRect(rect, paint);
}
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index c6313f6c3a88..51007c52260d 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -65,7 +65,7 @@ public:
LOG_ALWAYS_FATAL("SkiaCanvas does not support enableZ");
}
- virtual void punchHole(const SkRRect& rect) override;
+ virtual void punchHole(const SkRRect& rect, float alpha) override;
virtual void setBitmap(const SkBitmap& bitmap) override;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 7378351ef771..82d23b51b12a 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -152,7 +152,7 @@ public:
LOG_ALWAYS_FATAL("Not supported");
}
- virtual void punchHole(const SkRRect& rect) = 0;
+ virtual void punchHole(const SkRRect& rect, float alpha) = 0;
// ----------------------------------------------------------------------------
// Canvas state operations
diff --git a/libs/hwui/jni/android_graphics_Canvas.cpp b/libs/hwui/jni/android_graphics_Canvas.cpp
index fb7d5f72744d..0513447ed05e 100644
--- a/libs/hwui/jni/android_graphics_Canvas.cpp
+++ b/libs/hwui/jni/android_graphics_Canvas.cpp
@@ -713,9 +713,10 @@ static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) {
}
static void punchHole(JNIEnv* env, jobject, jlong canvasPtr, jfloat left, jfloat top, jfloat right,
- jfloat bottom, jfloat rx, jfloat ry) {
+ jfloat bottom, jfloat rx, jfloat ry, jfloat alpha) {
auto canvas = reinterpret_cast<Canvas*>(canvasPtr);
- canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry));
+ canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry),
+ alpha);
}
}; // namespace CanvasJNI
@@ -790,7 +791,7 @@ static const JNINativeMethod gDrawMethods[] = {
{"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString},
{"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars},
{"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString},
- {"nPunchHole", "(JFFFFFF)V", (void*) CanvasJNI::punchHole}
+ {"nPunchHole", "(JFFFFFFF)V", (void*) CanvasJNI::punchHole}
};
int register_android_graphics_Canvas(JNIEnv* env) {
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index 4f281fcde61d..704fba9241b6 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -271,6 +271,16 @@ static void android_view_ThreadedRenderer_setIsHighEndGfx(JNIEnv* env, jobject c
Properties::setIsHighEndGfx(jIsHighEndGfx);
}
+static void android_view_ThreadedRenderer_setIsLowRam(JNIEnv* env, jobject clazz,
+ jboolean isLowRam) {
+ Properties::setIsLowRam(isLowRam);
+}
+
+static void android_view_ThreadedRenderer_setIsSystemOrPersistent(JNIEnv* env, jobject clazz,
+ jboolean isSystemOrPersistent) {
+ Properties::setIsSystemOrPersistent(isSystemOrPersistent);
+}
+
static int android_view_ThreadedRenderer_syncAndDrawFrame(JNIEnv* env, jobject clazz,
jlong proxyPtr, jlongArray frameInfo,
jint frameInfoSize) {
@@ -949,6 +959,9 @@ static const JNINativeMethod gMethods[] = {
{"nSetColorMode", "(JI)V", (void*)android_view_ThreadedRenderer_setColorMode},
{"nSetSdrWhitePoint", "(JF)V", (void*)android_view_ThreadedRenderer_setSdrWhitePoint},
{"nSetIsHighEndGfx", "(Z)V", (void*)android_view_ThreadedRenderer_setIsHighEndGfx},
+ {"nSetIsLowRam", "(Z)V", (void*)android_view_ThreadedRenderer_setIsLowRam},
+ {"nSetIsSystemOrPersistent", "(Z)V",
+ (void*)android_view_ThreadedRenderer_setIsSystemOrPersistent},
{"nSyncAndDrawFrame", "(J[JI)I", (void*)android_view_ThreadedRenderer_syncAndDrawFrame},
{"nDestroy", "(JJ)V", (void*)android_view_ThreadedRenderer_destroy},
{"nRegisterAnimatingRenderNode", "(JJ)V",
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 3bf2b2e63a47..f2282e661535 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -201,6 +201,7 @@ protected:
paint.setAlpha((uint8_t)paint.getAlpha() * mAlpha);
return true;
}
+
void onDrawDrawable(SkDrawable* drawable, const SkMatrix* matrix) override {
// We unroll the drawable using "this" canvas, so that draw calls contained inside will
// get their alpha applied. The default SkPaintFilterCanvas::onDrawDrawable does not unroll.
@@ -292,7 +293,7 @@ void RenderNodeDrawable::drawContent(SkCanvas* canvas) const {
// with the same canvas transformation + clip into the target
// canvas then draw the layer on top
if (renderNode->hasHolePunches()) {
- TransformCanvas transformCanvas(canvas, SkBlendMode::kClear);
+ TransformCanvas transformCanvas(canvas, SkBlendMode::kDstOut);
displayList->draw(&transformCanvas);
}
canvas->drawImageRect(snapshotImage, SkRect::Make(srcBounds),
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index 5c6117d86415..1f87865f2672 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -69,20 +69,22 @@ void SkiaRecordingCanvas::initDisplayList(uirenderer::RenderNode* renderNode, in
mDisplayList->setHasHolePunches(false);
}
-void SkiaRecordingCanvas::punchHole(const SkRRect& rect) {
- // Add the marker annotation to allow HWUI to determine where the current
- // clip/transformation should be applied
+void SkiaRecordingCanvas::punchHole(const SkRRect& rect, float alpha) {
+ // Add the marker annotation to allow HWUI to determine the current
+ // clip/transformation and alpha should be applied
SkVector vector = rect.getSimpleRadii();
- float data[2];
+ float data[3];
data[0] = vector.x();
data[1] = vector.y();
+ data[2] = alpha;
mRecorder.drawAnnotation(rect.rect(), HOLE_PUNCH_ANNOTATION.c_str(),
- SkData::MakeWithCopy(data, 2 * sizeof(float)));
+ SkData::MakeWithCopy(data, sizeof(data)));
// Clear the current rect within the layer itself
SkPaint paint = SkPaint();
- paint.setColor(0);
- paint.setBlendMode(SkBlendMode::kClear);
+ paint.setColor(SkColors::kBlack);
+ paint.setAlphaf(alpha);
+ paint.setBlendMode(SkBlendMode::kDstOut);
mRecorder.drawRRect(rect, paint);
mDisplayList->setHasHolePunches(true);
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 89e3a2c24e1e..7844e2cc2a73 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -50,7 +50,7 @@ public:
initDisplayList(renderNode, width, height);
}
- virtual void punchHole(const SkRRect& rect) override;
+ virtual void punchHole(const SkRRect& rect, float alpha) override;
virtual void finishRecording(uirenderer::RenderNode* destination) override;
std::unique_ptr<SkiaDisplayList> finishRecording();
diff --git a/libs/hwui/pipeline/skia/TransformCanvas.cpp b/libs/hwui/pipeline/skia/TransformCanvas.cpp
index 33160d05e2d0..c320df035d08 100644
--- a/libs/hwui/pipeline/skia/TransformCanvas.cpp
+++ b/libs/hwui/pipeline/skia/TransformCanvas.cpp
@@ -29,13 +29,15 @@ using namespace android::uirenderer::skiapipeline;
void TransformCanvas::onDrawAnnotation(const SkRect& rect, const char* key, SkData* value) {
if (HOLE_PUNCH_ANNOTATION == key) {
auto* rectParams = reinterpret_cast<const float*>(value->data());
- float radiusX = rectParams[0];
- float radiusY = rectParams[1];
+ const float radiusX = rectParams[0];
+ const float radiusY = rectParams[1];
+ const float alpha = rectParams[2];
SkRRect roundRect = SkRRect::MakeRectXY(rect, radiusX, radiusY);
SkPaint paint;
paint.setColor(SkColors::kBlack);
paint.setBlendMode(mHolePunchBlendMode);
+ paint.setAlphaf(alpha);
mWrappedCanvas->drawRRect(roundRect, paint);
}
}
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index ded2b06fb3cf..1d24e718db1a 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -16,6 +16,16 @@
#include "CacheManager.h"
+#include <GrContextOptions.h>
+#include <SkExecutor.h>
+#include <SkGraphics.h>
+#include <SkMathPriv.h>
+#include <math.h>
+#include <utils/Trace.h>
+
+#include <set>
+
+#include "CanvasContext.h"
#include "DeviceInfo.h"
#include "Layer.h"
#include "Properties.h"
@@ -25,40 +35,34 @@
#include "pipeline/skia/SkiaMemoryTracer.h"
#include "renderstate/RenderState.h"
#include "thread/CommonPool.h"
-#include <utils/Trace.h>
-
-#include <GrContextOptions.h>
-#include <SkExecutor.h>
-#include <SkGraphics.h>
-#include <SkMathPriv.h>
-#include <math.h>
-#include <set>
namespace android {
namespace uirenderer {
namespace renderthread {
-// This multiplier was selected based on historical review of cache sizes relative
-// to the screen resolution. This is meant to be a conservative default based on
-// that analysis. The 4.0f is used because the default pixel format is assumed to
-// be ARGB_8888.
-#define SURFACE_SIZE_MULTIPLIER (12.0f * 4.0f)
-#define BACKGROUND_RETENTION_PERCENTAGE (0.5f)
-
-CacheManager::CacheManager()
- : mMaxSurfaceArea(DeviceInfo::getWidth() * DeviceInfo::getHeight())
- , mMaxResourceBytes(mMaxSurfaceArea * SURFACE_SIZE_MULTIPLIER)
- , mBackgroundResourceBytes(mMaxResourceBytes * BACKGROUND_RETENTION_PERCENTAGE)
- // This sets the maximum size for a single texture atlas in the GPU font cache. If
- // necessary, the cache can allocate additional textures that are counted against the
- // total cache limits provided to Skia.
- , mMaxGpuFontAtlasBytes(GrNextSizePow2(mMaxSurfaceArea))
- // This sets the maximum size of the CPU font cache to be at least the same size as the
- // total number of GPU font caches (i.e. 4 separate GPU atlases).
- , mMaxCpuFontCacheBytes(
- std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit()))
- , mBackgroundCpuFontCacheBytes(mMaxCpuFontCacheBytes * BACKGROUND_RETENTION_PERCENTAGE) {
+CacheManager::CacheManager(RenderThread& thread)
+ : mRenderThread(thread), mMemoryPolicy(loadMemoryPolicy()) {
+ mMaxSurfaceArea = static_cast<size_t>((DeviceInfo::getWidth() * DeviceInfo::getHeight()) *
+ mMemoryPolicy.initialMaxSurfaceAreaScale);
+ setupCacheLimits();
+}
+
+void CacheManager::setupCacheLimits() {
+ mMaxResourceBytes = mMaxSurfaceArea * mMemoryPolicy.surfaceSizeMultiplier;
+ mBackgroundResourceBytes = mMaxResourceBytes * mMemoryPolicy.backgroundRetentionPercent;
+ // This sets the maximum size for a single texture atlas in the GPU font cache. If
+ // necessary, the cache can allocate additional textures that are counted against the
+ // total cache limits provided to Skia.
+ mMaxGpuFontAtlasBytes = GrNextSizePow2(mMaxSurfaceArea);
+ // This sets the maximum size of the CPU font cache to be at least the same size as the
+ // total number of GPU font caches (i.e. 4 separate GPU atlases).
+ mMaxCpuFontCacheBytes = std::max(mMaxGpuFontAtlasBytes * 4, SkGraphics::GetFontCacheLimit());
+ mBackgroundCpuFontCacheBytes = mMaxCpuFontCacheBytes * mMemoryPolicy.backgroundRetentionPercent;
+
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
+ if (mGrContext) {
+ mGrContext->setResourceCacheLimit(mMaxResourceBytes);
+ }
}
void CacheManager::reset(sk_sp<GrDirectContext> context) {
@@ -69,6 +73,7 @@ void CacheManager::reset(sk_sp<GrDirectContext> context) {
if (context) {
mGrContext = std::move(context);
mGrContext->setResourceCacheLimit(mMaxResourceBytes);
+ mLastDeferredCleanup = systemTime(CLOCK_MONOTONIC);
}
}
@@ -96,7 +101,7 @@ void CacheManager::configureContext(GrContextOptions* contextOptions, const void
contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
}
-void CacheManager::trimMemory(TrimMemoryMode mode) {
+void CacheManager::trimMemory(TrimLevel mode) {
if (!mGrContext) {
return;
}
@@ -104,21 +109,28 @@ void CacheManager::trimMemory(TrimMemoryMode mode) {
// flush and submit all work to the gpu and wait for it to finish
mGrContext->flushAndSubmit(/*syncCpu=*/true);
+ if (!Properties::isHighEndGfx && mode >= TrimLevel::MODERATE) {
+ mode = TrimLevel::COMPLETE;
+ }
+
switch (mode) {
- case TrimMemoryMode::Complete:
+ case TrimLevel::COMPLETE:
mGrContext->freeGpuResources();
SkGraphics::PurgeAllCaches();
+ mRenderThread.destroyRenderingContext();
break;
- case TrimMemoryMode::UiHidden:
+ case TrimLevel::UI_HIDDEN:
// Here we purge all the unlocked scratch resources and then toggle the resources cache
// limits between the background and max amounts. This causes the unlocked resources
// that have persistent data to be purged in LRU order.
- mGrContext->purgeUnlockedResources(true);
mGrContext->setResourceCacheLimit(mBackgroundResourceBytes);
- mGrContext->setResourceCacheLimit(mMaxResourceBytes);
SkGraphics::SetFontCacheLimit(mBackgroundCpuFontCacheBytes);
+ mGrContext->purgeUnlockedResources(mMemoryPolicy.purgeScratchOnly);
+ mGrContext->setResourceCacheLimit(mMaxResourceBytes);
SkGraphics::SetFontCacheLimit(mMaxCpuFontCacheBytes);
break;
+ default:
+ break;
}
}
@@ -147,11 +159,29 @@ void CacheManager::getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage) {
}
void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState) {
+ log.appendFormat(R"(Memory policy:
+ Max surface area: %zu
+ Max resource usage: %.2fMB (x%.0f)
+ Background retention: %.0f%% (altUiHidden = %s)
+)",
+ mMaxSurfaceArea, mMaxResourceBytes / 1000000.f,
+ mMemoryPolicy.surfaceSizeMultiplier,
+ mMemoryPolicy.backgroundRetentionPercent * 100.0f,
+ mMemoryPolicy.useAlternativeUiHidden ? "true" : "false");
+ if (Properties::isSystemOrPersistent) {
+ log.appendFormat(" IsSystemOrPersistent\n");
+ }
+ log.appendFormat(" GPU Context timeout: %" PRIu64 "\n", ns2s(mMemoryPolicy.contextTimeout));
+ size_t stoppedContexts = 0;
+ for (auto context : mCanvasContexts) {
+ if (context->isStopped()) stoppedContexts++;
+ }
+ log.appendFormat("Contexts: %zu (stopped = %zu)\n", mCanvasContexts.size(), stoppedContexts);
+
if (!mGrContext) {
- log.appendFormat("No valid cache instance.\n");
+ log.appendFormat("No GPU context.\n");
return;
}
-
std::vector<skiapipeline::ResourcePair> cpuResourceMap = {
{"skia/sk_resource_cache/bitmap_", "Bitmaps"},
{"skia/sk_resource_cache/rrect-blur_", "Masks"},
@@ -199,6 +229,8 @@ void CacheManager::dumpMemoryUsage(String8& log, const RenderState* renderState)
}
void CacheManager::onFrameCompleted() {
+ cancelDestroyContext();
+ mFrameCompletions.next() = systemTime(CLOCK_MONOTONIC);
if (ATRACE_ENABLED()) {
static skiapipeline::ATraceMemoryDump tracer;
tracer.startFrame();
@@ -210,11 +242,82 @@ void CacheManager::onFrameCompleted() {
}
}
-void CacheManager::performDeferredCleanup(nsecs_t cleanupOlderThanMillis) {
- if (mGrContext) {
- mGrContext->performDeferredCleanup(
- std::chrono::milliseconds(cleanupOlderThanMillis),
- /* scratchResourcesOnly */true);
+void CacheManager::onThreadIdle() {
+ if (!mGrContext || mFrameCompletions.size() == 0) return;
+
+ const nsecs_t now = systemTime(CLOCK_MONOTONIC);
+ // Rate limiting
+ if ((now - mLastDeferredCleanup) < 25_ms) {
+ mLastDeferredCleanup = now;
+ const nsecs_t frameCompleteNanos = mFrameCompletions[0];
+ const nsecs_t frameDiffNanos = now - frameCompleteNanos;
+ const nsecs_t cleanupMillis =
+ ns2ms(std::max(frameDiffNanos, mMemoryPolicy.minimumResourceRetention));
+ mGrContext->performDeferredCleanup(std::chrono::milliseconds(cleanupMillis),
+ mMemoryPolicy.purgeScratchOnly);
+ }
+}
+
+void CacheManager::scheduleDestroyContext() {
+ if (mMemoryPolicy.contextTimeout > 0) {
+ mRenderThread.queue().postDelayed(mMemoryPolicy.contextTimeout,
+ [this, genId = mGenerationId] {
+ if (mGenerationId != genId) return;
+ // GenID should have already stopped this, but just in
+ // case
+ if (!areAllContextsStopped()) return;
+ mRenderThread.destroyRenderingContext();
+ });
+ }
+}
+
+void CacheManager::cancelDestroyContext() {
+ if (mIsDestructionPending) {
+ mIsDestructionPending = false;
+ mGenerationId++;
+ }
+}
+
+bool CacheManager::areAllContextsStopped() {
+ for (auto context : mCanvasContexts) {
+ if (!context->isStopped()) return false;
+ }
+ return true;
+}
+
+void CacheManager::checkUiHidden() {
+ if (!mGrContext) return;
+
+ if (mMemoryPolicy.useAlternativeUiHidden && areAllContextsStopped()) {
+ trimMemory(TrimLevel::UI_HIDDEN);
+ }
+}
+
+void CacheManager::registerCanvasContext(CanvasContext* context) {
+ mCanvasContexts.push_back(context);
+ cancelDestroyContext();
+}
+
+void CacheManager::unregisterCanvasContext(CanvasContext* context) {
+ std::erase(mCanvasContexts, context);
+ checkUiHidden();
+ if (mCanvasContexts.empty()) {
+ scheduleDestroyContext();
+ }
+}
+
+void CacheManager::onContextStopped(CanvasContext* context) {
+ checkUiHidden();
+ if (mMemoryPolicy.releaseContextOnStoppedOnly && areAllContextsStopped()) {
+ scheduleDestroyContext();
+ }
+}
+
+void CacheManager::notifyNextFrameSize(int width, int height) {
+ int frameArea = width * height;
+ if (frameArea > mMaxSurfaceArea) {
+ mMaxSurfaceArea = frameArea;
+ setupCacheLimits();
}
}
diff --git a/libs/hwui/renderthread/CacheManager.h b/libs/hwui/renderthread/CacheManager.h
index af82672c6f23..d21ac9badc43 100644
--- a/libs/hwui/renderthread/CacheManager.h
+++ b/libs/hwui/renderthread/CacheManager.h
@@ -22,7 +22,11 @@
#endif
#include <SkSurface.h>
#include <utils/String8.h>
+
#include <vector>
+
+#include "MemoryPolicy.h"
+#include "utils/RingBuffer.h"
#include "utils/TimeUtils.h"
namespace android {
@@ -35,17 +39,15 @@ class RenderState;
namespace renderthread {
-class IRenderPipeline;
class RenderThread;
+class CanvasContext;
class CacheManager {
public:
- enum class TrimMemoryMode { Complete, UiHidden };
-
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
void configureContext(GrContextOptions* context, const void* identity, ssize_t size);
#endif
- void trimMemory(TrimMemoryMode mode);
+ void trimMemory(TrimLevel mode);
void trimStaleResources();
void dumpMemoryUsage(String8& log, const RenderState* renderState = nullptr);
void getMemoryUsage(size_t* cpuUsage, size_t* gpuUsage);
@@ -53,30 +55,50 @@ public:
size_t getCacheSize() const { return mMaxResourceBytes; }
size_t getBackgroundCacheSize() const { return mBackgroundResourceBytes; }
void onFrameCompleted();
+ void notifyNextFrameSize(int width, int height);
+
+ void onThreadIdle();
- void performDeferredCleanup(nsecs_t cleanupOlderThanMillis);
+ void registerCanvasContext(CanvasContext* context);
+ void unregisterCanvasContext(CanvasContext* context);
+ void onContextStopped(CanvasContext* context);
private:
friend class RenderThread;
- explicit CacheManager();
+ explicit CacheManager(RenderThread& thread);
+ void setupCacheLimits();
+ bool areAllContextsStopped();
+ void checkUiHidden();
+ void scheduleDestroyContext();
+ void cancelDestroyContext();
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
void reset(sk_sp<GrDirectContext> grContext);
#endif
void destroy();
- const size_t mMaxSurfaceArea;
+ RenderThread& mRenderThread;
+ const MemoryPolicy& mMemoryPolicy;
#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
sk_sp<GrDirectContext> mGrContext;
#endif
- const size_t mMaxResourceBytes;
- const size_t mBackgroundResourceBytes;
+ size_t mMaxSurfaceArea = 0;
+
+ size_t mMaxResourceBytes = 0;
+ size_t mBackgroundResourceBytes = 0;
+
+ size_t mMaxGpuFontAtlasBytes = 0;
+ size_t mMaxCpuFontCacheBytes = 0;
+ size_t mBackgroundCpuFontCacheBytes = 0;
+
+ std::vector<CanvasContext*> mCanvasContexts;
+ RingBuffer<uint64_t, 100> mFrameCompletions;
- const size_t mMaxGpuFontAtlasBytes;
- const size_t mMaxCpuFontCacheBytes;
- const size_t mBackgroundCpuFontCacheBytes;
+ nsecs_t mLastDeferredCleanup = 0;
+ bool mIsDestructionPending = false;
+ uint32_t mGenerationId = 0;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 75d3ff7753cb..6a0c5a8e499e 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -42,9 +42,6 @@
#include "utils/GLUtils.h"
#include "utils/TimeUtils.h"
-#define TRIM_MEMORY_COMPLETE 80
-#define TRIM_MEMORY_UI_HIDDEN 20
-
#define LOG_FRAMETIME_MMA 0
#if LOG_FRAMETIME_MMA
@@ -122,6 +119,7 @@ CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode*
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
, mRenderPipeline(std::move(renderPipeline)) {
+ mRenderThread.cacheManager().registerCanvasContext(this);
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
mProfiler.setDensity(DeviceInfo::getDensity());
@@ -133,6 +131,7 @@ CanvasContext::~CanvasContext() {
node->clearRoot();
}
mRenderNodes.clear();
+ mRenderThread.cacheManager().unregisterCanvasContext(this);
}
void CanvasContext::addRenderNode(RenderNode* node, bool placeFront) {
@@ -154,6 +153,7 @@ void CanvasContext::destroy() {
freePrefetchedLayers();
destroyHardwareResources();
mAnimationContext->destroy();
+ mRenderThread.cacheManager().onContextStopped(this);
}
static void setBufferCount(ANativeWindow* window) {
@@ -251,6 +251,7 @@ void CanvasContext::setStopped(bool stopped) {
mGenerationID++;
mRenderThread.removeFrameCallback(this);
mRenderPipeline->onStop();
+ mRenderThread.cacheManager().onContextStopped(this);
} else if (mIsDirty && hasSurface()) {
mRenderThread.postFrameCallback(this);
}
@@ -461,7 +462,6 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
}
void CanvasContext::stopDrawing() {
- cleanupResources();
mRenderThread.removeFrameCallback(this);
mAnimationContext->pauseAnimators();
mGenerationID++;
@@ -648,25 +648,10 @@ nsecs_t CanvasContext::draw() {
}
}
- cleanupResources();
mRenderThread.cacheManager().onFrameCompleted();
return mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
}
-void CanvasContext::cleanupResources() {
- auto& tracker = mJankTracker.frames();
- auto size = tracker.size();
- auto capacity = tracker.capacity();
- if (size == capacity) {
- nsecs_t nowNanos = systemTime(SYSTEM_TIME_MONOTONIC);
- nsecs_t frameCompleteNanos =
- tracker[0].get(FrameInfoIndex::FrameCompleted);
- nsecs_t frameDiffNanos = nowNanos - frameCompleteNanos;
- nsecs_t cleanupMillis = ns2ms(std::max(frameDiffNanos, 10_s));
- mRenderThread.cacheManager().performDeferredCleanup(cleanupMillis);
- }
-}
-
void CanvasContext::reportMetricsWithPresentTime() {
{ // acquire lock
std::scoped_lock lock(mFrameMetricsReporterMutex);
@@ -790,6 +775,7 @@ SkISize CanvasContext::getNextFrameSize() const {
SkISize size;
size.fWidth = ANativeWindow_getWidth(anw);
size.fHeight = ANativeWindow_getHeight(anw);
+ mRenderThread.cacheManager().notifyNextFrameSize(size.fWidth, size.fHeight);
return size;
}
@@ -868,18 +854,6 @@ void CanvasContext::destroyHardwareResources() {
}
}
-void CanvasContext::trimMemory(RenderThread& thread, int level) {
- ATRACE_CALL();
- if (!thread.getGrContext()) return;
- ATRACE_CALL();
- if (level >= TRIM_MEMORY_COMPLETE) {
- thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
- thread.destroyRenderingContext();
- } else if (level >= TRIM_MEMORY_UI_HIDDEN) {
- thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
- }
-}
-
DeferredLayerUpdater* CanvasContext::createTextureLayer() {
return mRenderPipeline->createTextureLayer();
}
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 951ee216ce35..748ab96d7e06 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -127,6 +127,7 @@ public:
void setSurfaceControl(ASurfaceControl* surfaceControl);
bool pauseSurface();
void setStopped(bool stopped);
+ bool isStopped() { return mStopped || !hasSurface(); }
bool hasSurface() const { return mNativeSurface.get(); }
void allocateBuffers();
@@ -148,7 +149,6 @@ public:
void markLayerInUse(RenderNode* node);
void destroyHardwareResources();
- static void trimMemory(RenderThread& thread, int level);
DeferredLayerUpdater* createTextureLayer();
@@ -330,8 +330,6 @@ private:
std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
std::function<void()> mPrepareSurfaceControlForWebviewCallback;
-
- void cleanupResources();
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 59c914f0198c..03f02de98efe 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -243,7 +243,9 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
mContext->unpinImages();
for (size_t i = 0; i < mLayers.size(); i++) {
- mLayers[i]->apply();
+ if (mLayers[i]) {
+ mLayers[i]->apply();
+ }
}
mLayers.clear();
mContext->setContentDrawBounds(mContentDrawBounds);
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 40a0bac3762d..332471545f82 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -196,7 +196,8 @@ void RenderProxy::trimMemory(int level) {
// Avoid creating a RenderThread to do a trimMemory.
if (RenderThread::hasInstance()) {
RenderThread& thread = RenderThread::getInstance();
- thread.queue().post([&thread, level]() { CanvasContext::trimMemory(thread, level); });
+ const auto trimLevel = static_cast<TrimLevel>(level);
+ thread.queue().post([&thread, trimLevel]() { thread.trimMemory(trimLevel); });
}
}
@@ -205,7 +206,7 @@ void RenderProxy::purgeCaches() {
RenderThread& thread = RenderThread::getInstance();
thread.queue().post([&thread]() {
if (thread.getGrContext()) {
- thread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ thread.cacheManager().trimMemory(TrimLevel::COMPLETE);
}
});
}
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3ff4081726f3..7a7f1abdd268 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -251,7 +251,7 @@ void RenderThread::initThreadLocals() {
mEglManager = new EglManager();
mRenderState = new RenderState(*this);
mVkManager = VulkanManager::getInstance();
- mCacheManager = new CacheManager();
+ mCacheManager = new CacheManager(*this);
}
void RenderThread::setupFrameInterval() {
@@ -453,6 +453,8 @@ bool RenderThread::threadLoop() {
// next vsync (oops), so none of the callbacks are run.
requestVsync();
}
+
+ mCacheManager->onThreadIdle();
}
return false;
@@ -502,6 +504,11 @@ void RenderThread::preload() {
HardwareBitmapUploader::initialize();
}
+void RenderThread::trimMemory(TrimLevel level) {
+ ATRACE_CALL();
+ cacheManager().trimMemory(level);
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index c1f6790b25b2..0a89e5e944f8 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -17,11 +17,11 @@
#ifndef RENDERTHREAD_H_
#define RENDERTHREAD_H_
-#include <surface_control_private.h>
#include <GrDirectContext.h>
#include <SkBitmap.h>
#include <cutils/compiler.h>
#include <private/android/choreographer.h>
+#include <surface_control_private.h>
#include <thread/ThreadBase.h>
#include <utils/Looper.h>
#include <utils/Thread.h>
@@ -31,6 +31,7 @@
#include <set>
#include "CacheManager.h"
+#include "MemoryPolicy.h"
#include "ProfileDataContainer.h"
#include "RenderTask.h"
#include "TimeLord.h"
@@ -172,6 +173,8 @@ public:
return mASurfaceControlFunctions;
}
+ void trimMemory(TrimLevel level);
+
/**
* isCurrent provides a way to query, if the caller is running on
* the render thread.
diff --git a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
index 59230a754f4e..7d3ca9642458 100644
--- a/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/StretchyListViewAnimation.cpp
@@ -138,7 +138,7 @@ private:
roundRectPaint.setColor(Color::White);
if (addHolePunch) {
// Punch a hole but then cover it up, we don't want to actually see it
- canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight)));
+ canvas.punchHole(SkRRect::MakeRect(SkRect::MakeWH(itemWidth, itemHeight)), 1.f);
}
canvas.drawRoundRect(0, 0, itemWidth, itemHeight, dp(6), dp(6), roundRectPaint);
@@ -235,4 +235,4 @@ class StretchyUniformLayerListViewHolePunch : public StretchyListViewAnimation {
StretchEffectBehavior stretchBehavior() override { return StretchEffectBehavior::UniformScale; }
bool haveHolePunch() override { return true; }
bool forceLayer() override { return true; }
-}; \ No newline at end of file
+};
diff --git a/libs/hwui/tests/unit/CacheManagerTests.cpp b/libs/hwui/tests/unit/CacheManagerTests.cpp
index edd3e4e4f4d4..df06ead3aa0c 100644
--- a/libs/hwui/tests/unit/CacheManagerTests.cpp
+++ b/libs/hwui/tests/unit/CacheManagerTests.cpp
@@ -58,7 +58,7 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
ASSERT_TRUE(SkImage_pinAsTexture(image.get(), grContext));
// attempt to trim all memory while we still hold strong refs
- renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
// free the surfaces
@@ -75,11 +75,11 @@ RENDERTHREAD_SKIA_PIPELINE_TEST(CacheManager, trimMemory) {
ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() < purgeableBytes);
// UI hidden and make sure only some got purged (unique should remain)
- renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::UiHidden);
+ renderThread.cacheManager().trimMemory(TrimLevel::UI_HIDDEN);
ASSERT_TRUE(0 < grContext->getResourceCachePurgeableBytes());
ASSERT_TRUE(renderThread.cacheManager().getBackgroundCacheSize() > getCacheUsage(grContext));
// complete and make sure all get purged
- renderThread.cacheManager().trimMemory(CacheManager::TrimMemoryMode::Complete);
+ renderThread.cacheManager().trimMemory(TrimLevel::COMPLETE);
ASSERT_TRUE(0 == grContext->getResourceCachePurgeableBytes());
}
diff --git a/location/java/android/location/LocationTime.java b/location/java/android/location/LocationTime.java
index e5535d192776..2f03508fbb15 100644
--- a/location/java/android/location/LocationTime.java
+++ b/location/java/android/location/LocationTime.java
@@ -20,28 +20,32 @@ import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import java.time.Duration;
+import java.time.Instant;
+
/**
- * Data class for passing location derived time.
+ * Data class for passing GNSS-derived time.
* @hide
*/
public final class LocationTime implements Parcelable {
- private final long mTime;
+ private final long mUnixEpochTimeMillis;
private final long mElapsedRealtimeNanos;
- public LocationTime(long time, long elapsedRealtimeNanos) {
- mTime = time;
+ public LocationTime(long unixEpochTimeMillis, long elapsedRealtimeNanos) {
+ mUnixEpochTimeMillis = unixEpochTimeMillis;
mElapsedRealtimeNanos = elapsedRealtimeNanos;
}
/**
- * The current time, according to the Gnss location provider. */
- public long getTime() {
- return mTime;
+ * The Unix epoch time in millis, according to the Gnss location provider.
+ */
+ public long getUnixEpochTimeMillis() {
+ return mUnixEpochTimeMillis;
}
/**
- * The elapsed nanos since boot {@link #getTime} was computed at.
+ * The elapsed nanos since boot when {@link #getUnixEpochTimeMillis} was the current time.
*/
public long getElapsedRealtimeNanos() {
return mElapsedRealtimeNanos;
@@ -49,7 +53,7 @@ public final class LocationTime implements Parcelable {
@Override
public void writeToParcel(Parcel out, int flags) {
- out.writeLong(mTime);
+ out.writeLong(mUnixEpochTimeMillis);
out.writeLong(mElapsedRealtimeNanos);
}
@@ -58,8 +62,18 @@ public final class LocationTime implements Parcelable {
return 0;
}
+ @Override
+ public String toString() {
+ return "LocationTime{"
+ + "mUnixEpochTimeMillis=" + Instant.ofEpochMilli(mUnixEpochTimeMillis)
+ + "(" + mUnixEpochTimeMillis + ")"
+ + ", mElapsedRealtimeNanos=" + Duration.ofNanos(mElapsedRealtimeNanos)
+ + "(" + mElapsedRealtimeNanos + ")"
+ + '}';
+ }
+
public static final @NonNull Parcelable.Creator<LocationTime> CREATOR =
- new Parcelable.Creator<LocationTime>() {
+ new Parcelable.Creator<>() {
public LocationTime createFromParcel(Parcel in) {
long time = in.readLong();
long elapsedRealtimeNanos = in.readLong();
diff --git a/media/java/android/media/AudioAttributes.java b/media/java/android/media/AudioAttributes.java
index 546f0c665a98..dea6097ad89c 100644
--- a/media/java/android/media/AudioAttributes.java
+++ b/media/java/android/media/AudioAttributes.java
@@ -1433,6 +1433,8 @@ public final class AudioAttributes implements Parcelable {
return new String("AudioAttributes:"
+ " usage=" + usageToString()
+ " content=" + contentTypeToString()
+ + (mSource != MediaRecorder.AudioSource.AUDIO_SOURCE_INVALID
+ ? " source=" + MediaRecorder.toLogFriendlyAudioSource(mSource) : "")
+ " flags=0x" + Integer.toHexString(mFlags).toUpperCase()
+ " tags=" + mFormattedTags
+ " bundle=" + (mBundle == null ? "null" : mBundle.toString()));
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index d06bbc61f6eb..980accae733b 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -516,6 +516,9 @@ public final class AudioFormat implements Parcelable {
/** 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);
+ /** Output channel mask for 6.1
+ * Same as 5.1 with the addition of the back center channel */
+ public static final int CHANNEL_OUT_6POINT1 = (CHANNEL_OUT_5POINT1 | CHANNEL_OUT_BACK_CENTER);
/** @hide */
public static final int CHANNEL_OUT_5POINT1_SIDE = (CHANNEL_OUT_FRONT_LEFT | CHANNEL_OUT_FRONT_RIGHT |
CHANNEL_OUT_FRONT_CENTER | CHANNEL_OUT_LOW_FREQUENCY |
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 60c812ad048f..819358b49754 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -677,21 +677,39 @@ public final class AudioPlaybackConfiguration implements Parcelable {
@Override
public String toString() {
- return "AudioPlaybackConfiguration piid:" + mPlayerIId
- + " deviceId:" + mDeviceId
- + " type:" + toLogFriendlyPlayerType(mPlayerType)
- + " u/pid:" + mClientUid + "/" + mClientPid
- + " state:" + toLogFriendlyPlayerState(mPlayerState)
- + " attr:" + mPlayerAttr
- + " sessionId:" + mSessionId
- + " mutedState:"
- + " muteFromMasterMute=" + ((mMutedState & PLAYER_MUTE_MASTER) != 0)
- + " muteFromStreamVolume=" + ((mMutedState & PLAYER_MUTE_STREAM_VOLUME) != 0)
- + " muteFromStreamMuted=" + ((mMutedState & PLAYER_MUTE_STREAM_MUTED) != 0)
- + " muteFromPlaybackRestricted=" + ((mMutedState & PLAYER_MUTE_PLAYBACK_RESTRICTED)
- != 0)
- + " muteFromClientVolume=" + ((mMutedState & PLAYER_MUTE_CLIENT_VOLUME) != 0)
- + " muteFromVolumeShaper=" + ((mMutedState & PLAYER_MUTE_VOLUME_SHAPER) != 0);
+ StringBuilder apcToString = new StringBuilder();
+ apcToString.append("AudioPlaybackConfiguration piid:").append(mPlayerIId).append(
+ " deviceId:").append(mDeviceId).append(" type:").append(
+ toLogFriendlyPlayerType(mPlayerType)).append(" u/pid:").append(mClientUid).append(
+ "/").append(mClientPid).append(" state:").append(
+ toLogFriendlyPlayerState(mPlayerState)).append(" attr:").append(mPlayerAttr).append(
+ " sessionId:").append(mSessionId).append(" mutedState:");
+ if (mMutedState == PLAYER_MUTE_INVALID) {
+ apcToString.append("invalid ");
+ } else if (mMutedState == 0) {
+ apcToString.append("none ");
+ } else {
+ if ((mMutedState & PLAYER_MUTE_MASTER) != 0) {
+ apcToString.append("master ");
+ }
+ if ((mMutedState & PLAYER_MUTE_STREAM_VOLUME) != 0) {
+ apcToString.append("streamVolume ");
+ }
+ if ((mMutedState & PLAYER_MUTE_STREAM_MUTED) != 0) {
+ apcToString.append("streamMute ");
+ }
+ if ((mMutedState & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0) {
+ apcToString.append("playbackRestricted ");
+ }
+ if ((mMutedState & PLAYER_MUTE_CLIENT_VOLUME) != 0) {
+ apcToString.append("clientVolume ");
+ }
+ if ((mMutedState & PLAYER_MUTE_VOLUME_SHAPER) != 0) {
+ apcToString.append("volumeShaper ");
+ }
+ }
+
+ return apcToString.toString();
}
//=====================================================================
diff --git a/media/java/android/media/MediaFormat.java b/media/java/android/media/MediaFormat.java
index be9862bf1137..49b314d8481d 100644
--- a/media/java/android/media/MediaFormat.java
+++ b/media/java/android/media/MediaFormat.java
@@ -242,13 +242,14 @@ public final class MediaFormat {
* To decode such an image, {@link MediaCodec} decoder for
* {@link #MIMETYPE_VIDEO_HEVC} shall be used. The client needs to form
* the correct {@link #MediaFormat} based on additional information in
- * the track format, and send it to {@link MediaCodec#configure}.
+ * the track format (shown in the next paragraph), and send it to
+ * {@link MediaCodec#configure}.
*
* The track's MediaFormat will come with {@link #KEY_WIDTH} and
* {@link #KEY_HEIGHT} keys, which describes the width and height
* of the image. If the image doesn't contain grid (i.e. none of
* {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT},
- * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present}), the
+ * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present), the
* track will contain a single sample of coded data for the entire image,
* and the image width and height should be used to set up the decoder.
*
@@ -266,6 +267,36 @@ public final class MediaFormat {
public static final String MIMETYPE_IMAGE_ANDROID_HEIC = "image/vnd.android.heic";
/**
+ * MIME type for AVIF still image data encoded in AV1.
+ *
+ * To decode such an image, {@link MediaCodec} decoder for
+ * {@link #MIMETYPE_VIDEO_AV1} shall be used. The client needs to form
+ * the correct {@link #MediaFormat} based on additional information in
+ * the track format (shown in the next paragraph), and send it to
+ * {@link MediaCodec#configure}.
+ *
+ * The track's MediaFormat will come with {@link #KEY_WIDTH} and
+ * {@link #KEY_HEIGHT} keys, which describes the width and height
+ * of the image. If the image doesn't contain grid (i.e. none of
+ * {@link #KEY_TILE_WIDTH}, {@link #KEY_TILE_HEIGHT},
+ * {@link #KEY_GRID_ROWS}, {@link #KEY_GRID_COLUMNS} are present), the
+ * track will contain a single sample of coded data for the entire image,
+ * and the image width and height should be used to set up the decoder.
+ *
+ * If the image does come with grid, each sample from the track will
+ * contain one tile in the grid, of which the size is described by
+ * {@link #KEY_TILE_WIDTH} and {@link #KEY_TILE_HEIGHT}. This size
+ * (instead of {@link #KEY_WIDTH} and {@link #KEY_HEIGHT}) should be
+ * used to set up the decoder. The track contains {@link #KEY_GRID_ROWS}
+ * by {@link #KEY_GRID_COLUMNS} samples in row-major, top-row first,
+ * left-to-right order. The output image should be reconstructed by
+ * first tiling the decoding results of the tiles in the correct order,
+ * then trimming (before rotation is applied) on the bottom and right
+ * side, if the tiled area is larger than the image width and height.
+ */
+ public static final String MIMETYPE_IMAGE_AVIF = "image/avif";
+
+ /**
* MIME type for WebVTT subtitle data.
*/
public static final String MIMETYPE_TEXT_VTT = "text/vtt";
@@ -485,9 +516,11 @@ public final class MediaFormat {
/**
* A key describing the width (in pixels) of each tile of the content in a
- * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+ * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+ * The associated value is an integer.
*
- * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+ * instructions of such tracks.
*
* @see #KEY_TILE_HEIGHT
* @see #KEY_GRID_ROWS
@@ -497,9 +530,11 @@ public final class MediaFormat {
/**
* A key describing the height (in pixels) of each tile of the content in a
- * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+ * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+ * The associated value is an integer.
*
- * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+ * instructions of such tracks.
*
* @see #KEY_TILE_WIDTH
* @see #KEY_GRID_ROWS
@@ -509,9 +544,11 @@ public final class MediaFormat {
/**
* A key describing the number of grid rows in the content in a
- * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+ * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+ * The associated value is an integer.
*
- * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+ * instructions of such tracks.
*
* @see #KEY_TILE_WIDTH
* @see #KEY_TILE_HEIGHT
@@ -521,9 +558,11 @@ public final class MediaFormat {
/**
* A key describing the number of grid columns in the content in a
- * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track. The associated value is an integer.
+ * {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track.
+ * The associated value is an integer.
*
- * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} on decoding instructions of such tracks.
+ * Refer to {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} on decoding
+ * instructions of such tracks.
*
* @see #KEY_TILE_WIDTH
* @see #KEY_TILE_HEIGHT
@@ -1350,9 +1389,9 @@ public final class MediaFormat {
* selected in the absence of a specific user choice.
* This is currently used in two scenarios:
* 1) for subtitle tracks, when the user selected 'Default' for the captioning locale.
- * 2) for a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} track, indicating the image is the
- * primary item in the file.
-
+ * 2) for a {@link #MIMETYPE_IMAGE_ANDROID_HEIC} / {@link #MIMETYPE_IMAGE_AVIF} track,
+ * indicating the image is the primary item in the file.
+ *
* The associated value is an integer, where non-0 means TRUE. This is an optional
* field; if not specified, DEFAULT is considered to be FALSE.
*/
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 63173207ec17..aeb81c12ad6d 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -696,6 +696,13 @@ public class MediaPlayer extends PlayerBase
baseRegisterPlayer(sessionId);
}
+ private Parcel createPlayerIIdParcel() {
+ Parcel parcel = newRequest();
+ parcel.writeInt(INVOKE_ID_SET_PLAYER_IID);
+ parcel.writeInt(mPlayerIId);
+ return parcel;
+ }
+
/*
* Update the MediaPlayer SurfaceTexture.
* Call after setting a new display surface.
@@ -712,6 +719,7 @@ public class MediaPlayer extends PlayerBase
private static final int INVOKE_ID_DESELECT_TRACK = 5;
private static final int INVOKE_ID_SET_VIDEO_SCALE_MODE = 6;
private static final int INVOKE_ID_GET_SELECTED_TRACK = 7;
+ private static final int INVOKE_ID_SET_PLAYER_IID = 8;
/**
* Create a request parcel which can be routed to the native media
@@ -1309,16 +1317,26 @@ public class MediaPlayer extends PlayerBase
* @throws IllegalStateException if it is called in an invalid state
*/
public void prepare() throws IOException, IllegalStateException {
- _prepare();
+ Parcel piidParcel = createPlayerIIdParcel();
+ try {
+ int retCode = _prepare(piidParcel);
+ if (retCode != 0) {
+ Log.w(TAG, "prepare(): could not set piid " + mPlayerIId);
+ }
+ } finally {
+ piidParcel.recycle();
+ }
scanInternalSubtitleTracks();
// DrmInfo, if any, has been resolved by now.
synchronized (mDrmLock) {
mDrmInfoResolved = true;
}
+
}
- private native void _prepare() throws IOException, IllegalStateException;
+ /** Returns the result of sending the {@code piidParcel} to the MediaPlayerService. */
+ private native int _prepare(Parcel piidParcel) throws IOException, IllegalStateException;
/**
* Prepares the player for playback, asynchronously.
@@ -1330,7 +1348,20 @@ public class MediaPlayer extends PlayerBase
*
* @throws IllegalStateException if it is called in an invalid state
*/
- public native void prepareAsync() throws IllegalStateException;
+ public void prepareAsync() throws IllegalStateException {
+ Parcel piidParcel = createPlayerIIdParcel();
+ try {
+ int retCode = _prepareAsync(piidParcel);
+ if (retCode != 0) {
+ Log.w(TAG, "prepareAsync(): could not set piid " + mPlayerIId);
+ }
+ } finally {
+ piidParcel.recycle();
+ }
+ }
+
+ /** Returns the result of sending the {@code piidParcel} to the MediaPlayerService. */
+ private native int _prepareAsync(Parcel piidParcel) throws IllegalStateException;
/**
* Starts or resumes playback. If playback had previously been paused,
diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java
index 491a8ec2c130..87634aa37fea 100644
--- a/media/java/android/media/audio/common/AidlConversion.java
+++ b/media/java/android/media/audio/common/AidlConversion.java
@@ -355,13 +355,7 @@ public class AidlConversion {
case AudioChannelLayout.LAYOUT_5POINT1POINT4:
return AudioFormat.CHANNEL_OUT_5POINT1POINT4;
case AudioChannelLayout.LAYOUT_6POINT1:
- return AudioFormat.CHANNEL_OUT_FRONT_LEFT
- | AudioFormat.CHANNEL_OUT_FRONT_RIGHT
- | AudioFormat.CHANNEL_OUT_FRONT_CENTER
- | AudioFormat.CHANNEL_OUT_LOW_FREQUENCY
- | AudioFormat.CHANNEL_OUT_BACK_LEFT
- | AudioFormat.CHANNEL_OUT_BACK_RIGHT
- | AudioFormat.CHANNEL_OUT_BACK_CENTER;
+ return AudioFormat.CHANNEL_OUT_6POINT1;
case AudioChannelLayout.LAYOUT_7POINT1:
return AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
case AudioChannelLayout.LAYOUT_7POINT1POINT2:
diff --git a/media/java/android/media/audiopolicy/AudioProductStrategy.java b/media/java/android/media/audiopolicy/AudioProductStrategy.java
index f957498237a8..98819a3582a6 100644
--- a/media/java/android/media/audiopolicy/AudioProductStrategy.java
+++ b/media/java/android/media/audiopolicy/AudioProductStrategy.java
@@ -22,7 +22,6 @@ import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.media.AudioAttributes;
import android.media.AudioSystem;
-import android.media.MediaRecorder;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -130,9 +129,7 @@ public final class AudioProductStrategy implements Parcelable {
return aa;
}
}
- return new AudioAttributes.Builder()
- .setContentType(AudioAttributes.CONTENT_TYPE_UNKNOWN)
- .setUsage(AudioAttributes.USAGE_UNKNOWN).build();
+ return DEFAULT_ATTRIBUTES;
}
/**
@@ -217,7 +214,7 @@ public final class AudioProductStrategy implements Parcelable {
@SystemApi
public @NonNull AudioAttributes getAudioAttributes() {
// We need a choice, so take the first one
- return mAudioAttributesGroups.length == 0 ? (new AudioAttributes.Builder().build())
+ return mAudioAttributesGroups.length == 0 ? DEFAULT_ATTRIBUTES
: mAudioAttributesGroups[0].getAudioAttributes();
}
@@ -358,8 +355,7 @@ public final class AudioProductStrategy implements Parcelable {
* Default attributes, with default source to be aligned with native.
*/
private static final @NonNull AudioAttributes DEFAULT_ATTRIBUTES =
- new AudioAttributes.Builder().setCapturePreset(MediaRecorder.AudioSource.DEFAULT)
- .build();
+ new AudioAttributes.Builder().build();
/**
* @hide
@@ -429,8 +425,7 @@ public final class AudioProductStrategy implements Parcelable {
public @NonNull AudioAttributes getAudioAttributes() {
// We need a choice, so take the first one
- return mAudioAttributes.length == 0 ? (new AudioAttributes.Builder().build())
- : mAudioAttributes[0];
+ return mAudioAttributes.length == 0 ? DEFAULT_ATTRIBUTES : mAudioAttributes[0];
}
/**
diff --git a/media/java/android/media/projection/MediaProjectionGlobal.java b/media/java/android/media/projection/MediaProjectionGlobal.java
new file mode 100644
index 000000000000..4374a052dad2
--- /dev/null
+++ b/media/java/android/media/projection/MediaProjectionGlobal.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.projection;
+
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.content.pm.IPackageManager;
+import android.hardware.display.DisplayManagerGlobal;
+import android.hardware.display.IDisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.hardware.display.VirtualDisplayConfig;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.view.Surface;
+
+/**
+ * This is a helper for MediaProjection when requests are made from outside an application. This
+ * should only be used by processes running as shell as a way to capture recordings without being
+ * an application. The requests will fail if coming from any process that's not Shell.
+ * @hide
+ */
+@SystemApi
+public class MediaProjectionGlobal {
+ private static final Object sLock = new Object();
+ private static MediaProjectionGlobal sInstance;
+
+ /**
+ * @return The instance of {@link MediaProjectionGlobal}
+ */
+ @NonNull
+ public static MediaProjectionGlobal getInstance() {
+ synchronized (sLock) {
+ if (sInstance == null) {
+ final IBinder displayBinder = ServiceManager.getService(Context.DISPLAY_SERVICE);
+ final IBinder packageBinder = ServiceManager.getService("package");
+ if (displayBinder != null && packageBinder != null) {
+ sInstance = new MediaProjectionGlobal(
+ IDisplayManager.Stub.asInterface(displayBinder),
+ IPackageManager.Stub.asInterface(packageBinder));
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ private final IDisplayManager mDm;
+ private final IPackageManager mPackageManager;
+
+ private MediaProjectionGlobal(IDisplayManager dm, IPackageManager packageManager) {
+ mDm = dm;
+ mPackageManager = packageManager;
+ }
+
+ /**
+ * Creates a VirtualDisplay that will mirror the content of displayIdToMirror
+ * @param name The name for the virtual display
+ * @param width The initial width for the virtual display
+ * @param height The initial height for the virtual display
+ * @param displayIdToMirror The displayId that will be mirrored into the virtual display.
+ * @return VirtualDisplay that can be used to update properties.
+ */
+ @Nullable
+ public VirtualDisplay createVirtualDisplay(@NonNull String name, int width, int height,
+ int displayIdToMirror, @Nullable Surface surface) {
+
+ // Density doesn't matter since this virtual display is only used for mirroring.
+ VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
+ height, 1 /* densityDpi */)
+ .setFlags(VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR)
+ .setDisplayIdToMirror(displayIdToMirror);
+ if (surface != null) {
+ builder.setSurface(surface);
+ }
+ VirtualDisplayConfig virtualDisplayConfig = builder.build();
+
+ String[] packages;
+ try {
+ packages = mPackageManager.getPackagesForUid(Process.myUid());
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+
+ // Just use the first one since it just needs to match the package when looking it up by
+ // calling UID in system server.
+ // The call may come from a rooted device, in that case the requesting uid will be root so
+ // it will not have any package name
+ String packageName = packages == null ? null : packages[0];
+ DisplayManagerGlobal.VirtualDisplayCallback
+ callbackWrapper = new DisplayManagerGlobal.VirtualDisplayCallback(null, null);
+ int displayId;
+ try {
+ displayId = mDm.createVirtualDisplay(virtualDisplayConfig, callbackWrapper, null,
+ packageName);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ return DisplayManagerGlobal.getInstance().createVirtualDisplayWrapper(virtualDisplayConfig,
+ null, callbackWrapper, displayId);
+ }
+}
diff --git a/media/java/android/media/session/PlaybackState.java b/media/java/android/media/session/PlaybackState.java
index 9eacc74843f9..7891ee6d5861 100644
--- a/media/java/android/media/session/PlaybackState.java
+++ b/media/java/android/media/session/PlaybackState.java
@@ -333,7 +333,11 @@ public final class PlaybackState implements Parcelable {
@Override
public String toString() {
StringBuilder bob = new StringBuilder("PlaybackState {");
- bob.append("state=").append(mState);
+ bob.append("state=")
+ .append(getStringForStateInt(mState))
+ .append("(")
+ .append(mState)
+ .append(")");
bob.append(", position=").append(mPosition);
bob.append(", buffered position=").append(mBufferedPosition);
bob.append(", speed=").append(mSpeed);
@@ -533,6 +537,38 @@ public final class PlaybackState implements Parcelable {
}
};
+ /** Returns a human readable string representation of the given int {@code state} */
+ private static String getStringForStateInt(int state) {
+ switch (state) {
+ case STATE_NONE:
+ return "NONE";
+ case STATE_STOPPED:
+ return "STOPPED";
+ case STATE_PAUSED:
+ return "PAUSED";
+ case STATE_PLAYING:
+ return "PLAYING";
+ case STATE_FAST_FORWARDING:
+ return "FAST_FORWARDING";
+ case STATE_REWINDING:
+ return "REWINDING";
+ case STATE_BUFFERING:
+ return "BUFFERING";
+ case STATE_ERROR:
+ return "ERROR";
+ case STATE_CONNECTING:
+ return "CONNECTING";
+ case STATE_SKIPPING_TO_PREVIOUS:
+ return "SKIPPING_TO_PREVIOUS";
+ case STATE_SKIPPING_TO_NEXT:
+ return "SKIPPING_TO_NEXT";
+ case STATE_SKIPPING_TO_QUEUE_ITEM:
+ return "SKIPPING_TO_QUEUE_ITEM";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
/**
* {@link PlaybackState.CustomAction CustomActions} can be used to extend the capabilities of
* the standard transport controls by exposing app specific actions to
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index a548a472fc3a..da920bb63178 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -369,13 +369,13 @@ android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsu
setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
}
-static void
-android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
+static jint
+android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz, jobject piidParcel)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
+ if (mp == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
+ return UNKNOWN_ERROR;
}
// Handle the case where the display surface was set before the mp was
@@ -384,15 +384,20 @@ android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
mp->setVideoSurfaceTexture(st);
process_media_player_call( env, thiz, mp->prepare(), "java/io/IOException", "Prepare failed." );
+
+ // update the piid
+ Parcel *request = parcelForJavaObject(env, piidParcel);
+ auto reply = std::make_unique<Parcel>();
+ return static_cast<jint>(mp->invoke(*request, reply.get()));
}
-static void
-android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
+static jint
+android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz, jobject piidParcel)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
+ if (mp == nullptr) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return;
+ return UNKNOWN_ERROR;
}
// Handle the case where the display surface was set before the mp was
@@ -401,6 +406,11 @@ android_media_MediaPlayer_prepareAsync(JNIEnv *env, jobject thiz)
mp->setVideoSurfaceTexture(st);
process_media_player_call( env, thiz, mp->prepareAsync(), "java/io/IOException", "Prepare Async failed." );
+
+ // update the piid
+ Parcel *request = parcelForJavaObject(env, piidParcel);
+ auto reply = std::make_unique<Parcel>();
+ return static_cast<jint>(mp->invoke(*request, reply.get()));
}
static void
@@ -1380,8 +1390,8 @@ static const JNINativeMethod gMethods[] = {
{"_setDataSource", "(Ljava/io/FileDescriptor;JJ)V", (void *)android_media_MediaPlayer_setDataSourceFD},
{"_setDataSource", "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
- {"_prepare", "()V", (void *)android_media_MediaPlayer_prepare},
- {"prepareAsync", "()V", (void *)android_media_MediaPlayer_prepareAsync},
+ {"_prepare", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_prepare},
+ {"_prepareAsync", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_prepareAsync},
{"_start", "()V", (void *)android_media_MediaPlayer_start},
{"_stop", "()V", (void *)android_media_MediaPlayer_stop},
{"getVideoWidth", "()I", (void *)android_media_MediaPlayer_getVideoWidth},
diff --git a/media/jni/android_media_MediaProfiles.cpp b/media/jni/android_media_MediaProfiles.cpp
index ecb1d510f807..9868a148d998 100644
--- a/media/jni/android_media_MediaProfiles.cpp
+++ b/media/jni/android_media_MediaProfiles.cpp
@@ -255,21 +255,21 @@ android_media_MediaProfiles_native_get_camcorder_profiles(JNIEnv *env, jobject /
jmethodID audioProfileConstructorMethodID =
env->GetMethodID(audioProfileClazz, "<init>", "(IIIII)V");
- jobjectArray videoCodecs = (jobjectArray)env->NewObjectArray(
- cp->getVideoCodecs().size(), videoProfileClazz, nullptr);
+ jobjectArray videoCodecs = nullptr;
{
- int i = 0;
+ auto isAdvancedCodec = [](const MediaProfiles::VideoCodec *vc) -> bool {
+ return ((vc->getBitDepth() != 8
+ || vc->getChromaSubsampling() != CHROMA_SUBSAMPLING_YUV_420
+ || vc->getHdrFormat() != HDR_FORMAT_NONE));
+ };
+ std::vector<jobject> codecVector;
for (const MediaProfiles::VideoCodec *vc : cp->getVideoCodecs()) {
+ if (isAdvancedCodec(vc) && !static_cast<bool>(advanced)) {
+ continue;
+ }
chroma_subsampling cs = vc->getChromaSubsampling();
int bitDepth = vc->getBitDepth();
hdr_format hdr = vc->getHdrFormat();
-
- bool isAdvanced =
- (bitDepth != 8 || cs != CHROMA_SUBSAMPLING_YUV_420 || hdr != HDR_FORMAT_NONE);
- if (static_cast<bool>(advanced) && !isAdvanced) {
- continue;
- }
-
jobject videoCodec = env->NewObject(videoProfileClazz,
videoProfileConstructorMethodID,
vc->getCodec(),
@@ -281,10 +281,17 @@ android_media_MediaProfiles_native_get_camcorder_profiles(JNIEnv *env, jobject /
static_cast<int>(cs),
bitDepth,
static_cast<int>(hdr));
- env->SetObjectArrayElement(videoCodecs, i++, videoCodec);
+
+ codecVector.push_back(videoCodec);
}
- }
+ videoCodecs = (jobjectArray)env->NewObjectArray(codecVector.size(),
+ videoProfileClazz, nullptr);
+ int i = 0;
+ for (jobject codecObj : codecVector) {
+ env->SetObjectArrayElement(videoCodecs, i++, codecObj);
+ }
+ }
jobjectArray audioCodecs;
if (quality >= CAMCORDER_QUALITY_TIME_LAPSE_LIST_START
&& quality <= CAMCORDER_QUALITY_TIME_LAPSE_LIST_END) {
diff --git a/media/native/midi/Android.bp b/media/native/midi/Android.bp
index 7acb8c744ba7..a991a71fd3f1 100644
--- a/media/native/midi/Android.bp
+++ b/media/native/midi/Android.bp
@@ -74,4 +74,8 @@ ndk_library {
symbol_file: "libamidi.map.txt",
first_version: "29",
+ export_header_libs: [
+ "amidi",
+ ],
+
}
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 32b7a0780e63..8594ba5ca2da 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -27,6 +27,9 @@ ndk_library {
symbol_file: "libandroid.map.txt",
first_version: "9",
unversioned_until: "current",
+ export_header_libs: [
+ "libandroid_headers",
+ ],
}
cc_defaults {
diff --git a/native/android/OWNERS b/native/android/OWNERS
index cfe973400c98..d41652f2ad17 100644
--- a/native/android/OWNERS
+++ b/native/android/OWNERS
@@ -23,3 +23,6 @@ per-file hardware_buffer_jni.cpp = file:/graphics/java/android/graphics/OWNERS
per-file native_window_jni.cpp = file:/graphics/java/android/graphics/OWNERS
per-file surface_control.cpp = file:/graphics/java/android/graphics/OWNERS
per-file surface_texture.cpp = file:/graphics/java/android/graphics/OWNERS
+
+# Input
+per-file input.cpp = file:/INPUT_OWNERS
diff --git a/native/android/input.cpp b/native/android/input.cpp
index a231d8f153e7..812db0f1c507 100644
--- a/native/android/input.cpp
+++ b/native/android/input.cpp
@@ -295,6 +295,8 @@ int32_t AMotionEvent_getClassification(const AInputEvent* motion_event) {
return AMOTION_EVENT_CLASSIFICATION_AMBIGUOUS_GESTURE;
case android::MotionClassification::DEEP_PRESS:
return AMOTION_EVENT_CLASSIFICATION_DEEP_PRESS;
+ case android::MotionClassification::TWO_FINGER_SWIPE:
+ return AMOTION_EVENT_CLASSIFICATION_TWO_FINGER_SWIPE;
}
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 1f96617a1ede..cb0f22f974ad 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -238,6 +238,7 @@ LIBANDROID {
ASurfaceControl_createFromWindow; # introduced=29
ASurfaceControl_acquire; # introduced=31
ASurfaceControl_release; # introduced=29
+ ASurfaceControl_fromSurfaceControl; # introduced=34
ASurfaceTexture_acquireANativeWindow; # introduced=28
ASurfaceTexture_attachToGLContext; # introduced=28
ASurfaceTexture_detachFromGLContext; # introduced=28
@@ -255,6 +256,7 @@ LIBANDROID {
ASurfaceTransaction_apply; # introduced=29
ASurfaceTransaction_create; # introduced=29
ASurfaceTransaction_delete; # introduced=29
+ ASurfaceTransaction_fromTransaction; # introduced=34
ASurfaceTransaction_reparent; # introduced=29
ASurfaceTransaction_setBuffer; # introduced=29
ASurfaceTransaction_setBufferAlpha; # introduced=29
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 42f4406ce5e8..9e4d72671502 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -17,6 +17,8 @@
#include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
#include <android/native_window.h>
#include <android/surface_control.h>
+#include <android/surface_control_jni.h>
+#include <android_runtime/android_view_SurfaceControl.h>
#include <configstore/Utils.h>
#include <gui/HdrMetadata.h>
#include <gui/ISurfaceComposer.h>
@@ -28,6 +30,8 @@
#include <ui/DynamicDisplayInfo.h>
#include <utils/Timers.h>
+#include <utility>
+
using namespace android::hardware::configstore;
using namespace android::hardware::configstore::V1_0;
using namespace android;
@@ -134,6 +138,11 @@ void ASurfaceControl_release(ASurfaceControl* aSurfaceControl) {
SurfaceControl_release(surfaceControl);
}
+ASurfaceControl* ASurfaceControl_fromSurfaceControl(JNIEnv* env, jobject surfaceControlObj) {
+ return reinterpret_cast<ASurfaceControl*>(
+ android_view_SurfaceControl_getNativeSurfaceControl(env, surfaceControlObj));
+}
+
struct ASurfaceControlStats {
std::variant<int64_t, sp<Fence>> acquireTimeOrFence;
sp<Fence> previousReleaseFence;
@@ -190,6 +199,11 @@ void ASurfaceTransaction_delete(ASurfaceTransaction* aSurfaceTransaction) {
delete transaction;
}
+ASurfaceTransaction* ASurfaceTransaction_fromTransaction(JNIEnv* env, jobject transactionObj) {
+ return reinterpret_cast<ASurfaceTransaction*>(
+ android_view_SurfaceTransaction_getNativeSurfaceTransaction(env, transactionObj));
+}
+
void ASurfaceTransaction_apply(ASurfaceTransaction* aSurfaceTransaction) {
CHECK_NOT_NULL(aSurfaceTransaction);
diff --git a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
index fccef23bc89d..9147ccdc9329 100644
--- a/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-ro/strings.xml
@@ -16,22 +16,22 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="backup_confirm_title" msgid="827563724209303345">"Copiere de rezervă completă"</string>
+ <string name="backup_confirm_title" msgid="827563724209303345">"Backup complet"</string>
<string name="restore_confirm_title" msgid="5469365809567486602">"Restabilire completă"</string>
- <string name="backup_confirm_text" msgid="1878021282758896593">"S-a solicitat crearea unei copii de rezervă complete a tuturor datelor pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu ați solicitat dvs. copierea de rezervă, nu permiteți ca operațiunea să continue."</string>
- <string name="allow_backup_button_label" msgid="4217228747769644068">"Faceți backup pentru date"</string>
- <string name="deny_backup_button_label" msgid="6009119115581097708">"Nu creați copii de rezervă"</string>
- <string name="restore_confirm_text" msgid="7499866728030461776">"S-a solicitat o restabilire completă a tuturor datelor de pe un computer desktop conectat. Doriți să permiteți acest lucru?\n\nDacă nu dvs. ați solicitat această restabilire, nu permiteți continuarea operațiunii. Acest proces va înlocui toate datele existente în prezent pe dispozitiv!"</string>
- <string name="allow_restore_button_label" msgid="3081286752277127827">"Restabiliți datele dvs."</string>
- <string name="deny_restore_button_label" msgid="1724367334453104378">"Nu restabiliți"</string>
- <string name="current_password_text" msgid="8268189555578298067">"Introduceți mai jos parola actuală pentru copia de rezervă:"</string>
- <string name="device_encryption_restore_text" msgid="1570864916855208992">"Introduceți mai jos parola pentru criptarea dispozitivului."</string>
- <string name="device_encryption_backup_text" msgid="5866590762672844664">"Introduceți mai jos parola de criptare a dispozitivului. Aceasta va fi utilizată, de asemenea, pentru a cripta arhiva copiei de rezervă."</string>
- <string name="backup_enc_password_text" msgid="4981585714795233099">"Introduceți o parolă pentru a o utiliza la criptarea datelor copiei de rezervă complete. Dacă acest câmp rămâne necompletat, pentru copierea de rezervă se va utiliza parola dvs. actuală."</string>
- <string name="backup_enc_password_optional" msgid="1350137345907579306">"Dacă doriți să criptați datele copiei de rezervă complete, introduceți o parolă mai jos:"</string>
- <string name="restore_enc_password_text" msgid="6140898525580710823">"Dacă datele pentru restabilire sunt criptate, introduceți parola mai jos:"</string>
- <string name="toast_backup_started" msgid="550354281452756121">"Se începe copierea de rezervă..."</string>
- <string name="toast_backup_ended" msgid="3818080769548726424">"Copierea de rezervă a fost finalizată"</string>
+ <string name="backup_confirm_text" msgid="1878021282758896593">"S-a solicitat crearea unui backup complet pentru toate datele pe un computer desktop conectat. Permiți acest lucru?\n\nDacă nu tu ai solicitat backupul, nu permite ca operațiunea să continue."</string>
+ <string name="allow_backup_button_label" msgid="4217228747769644068">"Fă backup pentru date"</string>
+ <string name="deny_backup_button_label" msgid="6009119115581097708">"Nu face backup"</string>
+ <string name="restore_confirm_text" msgid="7499866728030461776">"S-a solicitat o restabilire completă a tuturor datelor de pe un computer desktop conectat. Permiți acest lucru?\n\nDacă nu tu ai solicitat această restabilire, nu permite continuarea operațiunii. Acest proces va înlocui toate datele existente pe dispozitiv!"</string>
+ <string name="allow_restore_button_label" msgid="3081286752277127827">"Restabilește datele"</string>
+ <string name="deny_restore_button_label" msgid="1724367334453104378">"Nu restabili"</string>
+ <string name="current_password_text" msgid="8268189555578298067">"Introdu mai jos parola actuală pentru backup:"</string>
+ <string name="device_encryption_restore_text" msgid="1570864916855208992">"Introdu mai jos parola pentru criptarea dispozitivului."</string>
+ <string name="device_encryption_backup_text" msgid="5866590762672844664">"Introdu mai jos parola de criptare a dispozitivului. Aceasta va fi folosită și pentru a cripta arhiva backupului."</string>
+ <string name="backup_enc_password_text" msgid="4981585714795233099">"Introdu o parolă pentru a o folosi la criptarea datelor backupului complet. Dacă acest câmp rămâne necompletat, pentru backup se va folosi parola actuală:"</string>
+ <string name="backup_enc_password_optional" msgid="1350137345907579306">"Dacă vrei să criptezi datele backupului complet, introdu o parolă mai jos:"</string>
+ <string name="restore_enc_password_text" msgid="6140898525580710823">"Dacă datele pentru restabilire sunt criptate, introdu parola mai jos:"</string>
+ <string name="toast_backup_started" msgid="550354281452756121">"Se începe backupul..."</string>
+ <string name="toast_backup_ended" msgid="3818080769548726424">"Backup terminat"</string>
<string name="toast_restore_started" msgid="7881679218971277385">"Se pornește restabilirea..."</string>
<string name="toast_restore_ended" msgid="1764041639199696132">"Restabilirea s-a încheiat"</string>
<string name="toast_timeout" msgid="5276598587087626877">"Operația a expirat"</string>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index 1644110bfe16..276127f6007d 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Трансліруйце змесціва праграм з вашага тэлефона"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; мець доступ да гэтай інфармацыі з вашага тэлефона"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Сэрвісы для некалькіх прылад"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на перадачу праграм плынню паміж вашымі прыладамі"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" запытвае дазвол ад імя вашай прылады \"<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>\" на трансляцыю праграм паміж вашымі прыладамі"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; мець доступ да гэтай інфармацыі з вашага тэлефона"</string>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 28aaa87756b2..83d0e02a5566 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -28,7 +28,7 @@
<string name="helper_summary_app_streaming" msgid="5977509499890099">"Gailu batetik bestera aplikazioak igortzeko baimena eskatzen ari da <xliff:g id="APP_NAME">%1$s</xliff:g>, <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> gailuaren izenean"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
- <string name="title_computer" msgid="4693714143506569253">"Eman informazio hori telefonotik hartzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
+ <string name="title_computer" msgid="4693714143506569253">"Eman telefonoko informazio hau atzitzeko baimena &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aplikazioari"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"Jakinarazpenak"</string>
<string name="permission_notification_summary" msgid="884075314530071011">"Jakinarazpen guztiak irakur ditzake; besteak beste, kontaktuak, mezuak, argazkiak eta antzeko informazioa"</string>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index a2c628ffbc23..59f4f8971881 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -25,13 +25,13 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Streymdu forritum símans"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aðgang að þessum upplýsingum úr símanum þínum"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Þjónustur á milli tækja"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um heimild fyrir straumspilun forrita á milli tækjanna þinna fyrir hönd <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> sendir beiðni um heimild til straumspilunar forrita á milli tækjanna þinna fyrir hönd <xliff:g id="DEVICE_TYPE">%2$s</xliff:g>"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; aðgang að þessum upplýsingum úr símanum þínum"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"Tilkynningar"</string>
- <string name="permission_notification_summary" msgid="884075314530071011">"Getur lesið allar tilkynningar, þar á meðal upplýsingar á borð við tengiliði skilaboð og myndir"</string>
+ <string name="permission_notification_summary" msgid="884075314530071011">"Getur lesið allar tilkynningar, þar á meðal upplýsingar á borð við tengiliði, skilaboð og myndir"</string>
<string name="permission_storage" msgid="6831099350839392343">"Myndir og efni"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Þjónusta Google Play"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index 3b2ce6d072d2..a3cd4ee3df28 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -28,7 +28,7 @@
<string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> が <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> に代わってデバイス間でアプリをストリーミングする権限をリクエストしています"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
- <string name="title_computer" msgid="4693714143506569253">"このスマートフォンからの情報へのアクセスを &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に許可"</string>
+ <string name="title_computer" msgid="4693714143506569253">"スマートフォンのこの情報へのアクセスを &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; に許可"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"通知"</string>
<string name="permission_notification_summary" msgid="884075314530071011">"連絡先、メッセージ、写真に関する情報を含め、すべての通知を読み取ることができます"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index bc140a2d3a48..ead203721afe 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -25,14 +25,14 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Телефондогу колдонмолорду алып ойнотуу"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Түзмөктөр аралык кызматтар"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду тышкы экранга чыгарууга уруксат сурап жатат"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан түзмөктөрүңүздүн ортосунда колдонмолорду өткөрүүгө уруксат сурап жатат"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; колдонмосуна телефонуңуздагы ушул маалыматты көрүүгө уруксат бериңиз"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"Билдирмелер"</string>
<string name="permission_notification_summary" msgid="884075314530071011">"Бардык билдирмелерди, анын ичинде байланыштар, билдирүүлөр жана сүрөттөр сыяктуу маалыматты окуй алат"</string>
- <string name="permission_storage" msgid="6831099350839392343">"Сүрөттөр жана медиа"</string>
+ <string name="permission_storage" msgid="6831099350839392343">"Сүрөттөр жана медиафайлдар"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play кызматтары"</string>
<string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> колдонмосу <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> түзмөгүңүздүн атынан телефондогу сүрөттөрдү, медиа файлдарды жана билдирмелерди колдонууга уруксат сурап жатат"</string>
diff --git a/packages/CompanionDeviceManager/res/values-nl/strings.xml b/packages/CompanionDeviceManager/res/values-nl/strings.xml
index 2039ee04fcca..9c7cc3c988b7 100644
--- a/packages/CompanionDeviceManager/res/values-nl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nl/strings.xml
@@ -22,7 +22,7 @@
<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="summary_watch" msgid="3002344206574997652">"Deze app is vereist om je <xliff:g id="DEVICE_NAME">%1$s</xliff:g> te beheren. <xliff:g id="APP_NAME">%2$s</xliff:g> kan interactie hebben met je meldingen en toegang krijgen tot rechten voor Telefoon, Sms, Contacten, Agenda, Gesprekslijsten en Apparaten in de buurt."</string>
<string name="permission_apps" msgid="6142133265286656158">"Apps"</string>
- <string name="permission_apps_summary" msgid="798718816711515431">"De apps van je telefoon streamen"</string>
+ <string name="permission_apps_summary" msgid="798718816711515431">"Stream de apps van je telefoon"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; toegang geven tot deze informatie op je telefoon"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Cross-device-services"</string>
<string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> vraagt namens jouw <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> toestemming om apps te streamen tussen je apparaten"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 619468e6eb43..e43bed5cc11e 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -19,16 +19,16 @@
<string name="app_label" msgid="4470785958457506021">"Manager de dispozitiv Companion"</string>
<string name="confirmation_title" msgid="3785000297483688997">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să vă acceseze dispozitivul &lt;strong&gt;<xliff:g id="DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
<string name="profile_name_watch" msgid="576290739483672360">"ceas"</string>
- <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="chooser_title" msgid="2262294130493605839">"Alege 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="summary_watch" msgid="3002344206574997652">"Această aplicație este necesară pentru a gestiona <xliff:g id="DEVICE_NAME">%1$s</xliff:g>. <xliff:g id="APP_NAME">%2$s</xliff:g> va putea să interacționeze cu notificările dvs. și să vă acceseze permisiunile pentru Telefon, SMS, Agendă, Calendar, Jurnale de apeluri și Dispozitive din apropiere."</string>
<string name="permission_apps" msgid="6142133265286656158">"Aplicații"</string>
<string name="permission_apps_summary" msgid="798718816711515431">"Să redea în stream aplicațiile telefonului"</string>
- <string name="title_app_streaming" msgid="2270331024626446950">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să acceseze aceste informații de pe telefon"</string>
+ <string name="title_app_streaming" msgid="2270331024626446950">"Permite ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să acceseze aceste informații de pe telefon"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Servicii pe mai multe dispozitive"</string>
<string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a reda în stream aplicații între dispozitivele dvs."</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
- <string name="title_computer" msgid="4693714143506569253">"Permiteți ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să acceseze aceste informații de pe telefon"</string>
+ <string name="title_computer" msgid="4693714143506569253">"Permite ca &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; să acceseze aceste informații de pe telefon"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"Notificări"</string>
<string name="permission_notification_summary" msgid="884075314530071011">"Poate să citească toate notificările, inclusiv informații cum ar fi agenda, mesajele și fotografiile"</string>
@@ -38,8 +38,8 @@
<string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> solicită permisiunea pentru <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> de a accesa fotografiile, conținutul media și notificările de pe telefon"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
- <string name="consent_yes" msgid="8344487259618762872">"Permiteți"</string>
- <string name="consent_no" msgid="2640796915611404382">"Nu permiteți"</string>
+ <string name="consent_yes" msgid="8344487259618762872">"Permite"</string>
+ <string name="consent_no" msgid="2640796915611404382">"Nu permite"</string>
<string name="consent_back" msgid="2560683030046918882">"Înapoi"</string>
<string name="permission_sync_confirmation_title" msgid="4409622174437248702">"Oferiți aplicațiilor de pe &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME">%1$s</xliff:g>&lt;/strong&gt; aceleași permisiuni ca pe &lt;strong&gt;<xliff:g id="PRIMARY_DEVICE_NAME">%2$s</xliff:g>&lt;/strong&gt;?"</string>
<string name="permission_sync_summary" msgid="4866838188678457084">"&lt;p&gt;Aici pot fi incluse accesul la microfon, la camera foto, la locație și alte permisiuni de accesare a informațiilor sensibile de pe &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME_0">%1$s</xliff:g>&lt;/strong&gt;.&lt;/p&gt; &lt;p&gt;Puteți modifica oricând aceste permisiuni din Setările de pe &lt;strong&gt;<xliff:g id="COMPANION_DEVICE_NAME_1">%1$s</xliff:g>&lt;/strong&gt;.&lt;/p&gt;"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index 9537dfdb3e17..ff19fa577448 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Streamovať aplikácie telefónu"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Povoľte aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; prístup k týmto informáciám z vášho telefónu"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Služby pre viacero zariadení"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na streamovanie aplikácií medzi vašimi zariadeniami v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> vyžaduje povolenie na streamovanie aplikácií medzi vašimi zariadeniami v mene tohto zariadenia (<xliff:g id="DEVICE_TYPE">%2$s</xliff:g>)"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Povoľte aplikácii &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; prístup k týmto informáciám z vášho telefónu"</string>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 9cb5f57eb038..812b4df347b8 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -25,7 +25,7 @@
<string name="permission_apps_summary" msgid="798718816711515431">"Tiririsha programu za simu yako"</string>
<string name="title_app_streaming" msgid="2270331024626446950">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ifikie maelezo haya kutoka kwenye simu yako"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"Huduma za kifaa kilichounganishwa kwingine"</string>
- <string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili itiririshe programu kati ya vifaa vyako"</string>
+ <string name="helper_summary_app_streaming" msgid="5977509499890099">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili itiririshe programu kati ya vifaa vyako"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
<string name="title_computer" msgid="4693714143506569253">"Ruhusu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ifikie maelezo haya kutoka kwenye simu yako"</string>
@@ -35,7 +35,7 @@
<string name="permission_storage" msgid="6831099350839392343">"Picha na maudhui"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Huduma za Google Play"</string>
- <string name="helper_summary_computer" msgid="9050724687678157852">"<xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili ifikie picha, maudhui na arifa za simu yako"</string>
+ <string name="helper_summary_computer" msgid="9050724687678157852">"Programu ya <xliff:g id="APP_NAME">%1$s</xliff:g> inaomba ruhusa kwa niaba ya <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> yako ili ifikie picha, maudhui na arifa za simu yako"</string>
<string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
<string name="summary_generic" msgid="2346762210105903720"></string>
<string name="consent_yes" msgid="8344487259618762872">"Ruhusu"</string>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index 8a3df03d3050..c3187963086b 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -31,7 +31,7 @@
<string name="title_computer" msgid="4693714143506569253">"మీ ఫోన్ నుండి ఈ సమాచారాన్ని యాక్సెస్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; యాప్‌ను అనుమతించండి"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"నోటిఫికేషన్‌లు"</string>
- <string name="permission_notification_summary" msgid="884075314530071011">"కాంటాక్ట్‌లు, మెసేజ్‌లు, ఫోటోల వంటి సమాచారంతో సహా అన్ని నోటిఫికేషన్‌లను చదవగలరు"</string>
+ <string name="permission_notification_summary" msgid="884075314530071011">"కాంటాక్ట్‌లు, మెసేజ్‌లు, ఫోటోల వంటి సమాచారంతో సహా అన్ని నోటిఫికేషన్‌లను చదవగలదు"</string>
<string name="permission_storage" msgid="6831099350839392343">"ఫోటోలు, మీడియా"</string>
<string name="permission_storage_summary" msgid="3918240895519506417"></string>
<string name="helper_title_computer" msgid="4671071173916176037">"Google Play సర్వీసులు"</string>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index 074049882b5c..65b2ba576d41 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -23,12 +23,12 @@
<string name="summary_watch" msgid="3002344206574997652">"‏آپ کے <xliff:g id="DEVICE_NAME">%1$s</xliff:g> کا نظم کرنے کے لئے اس ایپ کی ضرورت ہے۔ <xliff:g id="APP_NAME">%2$s</xliff:g> کو آپ کی اطلاعات کے ساتھ تعامل کرنے اور آپ کے فون، SMS، رابطوں، کیلنڈر، کال لاگز اور قریبی آلات کی اجازتوں تک رسائی کی اجازت ہوگی۔"</string>
<string name="permission_apps" msgid="6142133265286656158">"ایپس"</string>
<string name="permission_apps_summary" msgid="798718816711515431">"اپنے فون کی ایپس کی سلسلہ بندی کریں"</string>
- <string name="title_app_streaming" msgid="2270331024626446950">"‏‎&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;‎ کو اپنے فون سے ان معلومات تک رسائی حاصل کرنے کی اجازت دیں"</string>
+ <string name="title_app_streaming" msgid="2270331024626446950">"‏اپنے فون سے ان معلومات تک رسائی حاصل کرنے کی &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں"</string>
<string name="helper_title_app_streaming" msgid="4151687003439969765">"کراس ڈیوائس سروسز"</string>
<string name="helper_summary_app_streaming" msgid="5977509499890099">"<xliff:g id="APP_NAME">%1$s</xliff:g> ایپ آپ کے <xliff:g id="DEVICE_TYPE">%2$s</xliff:g> کی جانب سے آپ کے آلات کے درمیان ایپس کی سلسلہ بندی کرنے کی اجازت کی درخواست کر رہی ہے"</string>
<string name="title_automotive_projection" msgid="3296005598978412847"></string>
<string name="summary_automotive_projection" msgid="8683801274662496164"></string>
- <string name="title_computer" msgid="4693714143506569253">"‏اپنے فون سے اس معلومات تک رسائی حاصل Allow &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کرنے کی اجازت دیں"</string>
+ <string name="title_computer" msgid="4693714143506569253">"‏اپنے فون سے اس معلومات تک رسائی حاصل کرنے کی &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو اجازت دیں"</string>
<string name="summary_computer" msgid="3798467601598297062"></string>
<string name="permission_notification" msgid="693762568127741203">"اطلاعات"</string>
<string name="permission_notification_summary" msgid="884075314530071011">"رابطوں، پیغامات اور تصاویر جیسی معلومات سمیت تمام اطلاعات پڑھ سکتے ہیں"</string>
diff --git a/packages/CredentialManager/Android.bp b/packages/CredentialManager/Android.bp
new file mode 100644
index 000000000000..51943fffb36d
--- /dev/null
+++ b/packages/CredentialManager/Android.bp
@@ -0,0 +1,35 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_app {
+ name: "CredentialManager",
+ defaults: ["platform_app_defaults"],
+ certificate: "platform",
+ srcs: ["src/**/*.kt"],
+ resource_dirs: ["res"],
+
+ static_libs: [
+ "androidx.activity_activity-compose",
+ "androidx.appcompat_appcompat",
+ "androidx.compose.material_material",
+ "androidx.compose.runtime_runtime",
+ "androidx.compose.ui_ui",
+ "androidx.compose.ui_ui-tooling",
+ "androidx.core_core-ktx",
+ "androidx.lifecycle_lifecycle-extensions",
+ "androidx.lifecycle_lifecycle-livedata",
+ "androidx.lifecycle_lifecycle-runtime-ktx",
+ "androidx.lifecycle_lifecycle-viewmodel-compose",
+ "androidx.recyclerview_recyclerview",
+ ],
+
+ platform_apis: true,
+
+ kotlincflags: ["-Xjvm-default=enable"],
+}
diff --git a/packages/CredentialManager/AndroidManifest.xml b/packages/CredentialManager/AndroidManifest.xml
new file mode 100644
index 000000000000..586ef86f26f6
--- /dev/null
+++ b/packages/CredentialManager/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.credentialmanager">
+
+ <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"/>
+ <uses-permission android:name="android.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS"/>
+
+ <application
+ android:allowBackup="true"
+ android:dataExtractionRules="@xml/data_extraction_rules"
+ android:fullBackupContent="@xml/backup_rules"
+ android:icon="@mipmap/ic_launcher"
+ android:label="@string/app_name"
+ android:roundIcon="@mipmap/ic_launcher_round"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.CredentialSelector">
+
+ <activity
+ android:name=".CredentialSelectorActivity"
+ android:exported="true"
+ android:label="@string/app_name"
+ android:launchMode="singleInstance"
+ android:noHistory="true"
+ android:excludeFromRecents="true"
+ android:theme="@style/Theme.CredentialSelector">
+ </activity>
+ </application>
+
+</manifest>
diff --git a/packages/CredentialManager/OWNERS b/packages/CredentialManager/OWNERS
new file mode 100644
index 000000000000..f3b43c171025
--- /dev/null
+++ b/packages/CredentialManager/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/credentials/OWNERS
diff --git a/packages/SystemUI/compose/gallery/res/drawable-v24/ic_launcher_foreground.xml b/packages/CredentialManager/res/drawable-v24/ic_launcher_foreground.xml
index 966abaff2074..966abaff2074 100644
--- a/packages/SystemUI/compose/gallery/res/drawable-v24/ic_launcher_foreground.xml
+++ b/packages/CredentialManager/res/drawable-v24/ic_launcher_foreground.xml
diff --git a/packages/SystemUI/compose/gallery/res/drawable/ic_launcher_background.xml b/packages/CredentialManager/res/drawable/ic_launcher_background.xml
index 61bb79edb709..61bb79edb709 100644
--- a/packages/SystemUI/compose/gallery/res/drawable/ic_launcher_background.xml
+++ b/packages/CredentialManager/res/drawable/ic_launcher_background.xml
diff --git a/packages/CredentialManager/res/drawable/ic_passkey.xml b/packages/CredentialManager/res/drawable/ic_passkey.xml
new file mode 100644
index 000000000000..041a32164073
--- /dev/null
+++ b/packages/CredentialManager/res/drawable/ic_passkey.xml
@@ -0,0 +1,16 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="28dp"
+ android:height="24dp"
+ android:viewportWidth="28"
+ android:viewportHeight="24">
+ <path
+ android:pathData="M27.453,13.253C27.453,14.952 26.424,16.411 24.955,17.041L26.21,18.295L24.839,19.666L26.21,21.037L23.305,23.942L22.012,22.65L22.012,17.156C20.385,16.605 19.213,15.066 19.213,13.253C19.213,10.977 21.058,9.133 23.333,9.133C25.609,9.133 27.453,10.977 27.453,13.253ZM25.47,13.254C25.47,14.434 24.514,15.39 23.334,15.39C22.154,15.39 21.197,14.434 21.197,13.254C21.197,12.074 22.154,11.118 23.334,11.118C24.514,11.118 25.47,12.074 25.47,13.254Z"
+ android:fillColor="#00639B"
+ android:fillType="evenOdd"/>
+ <path
+ android:pathData="M17.85,5.768C17.85,8.953 15.268,11.536 12.083,11.536C8.897,11.536 6.315,8.953 6.315,5.768C6.315,2.582 8.897,0 12.083,0C15.268,0 17.85,2.582 17.85,5.768Z"
+ android:fillColor="#00639B"/>
+ <path
+ android:pathData="M0.547,20.15C0.547,16.32 8.23,14.382 12.083,14.382C13.59,14.382 15.684,14.679 17.674,15.269C18.116,16.454 18.952,17.447 20.022,18.089V23.071H0.547V20.15Z"
+ android:fillColor="#00639B"/>
+</vector>
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher.xml b/packages/CredentialManager/res/mipmap-anydpi-v26/ic_launcher.xml
index 03eed2533da2..03eed2533da2 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher.xml
+++ b/packages/CredentialManager/res/mipmap-anydpi-v26/ic_launcher.xml
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher_round.xml b/packages/CredentialManager/res/mipmap-anydpi-v26/ic_launcher_round.xml
index 03eed2533da2..03eed2533da2 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher_round.xml
+++ b/packages/CredentialManager/res/mipmap-anydpi-v26/ic_launcher_round.xml
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher.webp b/packages/CredentialManager/res/mipmap-hdpi/ic_launcher.webp
index c209e78ecd37..c209e78ecd37 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher.webp
+++ b/packages/CredentialManager/res/mipmap-hdpi/ic_launcher.webp
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher_round.webp b/packages/CredentialManager/res/mipmap-hdpi/ic_launcher_round.webp
index b2dfe3d1ba5c..b2dfe3d1ba5c 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher_round.webp
+++ b/packages/CredentialManager/res/mipmap-hdpi/ic_launcher_round.webp
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher.webp b/packages/CredentialManager/res/mipmap-mdpi/ic_launcher.webp
index 4f0f1d64e58b..4f0f1d64e58b 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher.webp
+++ b/packages/CredentialManager/res/mipmap-mdpi/ic_launcher.webp
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher_round.webp b/packages/CredentialManager/res/mipmap-mdpi/ic_launcher_round.webp
index 62b611da0816..62b611da0816 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher_round.webp
+++ b/packages/CredentialManager/res/mipmap-mdpi/ic_launcher_round.webp
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher.webp b/packages/CredentialManager/res/mipmap-xhdpi/ic_launcher.webp
index 948a3070fe34..948a3070fe34 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher.webp
+++ b/packages/CredentialManager/res/mipmap-xhdpi/ic_launcher.webp
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher_round.webp b/packages/CredentialManager/res/mipmap-xhdpi/ic_launcher_round.webp
index 1b9a6956b3ac..1b9a6956b3ac 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher_round.webp
+++ b/packages/CredentialManager/res/mipmap-xhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher.webp b/packages/CredentialManager/res/mipmap-xxhdpi/ic_launcher.webp
index 28d4b77f9f03..28d4b77f9f03 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher.webp
+++ b/packages/CredentialManager/res/mipmap-xxhdpi/ic_launcher.webp
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher_round.webp b/packages/CredentialManager/res/mipmap-xxhdpi/ic_launcher_round.webp
index 9287f5083623..9287f5083623 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher_round.webp
+++ b/packages/CredentialManager/res/mipmap-xxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher.webp b/packages/CredentialManager/res/mipmap-xxxhdpi/ic_launcher.webp
index aa7d6427e6fa..aa7d6427e6fa 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher.webp
+++ b/packages/CredentialManager/res/mipmap-xxxhdpi/ic_launcher.webp
Binary files differ
diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher_round.webp b/packages/CredentialManager/res/mipmap-xxxhdpi/ic_launcher_round.webp
index 9126ae37cbc3..9126ae37cbc3 100644
--- a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher_round.webp
+++ b/packages/CredentialManager/res/mipmap-xxxhdpi/ic_launcher_round.webp
Binary files differ
diff --git a/packages/CredentialManager/res/values/colors.xml b/packages/CredentialManager/res/values/colors.xml
new file mode 100644
index 000000000000..09837df62f44
--- /dev/null
+++ b/packages/CredentialManager/res/values/colors.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="purple_200">#FFBB86FC</color>
+ <color name="purple_500">#FF6200EE</color>
+ <color name="purple_700">#FF3700B3</color>
+ <color name="teal_200">#FF03DAC5</color>
+ <color name="teal_700">#FF018786</color>
+ <color name="black">#FF000000</color>
+ <color name="white">#FFFFFFFF</color>
+</resources> \ No newline at end of file
diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml
new file mode 100644
index 000000000000..2901705d5836
--- /dev/null
+++ b/packages/CredentialManager/res/values/strings.xml
@@ -0,0 +1,13 @@
+<resources>
+ <string name="app_name">CredentialManager</string>
+ <string name="string_cancel">Cancel</string>
+ <string name="string_continue">Continue</string>
+ <string name="string_more_options">More options</string>
+ <string name="string_no_thanks">No thanks</string>
+ <string name="passkey_creation_intro_title">A simple way to sign in safely</string>
+ <string name="passkey_creation_intro_body">Use your fingerprint, face or screen lock to sign in with a unique passkey that can’t be forgotten or stolen. Learn more</string>
+ <string name="choose_provider_title">Choose your default provider</string>
+ <string name="choose_provider_body">This provider will store passkeys and passwords for you and help you easily autofill and sign in. Learn more</string>
+ <string name="choose_create_option_title">Create a passkey at</string>
+ <string name="choose_sign_in_title">Use saved sign in</string>
+</resources> \ No newline at end of file
diff --git a/packages/CredentialManager/res/values/themes.xml b/packages/CredentialManager/res/values/themes.xml
new file mode 100644
index 000000000000..feec74608000
--- /dev/null
+++ b/packages/CredentialManager/res/values/themes.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <style name="Theme.CredentialSelector" parent="@android:style/ThemeOverlay.Material">
+ <item name="android:statusBarColor">@color/purple_700</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/packages/CredentialManager/res/xml/backup_rules.xml b/packages/CredentialManager/res/xml/backup_rules.xml
new file mode 100644
index 000000000000..9b42d90d94bb
--- /dev/null
+++ b/packages/CredentialManager/res/xml/backup_rules.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample backup rules file; uncomment and customize as necessary.
+ See https://developer.android.com/guide/topics/data/autobackup
+ for details.
+ Note: This file is ignored for devices older that API 31
+ See https://developer.android.com/about/versions/12/backup-restore
+-->
+<full-backup-content>
+ <!--
+ <include domain="sharedpref" path="."/>
+ <exclude domain="sharedpref" path="device.xml"/>
+-->
+</full-backup-content> \ No newline at end of file
diff --git a/packages/CredentialManager/res/xml/data_extraction_rules.xml b/packages/CredentialManager/res/xml/data_extraction_rules.xml
new file mode 100644
index 000000000000..c6c3bb05a956
--- /dev/null
+++ b/packages/CredentialManager/res/xml/data_extraction_rules.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Sample data extraction rules file; uncomment and customize as necessary.
+ See https://developer.android.com/about/versions/12/backup-restore#xml-changes
+ for details.
+-->
+<data-extraction-rules>
+ <cloud-backup>
+ <!-- TODO: Use <include> and <exclude> to control what is backed up.
+ <include .../>
+ <exclude .../>
+ -->
+ </cloud-backup>
+ <!--
+ <device-transfer>
+ <include .../>
+ <exclude .../>
+ </device-transfer>
+ -->
+</data-extraction-rules> \ No newline at end of file
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
new file mode 100644
index 000000000000..59186336056e
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialManagerRepo.kt
@@ -0,0 +1,151 @@
+package com.android.credentialmanager
+
+import android.content.Context
+import com.android.credentialmanager.createflow.CreateOptionInfo
+import com.android.credentialmanager.createflow.CreatePasskeyUiState
+import com.android.credentialmanager.createflow.CreateScreenState
+import com.android.credentialmanager.createflow.ProviderInfo
+import com.android.credentialmanager.getflow.CredentialOptionInfo
+import com.android.credentialmanager.getflow.GetCredentialUiState
+import com.android.credentialmanager.getflow.GetScreenState
+
+// Consider repo per screen, similar to view model?
+class CredentialManagerRepo(
+ private val context: Context
+) {
+ private fun getCredentialProviderList():
+ List<com.android.credentialmanager.getflow.ProviderInfo> {
+ return listOf(
+ com.android.credentialmanager.getflow.ProviderInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ name = "Google Password Manager",
+ appDomainName = "tribank.us",
+ credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
+ credentialOptions = listOf(
+ CredentialOptionInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ title = "Elisa Backett",
+ subtitle = "elisa.beckett@gmail.com",
+ id = "id-1",
+ ),
+ CredentialOptionInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ title = "Elisa Backett Work",
+ subtitle = "elisa.beckett.work@google.com",
+ id = "id-2",
+ ),
+ )
+ ),
+ com.android.credentialmanager.getflow.ProviderInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ name = "Lastpass",
+ appDomainName = "tribank.us",
+ credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
+ credentialOptions = listOf(
+ CredentialOptionInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ title = "Elisa Backett",
+ subtitle = "elisa.beckett@lastpass.com",
+ id = "id-1",
+ ),
+ )
+ ),
+ com.android.credentialmanager.getflow.ProviderInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ name = "Dashlane",
+ appDomainName = "tribank.us",
+ credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
+ credentialOptions = listOf(
+ CredentialOptionInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ title = "Elisa Backett",
+ subtitle = "elisa.beckett@dashlane.com",
+ id = "id-1",
+ ),
+ )
+ ),
+ )
+ }
+
+ private fun createCredentialProviderList(): List<ProviderInfo> {
+ return listOf(
+ ProviderInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ name = "Google Password Manager",
+ appDomainName = "tribank.us",
+ credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
+ createOptions = listOf(
+ CreateOptionInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ title = "Elisa Backett",
+ subtitle = "elisa.beckett@gmail.com",
+ id = "id-1",
+ ),
+ CreateOptionInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ title = "Elisa Backett Work",
+ subtitle = "elisa.beckett.work@google.com",
+ id = "id-2",
+ ),
+ )
+ ),
+ ProviderInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ name = "Lastpass",
+ appDomainName = "tribank.us",
+ credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
+ createOptions = listOf(
+ CreateOptionInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ title = "Elisa Backett",
+ subtitle = "elisa.beckett@lastpass.com",
+ id = "id-1",
+ ),
+ )
+ ),
+ ProviderInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ name = "Dashlane",
+ appDomainName = "tribank.us",
+ credentialTypeIcon = context.getDrawable(R.drawable.ic_passkey)!!,
+ createOptions = listOf(
+ CreateOptionInfo(
+ icon = context.getDrawable(R.drawable.ic_passkey)!!,
+ title = "Elisa Backett",
+ subtitle = "elisa.beckett@dashlane.com",
+ id = "id-1",
+ ),
+ )
+ ),
+ )
+ }
+
+ fun getCredentialInitialUiState(): GetCredentialUiState {
+ val providerList = getCredentialProviderList()
+ return GetCredentialUiState(
+ providerList,
+ GetScreenState.CREDENTIAL_SELECTION,
+ providerList.first()
+ )
+ }
+
+ fun createPasskeyInitialUiState(): CreatePasskeyUiState {
+ val providerList = createCredentialProviderList()
+ return CreatePasskeyUiState(
+ providers = providerList,
+ currentScreenState = CreateScreenState.PASSKEY_INTRO,
+ )
+ }
+
+ companion object {
+ lateinit var repo: CredentialManagerRepo
+
+ fun setup(context: Context) {
+ repo = CredentialManagerRepo(context)
+ }
+
+ fun getInstance(): CredentialManagerRepo {
+ return repo
+ }
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
new file mode 100644
index 000000000000..5cd6a13a377a
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -0,0 +1,52 @@
+package com.android.credentialmanager
+
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.setContent
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.runtime.Composable
+import com.android.credentialmanager.common.DialogType
+import com.android.credentialmanager.createflow.CreatePasskeyScreen
+import com.android.credentialmanager.getflow.GetCredentialScreen
+import com.android.credentialmanager.ui.theme.CredentialSelectorTheme
+
+@ExperimentalMaterialApi
+class CredentialSelectorActivity : ComponentActivity() {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ CredentialManagerRepo.setup(this)
+ val startDestination = intent.extras?.getString(
+ "start_destination",
+ "CREATE_PASSKEY"
+ ) ?: "CREATE_PASSKEY"
+
+ setContent {
+ CredentialSelectorTheme {
+ CredentialManagerBottomSheet(startDestination)
+ }
+ }
+ }
+
+ @ExperimentalMaterialApi
+ @Composable
+ fun CredentialManagerBottomSheet(operationType: String) {
+ val dialogType = DialogType.toDialogType(operationType)
+ when (dialogType) {
+ DialogType.CREATE_PASSKEY -> {
+ CreatePasskeyScreen(cancelActivity = onCancel)
+ }
+ DialogType.GET_CREDENTIALS -> {
+ GetCredentialScreen(cancelActivity = onCancel)
+ }
+ else -> {
+ Log.w("AccountSelector", "Unknown type, not rendering any UI")
+ this.finish()
+ }
+ }
+ }
+
+ private val onCancel = {
+ this@CredentialSelectorActivity.finish()
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt
new file mode 100644
index 000000000000..8bb80a1b02fd
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/common/DialogType.kt
@@ -0,0 +1,18 @@
+package com.android.credentialmanager.common
+
+enum class DialogType {
+ CREATE_PASSKEY,
+ GET_CREDENTIALS,
+ CREATE_PASSWORD,
+ UNKNOWN;
+
+ companion object {
+ fun toDialogType(value: String): DialogType {
+ return try {
+ valueOf(value)
+ } catch (e: IllegalArgumentException) {
+ UNKNOWN
+ }
+ }
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
new file mode 100644
index 000000000000..5aa1e9ba6183
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreateModel.kt
@@ -0,0 +1,25 @@
+package com.android.credentialmanager.createflow
+
+import android.graphics.drawable.Drawable
+
+data class ProviderInfo(
+ val icon: Drawable,
+ val name: String,
+ val appDomainName: String,
+ val credentialTypeIcon: Drawable,
+ val createOptions: List<CreateOptionInfo>,
+)
+
+data class CreateOptionInfo(
+ val icon: Drawable,
+ val title: String,
+ val subtitle: String,
+ val id: String,
+)
+
+/** The name of the current screen. */
+enum class CreateScreenState {
+ PASSKEY_INTRO,
+ PROVIDER_SELECTION,
+ CREATION_OPTION_SELECTION,
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
new file mode 100644
index 000000000000..60a8e4b24d57
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyComponents.kt
@@ -0,0 +1,398 @@
+package com.android.credentialmanager.createflow
+
+import androidx.compose.foundation.BorderStroke
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.Button
+import androidx.compose.material.ButtonColors
+import androidx.compose.material.ButtonDefaults
+import androidx.compose.material.Card
+import androidx.compose.material.Chip
+import androidx.compose.material.ChipDefaults
+import androidx.compose.material.Divider
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.Icon
+import androidx.compose.material.ModalBottomSheetLayout
+import androidx.compose.material.ModalBottomSheetValue
+import androidx.compose.material.Text
+import androidx.compose.material.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.core.graphics.drawable.toBitmap
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.credentialmanager.R
+import com.android.credentialmanager.ui.theme.Grey100
+import com.android.credentialmanager.ui.theme.Shapes
+import com.android.credentialmanager.ui.theme.Typography
+import com.android.credentialmanager.ui.theme.lightBackgroundColor
+import com.android.credentialmanager.ui.theme.lightColorAccentSecondary
+import com.android.credentialmanager.ui.theme.lightSurface1
+
+@ExperimentalMaterialApi
+@Composable
+fun CreatePasskeyScreen(
+ viewModel: CreatePasskeyViewModel = viewModel(),
+ cancelActivity: () -> Unit,
+) {
+ val state = rememberModalBottomSheetState(
+ initialValue = ModalBottomSheetValue.Expanded,
+ skipHalfExpanded = true
+ )
+ ModalBottomSheetLayout(
+ sheetState = state,
+ sheetContent = {
+ val uiState = viewModel.uiState
+ when (uiState.currentScreenState) {
+ CreateScreenState.PASSKEY_INTRO -> ConfirmationCard(
+ onConfirm = {viewModel.onConfirmIntro()},
+ onCancel = cancelActivity,
+ )
+ CreateScreenState.PROVIDER_SELECTION -> ProviderSelectionCard(
+ providerList = uiState.providers,
+ onCancel = cancelActivity,
+ onProviderSelected = {viewModel.onProviderSelected(it)}
+ )
+ CreateScreenState.CREATION_OPTION_SELECTION -> CreationSelectionCard(
+ providerInfo = uiState.selectedProvider!!,
+ onOptionSelected = {viewModel.onCreateOptionSelected(it)},
+ onCancel = cancelActivity,
+ multiProvider = uiState.providers.size > 1,
+ onMoreOptionSelected = {viewModel.onMoreOptionSelected()}
+ )
+ }
+ },
+ scrimColor = Color.Transparent,
+ sheetShape = Shapes.medium,
+ ) {}
+ LaunchedEffect(state.currentValue) {
+ when (state.currentValue) {
+ ModalBottomSheetValue.Hidden -> {
+ cancelActivity()
+ }
+ }
+ }
+}
+
+@Composable
+fun ConfirmationCard(
+ onConfirm: () -> Unit,
+ onCancel: () -> Unit,
+) {
+ Card(
+ backgroundColor = lightBackgroundColor,
+ ) {
+ Column() {
+ Icon(
+ painter = painterResource(R.drawable.ic_passkey),
+ contentDescription = null,
+ tint = Color.Unspecified,
+ modifier = Modifier.align(alignment = Alignment.CenterHorizontally).padding(top = 24.dp)
+ )
+ Text(
+ text = stringResource(R.string.passkey_creation_intro_title),
+ style = Typography.subtitle1,
+ modifier = Modifier
+ .padding(horizontal = 24.dp)
+ .align(alignment = Alignment.CenterHorizontally)
+ )
+ Divider(
+ thickness = 24.dp,
+ color = Color.Transparent
+ )
+ Text(
+ text = stringResource(R.string.passkey_creation_intro_body),
+ style = Typography.body1,
+ modifier = Modifier.padding(horizontal = 28.dp)
+ )
+ Divider(
+ thickness = 48.dp,
+ color = Color.Transparent
+ )
+ Row(
+ horizontalArrangement = Arrangement.SpaceBetween,
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
+ ) {
+ CancelButton(
+ stringResource(R.string.string_cancel),
+ onclick = onCancel
+ )
+ ConfirmButton(
+ stringResource(R.string.string_continue),
+ onclick = onConfirm
+ )
+ }
+ Divider(
+ thickness = 18.dp,
+ color = Color.Transparent,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ }
+ }
+}
+
+@ExperimentalMaterialApi
+@Composable
+fun ProviderSelectionCard(
+ providerList: List<ProviderInfo>,
+ onProviderSelected: (String) -> Unit,
+ onCancel: () -> Unit
+) {
+ Card(
+ backgroundColor = lightBackgroundColor,
+ ) {
+ Column() {
+ Text(
+ text = stringResource(R.string.choose_provider_title),
+ style = Typography.subtitle1,
+ modifier = Modifier.padding(all = 24.dp).align(alignment = Alignment.CenterHorizontally)
+ )
+ Text(
+ text = stringResource(R.string.choose_provider_body),
+ style = Typography.body1,
+ modifier = Modifier.padding(horizontal = 28.dp)
+ )
+ Divider(
+ thickness = 24.dp,
+ color = Color.Transparent
+ )
+ Card(
+ shape = Shapes.medium,
+ modifier = Modifier
+ .padding(horizontal = 24.dp)
+ .align(alignment = Alignment.CenterHorizontally)
+ ) {
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(2.dp)
+ ) {
+ providerList.forEach {
+ item {
+ ProviderRow(providerInfo = it, onProviderSelected = onProviderSelected)
+ }
+ }
+ }
+ }
+ Divider(
+ thickness = 24.dp,
+ color = Color.Transparent
+ )
+ Row(
+ horizontalArrangement = Arrangement.Start,
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
+ ) {
+ CancelButton(stringResource(R.string.string_cancel), onCancel)
+ }
+ Divider(
+ thickness = 18.dp,
+ color = Color.Transparent,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ }
+ }
+}
+
+@ExperimentalMaterialApi
+@Composable
+fun ProviderRow(providerInfo: ProviderInfo, onProviderSelected: (String) -> Unit) {
+ Chip(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = {onProviderSelected(providerInfo.name)},
+ leadingIcon = {
+ Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
+ bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
+ // painter = painterResource(R.drawable.ic_passkey),
+ // TODO: add description.
+ contentDescription = "")
+ },
+ colors = ChipDefaults.chipColors(
+ backgroundColor = Grey100,
+ leadingIconContentColor = Grey100
+ ),
+ shape = Shapes.large
+ ) {
+ Text(
+ text = providerInfo.name,
+ style = Typography.button,
+ modifier = Modifier.padding(vertical = 18.dp)
+ )
+ }
+}
+
+@Composable
+fun CancelButton(text: String, onclick: () -> Unit) {
+ val colors = ButtonDefaults.buttonColors(
+ backgroundColor = lightBackgroundColor
+ )
+ NavigationButton(
+ border = BorderStroke(1.dp, lightSurface1),
+ colors = colors,
+ text = text,
+ onclick = onclick)
+}
+
+@Composable
+fun ConfirmButton(text: String, onclick: () -> Unit) {
+ val colors = ButtonDefaults.buttonColors(
+ backgroundColor = lightColorAccentSecondary
+ )
+ NavigationButton(
+ colors = colors,
+ text = text,
+ onclick = onclick)
+}
+
+@Composable
+fun NavigationButton(
+ border: BorderStroke? = null,
+ colors: ButtonColors,
+ text: String,
+ onclick: () -> Unit
+) {
+ Button(
+ onClick = onclick,
+ shape = Shapes.small,
+ colors = colors,
+ border = border
+ ) {
+ Text(text = text, style = Typography.button)
+ }
+}
+
+@ExperimentalMaterialApi
+@Composable
+fun CreationSelectionCard(
+ providerInfo: ProviderInfo,
+ onOptionSelected: (String) -> Unit,
+ onCancel: () -> Unit,
+ multiProvider: Boolean,
+ onMoreOptionSelected: () -> Unit,
+) {
+ Card(
+ backgroundColor = lightBackgroundColor,
+ ) {
+ Column() {
+ Icon(
+ bitmap = providerInfo.credentialTypeIcon.toBitmap().asImageBitmap(),
+ contentDescription = null,
+ tint = Color.Unspecified,
+ modifier = Modifier.align(alignment = Alignment.CenterHorizontally).padding(top = 24.dp)
+ )
+ Text(
+ text = "${stringResource(R.string.choose_create_option_title)} ${providerInfo.name}",
+ style = Typography.subtitle1,
+ modifier = Modifier.padding(all = 24.dp).align(alignment = Alignment.CenterHorizontally)
+ )
+ Text(
+ text = providerInfo.appDomainName,
+ style = Typography.body2,
+ modifier = Modifier.padding(horizontal = 28.dp)
+ )
+ Divider(
+ thickness = 24.dp,
+ color = Color.Transparent
+ )
+ Card(
+ shape = Shapes.medium,
+ modifier = Modifier
+ .padding(horizontal = 24.dp)
+ .align(alignment = Alignment.CenterHorizontally)
+ ) {
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(2.dp)
+ ) {
+ providerInfo.createOptions.forEach {
+ item {
+ CreateOptionRow(createOptionInfo = it, onOptionSelected = onOptionSelected)
+ }
+ }
+ if (multiProvider) {
+ item {
+ MoreOptionRow(onSelect = onMoreOptionSelected)
+ }
+ }
+ }
+ }
+ Divider(
+ thickness = 24.dp,
+ color = Color.Transparent
+ )
+ Row(
+ horizontalArrangement = Arrangement.Start,
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
+ ) {
+ CancelButton(stringResource(R.string.string_cancel), onCancel)
+ }
+ Divider(
+ thickness = 18.dp,
+ color = Color.Transparent,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ }
+ }
+}
+
+@ExperimentalMaterialApi
+@Composable
+fun CreateOptionRow(createOptionInfo: CreateOptionInfo, onOptionSelected: (String) -> Unit) {
+ Chip(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = {onOptionSelected(createOptionInfo.id)},
+ leadingIcon = {
+ Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
+ bitmap = createOptionInfo.icon.toBitmap().asImageBitmap(),
+ // painter = painterResource(R.drawable.ic_passkey),
+ // TODO: add description.
+ contentDescription = "")
+ },
+ colors = ChipDefaults.chipColors(
+ backgroundColor = Grey100,
+ leadingIconContentColor = Grey100
+ ),
+ shape = Shapes.large
+ ) {
+ Column() {
+ Text(
+ text = createOptionInfo.title,
+ style = Typography.h6,
+ modifier = Modifier.padding(top = 16.dp)
+ )
+ Text(
+ text = createOptionInfo.subtitle,
+ style = Typography.body2,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ }
+ }
+}
+
+@ExperimentalMaterialApi
+@Composable
+fun MoreOptionRow(onSelect: () -> Unit) {
+ Chip(
+ modifier = Modifier.fillMaxWidth().height(52.dp),
+ onClick = onSelect,
+ colors = ChipDefaults.chipColors(
+ backgroundColor = Grey100,
+ leadingIconContentColor = Grey100
+ ),
+ shape = Shapes.large
+ ) {
+ Text(
+ text = stringResource(R.string.string_more_options),
+ style = Typography.h6,
+ )
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
new file mode 100644
index 000000000000..e42016d59b0b
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/createflow/CreatePasskeyViewModel.kt
@@ -0,0 +1,58 @@
+package com.android.credentialmanager.createflow
+
+import android.util.Log
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.ViewModel
+import com.android.credentialmanager.CredentialManagerRepo
+
+data class CreatePasskeyUiState(
+ val providers: List<ProviderInfo>,
+ val currentScreenState: CreateScreenState,
+ val selectedProvider: ProviderInfo? = null,
+)
+
+class CreatePasskeyViewModel(
+ credManRepo: CredentialManagerRepo = CredentialManagerRepo.getInstance()
+) : ViewModel() {
+
+ var uiState by mutableStateOf(credManRepo.createPasskeyInitialUiState())
+ private set
+
+ fun onConfirmIntro() {
+ if (uiState.providers.size > 1) {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.PROVIDER_SELECTION
+ )
+ } else if (uiState.providers.size == 1){
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
+ selectedProvider = uiState.providers.first()
+ )
+ } else {
+ throw java.lang.IllegalStateException("Empty provider list.")
+ }
+ }
+
+ fun onProviderSelected(providerName: String) {
+ uiState = uiState.copy(
+ currentScreenState = CreateScreenState.CREATION_OPTION_SELECTION,
+ selectedProvider = getProviderInfoByName(providerName)
+ )
+ }
+
+ fun onCreateOptionSelected(createOptionId: String) {
+ Log.d("Account Selector", "Option selected for creation: $createOptionId")
+ }
+
+ fun getProviderInfoByName(providerName: String): ProviderInfo {
+ return uiState.providers.single {
+ it.name.equals(providerName)
+ }
+ }
+
+ fun onMoreOptionSelected() {
+ Log.d("Account Selector", "On more option selected")
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
new file mode 100644
index 000000000000..4b957e84fc2b
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt
@@ -0,0 +1,203 @@
+package com.android.credentialmanager.getflow
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material.Card
+import androidx.compose.material.Chip
+import androidx.compose.material.ChipDefaults
+import androidx.compose.material.Divider
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.Icon
+import androidx.compose.material.ModalBottomSheetLayout
+import androidx.compose.material.ModalBottomSheetValue
+import androidx.compose.material.Text
+import androidx.compose.material.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.asImageBitmap
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.core.graphics.drawable.toBitmap
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.credentialmanager.R
+import com.android.credentialmanager.createflow.CancelButton
+import com.android.credentialmanager.ui.theme.Grey100
+import com.android.credentialmanager.ui.theme.Shapes
+import com.android.credentialmanager.ui.theme.Typography
+import com.android.credentialmanager.ui.theme.lightBackgroundColor
+
+@ExperimentalMaterialApi
+@Composable
+fun GetCredentialScreen(
+ viewModel: GetCredentialViewModel = viewModel(),
+ cancelActivity: () -> Unit,
+) {
+ val state = rememberModalBottomSheetState(
+ initialValue = ModalBottomSheetValue.Expanded,
+ skipHalfExpanded = true
+ )
+ ModalBottomSheetLayout(
+ sheetState = state,
+ sheetContent = {
+ val uiState = viewModel.uiState
+ when (uiState.currentScreenState) {
+ GetScreenState.CREDENTIAL_SELECTION -> CredentialSelectionCard(
+ providerInfo = uiState.selectedProvider!!,
+ onCancel = cancelActivity,
+ onOptionSelected = {viewModel.onCredentailSelected(it)},
+ multiProvider = uiState.providers.size > 1,
+ onMoreOptionSelected = {viewModel.onMoreOptionSelected()},
+ )
+ }
+ },
+ scrimColor = Color.Transparent,
+ sheetShape = Shapes.medium,
+ ) {}
+ LaunchedEffect(state.currentValue) {
+ when (state.currentValue) {
+ ModalBottomSheetValue.Hidden -> {
+ cancelActivity()
+ }
+ }
+ }
+}
+
+@ExperimentalMaterialApi
+@Composable
+fun CredentialSelectionCard(
+ providerInfo: ProviderInfo,
+ onOptionSelected: (String) -> Unit,
+ onCancel: () -> Unit,
+ multiProvider: Boolean,
+ onMoreOptionSelected: () -> Unit,
+) {
+ Card(
+ backgroundColor = lightBackgroundColor,
+ ) {
+ Column() {
+ Icon(
+ bitmap = providerInfo.credentialTypeIcon.toBitmap().asImageBitmap(),
+ contentDescription = null,
+ tint = Color.Unspecified,
+ modifier = Modifier.align(alignment = Alignment.CenterHorizontally).padding(top = 24.dp)
+ )
+ Text(
+ text = stringResource(R.string.choose_sign_in_title),
+ style = Typography.subtitle1,
+ modifier = Modifier
+ .padding(all = 24.dp)
+ .align(alignment = Alignment.CenterHorizontally)
+ )
+ Text(
+ text = providerInfo.appDomainName,
+ style = Typography.body2,
+ modifier = Modifier.padding(horizontal = 28.dp)
+ )
+ Divider(
+ thickness = 24.dp,
+ color = Color.Transparent
+ )
+ Card(
+ shape = Shapes.medium,
+ modifier = Modifier
+ .padding(horizontal = 24.dp)
+ .align(alignment = Alignment.CenterHorizontally)
+ ) {
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(2.dp)
+ ) {
+ providerInfo.credentialOptions.forEach {
+ item {
+ CredentialOptionRow(credentialOptionInfo = it, onOptionSelected = onOptionSelected)
+ }
+ }
+ if (multiProvider) {
+ item {
+ MoreOptionRow(onSelect = onMoreOptionSelected)
+ }
+ }
+ }
+ }
+ Divider(
+ thickness = 24.dp,
+ color = Color.Transparent
+ )
+ Row(
+ horizontalArrangement = Arrangement.Start,
+ modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
+ ) {
+ CancelButton(stringResource(R.string.string_no_thanks), onCancel)
+ }
+ Divider(
+ thickness = 18.dp,
+ color = Color.Transparent,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ }
+ }
+}
+
+@ExperimentalMaterialApi
+@Composable
+fun CredentialOptionRow(
+ credentialOptionInfo: CredentialOptionInfo,
+ onOptionSelected: (String) -> Unit
+) {
+ Chip(
+ modifier = Modifier.fillMaxWidth(),
+ onClick = {onOptionSelected(credentialOptionInfo.id)},
+ leadingIcon = {
+ Image(modifier = Modifier.size(24.dp, 24.dp).padding(start = 10.dp),
+ bitmap = credentialOptionInfo.icon.toBitmap().asImageBitmap(),
+ // TODO: add description.
+ contentDescription = "")
+ },
+ colors = ChipDefaults.chipColors(
+ backgroundColor = Grey100,
+ leadingIconContentColor = Grey100
+ ),
+ shape = Shapes.large
+ ) {
+ Column() {
+ Text(
+ text = credentialOptionInfo.title,
+ style = Typography.h6,
+ modifier = Modifier.padding(top = 16.dp)
+ )
+ Text(
+ text = credentialOptionInfo.subtitle,
+ style = Typography.body2,
+ modifier = Modifier.padding(bottom = 16.dp)
+ )
+ }
+ }
+}
+
+@ExperimentalMaterialApi
+@Composable
+fun MoreOptionRow(onSelect: () -> Unit) {
+ Chip(
+ modifier = Modifier.fillMaxWidth().height(52.dp),
+ onClick = onSelect,
+ colors = ChipDefaults.chipColors(
+ backgroundColor = Grey100,
+ leadingIconContentColor = Grey100
+ ),
+ shape = Shapes.large
+ ) {
+ Text(
+ text = stringResource(R.string.string_more_options),
+ style = Typography.h6,
+ )
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
new file mode 100644
index 000000000000..06bcd7fedc6f
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialViewModel.kt
@@ -0,0 +1,30 @@
+package com.android.credentialmanager.getflow
+
+import android.util.Log
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.ViewModel
+import com.android.credentialmanager.CredentialManagerRepo
+
+data class GetCredentialUiState(
+ val providers: List<ProviderInfo>,
+ val currentScreenState: GetScreenState,
+ val selectedProvider: ProviderInfo? = null,
+)
+
+class GetCredentialViewModel(
+ credManRepo: CredentialManagerRepo = CredentialManagerRepo.getInstance()
+) : ViewModel() {
+
+ var uiState by mutableStateOf(credManRepo.getCredentialInitialUiState())
+ private set
+
+ fun onCredentailSelected(credentialId: String) {
+ Log.d("Account Selector", "credential selected: $credentialId")
+ }
+
+ fun onMoreOptionSelected() {
+ Log.d("Account Selector", "More Option selected")
+ }
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
new file mode 100644
index 000000000000..867e9c2acc63
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt
@@ -0,0 +1,23 @@
+package com.android.credentialmanager.getflow
+
+import android.graphics.drawable.Drawable
+
+data class ProviderInfo(
+ val icon: Drawable,
+ val name: String,
+ val appDomainName: String,
+ val credentialTypeIcon: Drawable,
+ val credentialOptions: List<CredentialOptionInfo>,
+)
+
+data class CredentialOptionInfo(
+ val icon: Drawable,
+ val title: String,
+ val subtitle: String,
+ val id: String,
+)
+
+/** The name of the current screen. */
+enum class GetScreenState {
+ CREDENTIAL_SELECTION,
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt
new file mode 100644
index 000000000000..abb4bfbf915e
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Color.kt
@@ -0,0 +1,14 @@
+package com.android.credentialmanager.ui.theme
+
+import androidx.compose.ui.graphics.Color
+
+val Grey100 = Color(0xFFF1F3F4)
+val Purple200 = Color(0xFFBB86FC)
+val Purple500 = Color(0xFF6200EE)
+val Purple700 = Color(0xFF3700B3)
+val Teal200 = Color(0xFF03DAC5)
+val lightColorAccentSecondary = Color(0xFFC2E7FF)
+val lightBackgroundColor = Color(0xFFF0F0F0)
+val lightSurface1 = Color(0xFF6991D6)
+val textColorSecondary = Color(0xFF40484B)
+val textColorPrimary = Color(0xFF191C1D)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Shape.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Shape.kt
new file mode 100644
index 000000000000..cba86585ee59
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Shape.kt
@@ -0,0 +1,11 @@
+package com.android.credentialmanager.ui.theme
+
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Shapes
+import androidx.compose.ui.unit.dp
+
+val Shapes = Shapes(
+ small = RoundedCornerShape(100.dp),
+ medium = RoundedCornerShape(20.dp),
+ large = RoundedCornerShape(0.dp)
+)
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt
new file mode 100644
index 000000000000..a9d20ae9c42e
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Theme.kt
@@ -0,0 +1,47 @@
+package com.android.credentialmanager.ui.theme
+
+import androidx.compose.foundation.isSystemInDarkTheme
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.darkColors
+import androidx.compose.material.lightColors
+import androidx.compose.runtime.Composable
+
+private val DarkColorPalette = darkColors(
+ primary = Purple200,
+ primaryVariant = Purple700,
+ secondary = Teal200
+)
+
+private val LightColorPalette = lightColors(
+ primary = Purple500,
+ primaryVariant = Purple700,
+ secondary = Teal200
+
+ /* Other default colors to override
+ background = Color.White,
+ surface = Color.White,
+ onPrimary = Color.White,
+ onSecondary = Color.Black,
+ onBackground = Color.Black,
+ onSurface = Color.Black,
+ */
+)
+
+@Composable
+fun CredentialSelectorTheme(
+ darkTheme: Boolean = isSystemInDarkTheme(),
+ content: @Composable () -> Unit
+) {
+ val colors = if (darkTheme) {
+ DarkColorPalette
+ } else {
+ LightColorPalette
+ }
+
+ MaterialTheme(
+ colors = colors,
+ typography = Typography,
+ shapes = Shapes,
+ content = content
+ )
+}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt
new file mode 100644
index 000000000000..d8fb01c17f95
--- /dev/null
+++ b/packages/CredentialManager/src/com/android/credentialmanager/ui/theme/Type.kt
@@ -0,0 +1,56 @@
+package com.android.credentialmanager.ui.theme
+
+import androidx.compose.material.Typography
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+
+// Set of Material typography styles to start with
+val Typography = Typography(
+ subtitle1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 24.sp,
+ lineHeight = 32.sp,
+ ),
+ body1 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 14.sp,
+ lineHeight = 20.sp,
+ ),
+ body2 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 14.sp,
+ lineHeight = 20.sp,
+ color = textColorSecondary
+ ),
+ button = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 14.sp,
+ lineHeight = 20.sp,
+ ),
+ h6 = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Medium,
+ fontSize = 16.sp,
+ lineHeight = 24.sp,
+ color = textColorPrimary
+ ),
+
+ /* Other default text styles to override
+ button = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.W500,
+ fontSize = 14.sp
+ ),
+ caption = TextStyle(
+ fontFamily = FontFamily.Default,
+ fontWeight = FontWeight.Normal,
+ fontSize = 12.sp
+ )
+ */
+)
diff --git a/packages/DynamicSystemInstallationService/res/values-ro/strings.xml b/packages/DynamicSystemInstallationService/res/values-ro/strings.xml
index 27b88db6eef4..22a46d5ec748 100644
--- a/packages/DynamicSystemInstallationService/res/values-ro/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values-ro/strings.xml
@@ -1,16 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="keyguard_description" msgid="8582605799129954556">"Introduceți parola și accesați Actualizările de sistem dinamice"</string>
+ <string name="keyguard_description" msgid="8582605799129954556">"Introdu parola și accesați Actualizările de sistem dinamice"</string>
<string name="notification_install_completed" msgid="6252047868415172643">"Sistemul dinamic este pregătit. Ca să începeți să-l folosiți, reporniți dispozitivul."</string>
<string name="notification_install_inprogress" msgid="7383334330065065017">"Se instalează"</string>
<string name="notification_install_failed" msgid="4066039210317521404">"Instalarea nu a reușit"</string>
<string name="notification_image_validation_failed" msgid="2720357826403917016">"Nu s-a validat imaginea. Abandonați instalarea."</string>
- <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Rulăm un sistem dinamic. Reporniți pentru a folosi versiunea Android inițială."</string>
- <string name="notification_action_cancel" msgid="5929299408545961077">"Anulați"</string>
+ <string name="notification_dynsystem_in_use" msgid="1053194595682188396">"Rulăm un sistem dinamic. Repornește pentru a folosi versiunea Android inițială."</string>
+ <string name="notification_action_cancel" msgid="5929299408545961077">"Anulează"</string>
<string name="notification_action_discard" msgid="1817481003134947493">"Renunțați"</string>
- <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Reporniți"</string>
- <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Reporniți"</string>
+ <string name="notification_action_reboot_to_dynsystem" msgid="4015817159115912479">"Repornește"</string>
+ <string name="notification_action_reboot_to_origin" msgid="4013901243271889897">"Repornește"</string>
<string name="toast_dynsystem_discarded" msgid="1733249860276017050">"S-a renunțat la sistemul dinamic"</string>
<string name="toast_failed_to_reboot_to_dynsystem" msgid="6336737274625452067">"Nu se poate reporni sau încărca sistemul dinamic"</string>
<string name="toast_failed_to_disable_dynsystem" msgid="3285742944977744413">"Sistemul dinamic nu a fost dezactivat"</string>
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
index 03cb145e84da..6b793fcd810c 100644
--- a/packages/PackageInstaller/res/values-ro/strings.xml
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -17,10 +17,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="7488448184431507488">"Program de instalare a pachetelor"</string>
- <string name="install" msgid="711829760615509273">"Instalați"</string>
- <string name="update" msgid="3932142540719227615">"Actualizați"</string>
+ <string name="install" msgid="711829760615509273">"Instalează"</string>
+ <string name="update" msgid="3932142540719227615">"Actualizează"</string>
<string name="done" msgid="6632441120016885253">"Gata"</string>
- <string name="cancel" msgid="1018267193425558088">"Anulați"</string>
+ <string name="cancel" msgid="1018267193425558088">"Anulează"</string>
<string name="installing" msgid="4921993079741206516">"Se instalează…"</string>
<string name="installing_app" msgid="1165095864863849422">"Se instalează <xliff:g id="PACKAGE_LABEL">%1$s</xliff:g>…"</string>
<string name="install_done" msgid="5987363587661783896">"Aplicație instalată."</string>
@@ -29,36 +29,36 @@
<string name="install_failed" msgid="5777824004474125469">"Aplicația nu a fost instalată."</string>
<string name="install_failed_blocked" msgid="8512284352994752094">"Instalarea pachetului a fost blocată."</string>
<string name="install_failed_conflict" msgid="3493184212162521426">"Aplicația nu a fost instalată deoarece pachetul intră în conflict cu un pachet existent."</string>
- <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"Aplicația nu a fost instalată deoarece nu este compatibilă cu tableta dvs."</string>
- <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"Aplicația nu este compatibilă cu televizorul dvs."</string>
- <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"Aplicația nu a fost instalată deoarece nu este compatibilă cu telefonul dvs."</string>
+ <string name="install_failed_incompatible" product="tablet" msgid="6019021440094927928">"Aplicația nu a fost instalată deoarece nu este compatibilă cu tableta."</string>
+ <string name="install_failed_incompatible" product="tv" msgid="2890001324362291683">"Aplicația nu este compatibilă cu televizorul."</string>
+ <string name="install_failed_incompatible" product="default" msgid="7254630419511645826">"Aplicația nu a fost instalată deoarece nu este compatibilă cu telefonul."</string>
<string name="install_failed_invalid_apk" msgid="8581007676422623930">"Aplicația nu a fost instalată deoarece pachetul este nevalid."</string>
- <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe tableta dvs."</string>
- <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe televizorul dvs."</string>
- <string name="install_failed_msg" product="default" msgid="6484461562647915707">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe telefonul dvs."</string>
- <string name="launch" msgid="3952550563999890101">"Deschideți"</string>
+ <string name="install_failed_msg" product="tablet" msgid="6298387264270562442">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe tabletă."</string>
+ <string name="install_failed_msg" product="tv" msgid="1920009940048975221">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe televizor."</string>
+ <string name="install_failed_msg" product="default" msgid="6484461562647915707">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată pe telefon."</string>
+ <string name="launch" msgid="3952550563999890101">"Deschide"</string>
<string name="unknown_apps_admin_dlg_text" msgid="4456572224020176095">"Administratorul nu permite instalarea aplicațiilor obținute din surse necunoscute"</string>
<string name="unknown_apps_user_restriction_dlg_text" msgid="151020786933988344">"Aplicațiile necunoscute nu pot fi instalate de acest utilizator"</string>
<string name="install_apps_user_restriction_dlg_text" msgid="2154119597001074022">"Acest utilizator nu are permisiunea să instaleze aplicații"</string>
<string name="ok" msgid="7871959885003339302">"OK"</string>
- <string name="manage_applications" msgid="5400164782453975580">"Gestionați aplicații"</string>
+ <string name="manage_applications" msgid="5400164782453975580">"Gestionează"</string>
<string name="out_of_space_dlg_title" msgid="4156690013884649502">"Spațiu de stocare insuficient"</string>
- <string name="out_of_space_dlg_text" msgid="8727714096031856231">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată. Eliberați spațiu și încercați din nou."</string>
+ <string name="out_of_space_dlg_text" msgid="8727714096031856231">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi instalată. Eliberează spațiu și încearcă din nou."</string>
<string name="app_not_found_dlg_title" msgid="5107924008597470285">"Aplicația nu a fost găsită"</string>
<string name="app_not_found_dlg_text" msgid="5219983779377811611">"Aplicația nu a fost găsită în lista de aplicații instalate."</string>
<string name="user_is_not_allowed_dlg_title" msgid="6915293433252210232">"Nepermis"</string>
<string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"Utilizatorul actual nu are permisiune pentru a face această dezinstalare."</string>
<string name="generic_error_dlg_title" msgid="5863195085927067752">"Eroare"</string>
<string name="generic_error_dlg_text" msgid="5287861443265795232">"Aplicația nu a putut fi dezinstalată."</string>
- <string name="uninstall_application_title" msgid="4045420072401428123">"Dezinstalați aplicația"</string>
- <string name="uninstall_update_title" msgid="824411791011583031">"Dezinstalați actualizarea"</string>
+ <string name="uninstall_application_title" msgid="4045420072401428123">"Dezinstalează aplicația"</string>
+ <string name="uninstall_update_title" msgid="824411791011583031">"Dezinstalează actualizarea"</string>
<string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> face parte din următoarea aplicație:"</string>
- <string name="uninstall_application_text" msgid="3816830743706143980">"Doriți să dezinstalați această aplicație?"</string>
- <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Doriți să dezinstalați această aplicație pentru "<b>"toți"</b>" utilizatorii? Aplicația și datele acesteia vor fi eliminate de la "<b>"toți"</b>" utilizatorii de pe acest dispozitiv."</string>
- <string name="uninstall_application_text_user" msgid="498072714173920526">"Dezinstalați această aplicație pentru utilizatorul <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
+ <string name="uninstall_application_text" msgid="3816830743706143980">"Dezinstalezi această aplicație?"</string>
+ <string name="uninstall_application_text_all_users" msgid="575491774380227119">"Dezinstalezi această aplicație pentru "<b>"toți"</b>" utilizatorii? Aplicația și datele acesteia vor fi eliminate de la "<b>"toți"</b>" utilizatorii de pe acest dispozitiv."</string>
+ <string name="uninstall_application_text_user" msgid="498072714173920526">"Dezinstalezi această aplicație pentru utilizatorul <xliff:g id="USERNAME">%1$s</xliff:g>?"</string>
<string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"Doriți să dezinstalați această aplicație din profilul de serviciu?"</string>
- <string name="uninstall_update_text" msgid="863648314632448705">"Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate."</string>
- <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate. Această acțiune va afecta toți utilizatorii dispozitivului, inclusiv pe cei cu profiluri de serviciu."</string>
+ <string name="uninstall_update_text" msgid="863648314632448705">"Înlocuiești această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate."</string>
+ <string name="uninstall_update_text_multiuser" msgid="8992883151333057227">"Înlocuiești această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate. Această acțiune va afecta toți utilizatorii dispozitivului, inclusiv pe cei cu profiluri de serviciu."</string>
<string name="uninstall_keep_data" msgid="7002379587465487550">"Păstrează <xliff:g id="SIZE">%1$s</xliff:g> din datele aplicației."</string>
<string name="uninstalling_notification_channel" msgid="840153394325714653">"Dezinstalări în curs"</string>
<string name="uninstall_failure_notification_channel" msgid="1136405866767576588">"Dezinstalări nereușite"</string>
@@ -71,10 +71,10 @@
<string name="uninstall_failed_device_policy_manager" msgid="785293813665540305">"Nu se poate dezinstala aplicația activă de administrare a dispozitivului"</string>
<string name="uninstall_failed_device_policy_manager_of_user" msgid="4813104025494168064">"Nu se poate dezinstala aplicația activă de administrare a dispozitivului pentru <xliff:g id="USERNAME">%1$s</xliff:g>"</string>
<string name="uninstall_all_blocked_profile_owner" msgid="2009393666026751501">"Aplicația este necesară unor utilizatori sau profiluri și a fost dezinstalată pentru alții"</string>
- <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"Aplicația este necesară pentru profilul dvs. și nu poate fi dezinstalată."</string>
+ <string name="uninstall_blocked_profile_owner" msgid="6373897407002404848">"Aplicația este necesară pentru profilul tău și nu poate fi dezinstalată."</string>
<string name="uninstall_blocked_device_owner" msgid="6724602931761073901">"Aplicația este necesară administratorului dispozitivului și nu poate fi dezinstalată."</string>
- <string name="manage_device_administrators" msgid="3092696419363842816">"Gestionați aplicațiile de administrare dispozitiv"</string>
- <string name="manage_users" msgid="1243995386982560813">"Gestionați utilizatorii"</string>
+ <string name="manage_device_administrators" msgid="3092696419363842816">"Gestionează aplicațiile de administrare dispozitiv"</string>
+ <string name="manage_users" msgid="1243995386982560813">"Gestionează utilizatorii"</string>
<string name="uninstall_failed_msg" msgid="2176744834786696012">"Aplicația <xliff:g id="APP_NAME">%1$s</xliff:g> nu a putut fi dezinstalată."</string>
<string name="Parse_error_dlg_text" msgid="1661404001063076789">"A apărut o problemă la analizarea pachetului."</string>
<string name="wear_not_allowed_dlg_title" msgid="8664785993465117517">"Android Wear"</string>
@@ -84,10 +84,10 @@
<string name="untrusted_external_source_warning" product="tablet" msgid="7067510047443133095">"Din motive de securitate, tableta dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă. Puteți modifica această opțiune în Setări."</string>
<string name="untrusted_external_source_warning" product="tv" msgid="7057271609532508035">"Din motive de securitate, televizorul dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă. Puteți modifica această opțiune în Setări."</string>
<string name="untrusted_external_source_warning" product="default" msgid="8444191224459138919">"Din motive de securitate, telefonul dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă. Puteți modifica această opțiune în Setări."</string>
- <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonul și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați această aplicație, acceptați că sunteți singura persoană responsabilă pentru deteriorarea telefonului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
- <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tableta și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați aplicația, acceptați că sunteți singura persoană responsabilă pentru deteriorarea tabletei sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
- <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televizorul și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați această aplicație, acceptați că sunteți singura persoană responsabilă pentru deteriorarea televizorului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
- <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuați"</string>
+ <string name="anonymous_source_warning" product="default" msgid="2784902545920822500">"Telefonul și datele tale personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi această aplicație, accepți că ești singura persoană responsabilă pentru deteriorarea telefonului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
+ <string name="anonymous_source_warning" product="tablet" msgid="3939101621438855516">"Tableta și datele tale personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi aplicația, accepți că ești singura persoană responsabilă pentru deteriorarea tabletei sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
+ <string name="anonymous_source_warning" product="tv" msgid="5599483539528168566">"Televizorul și datele tale cu caracter personal sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalezi această aplicație, accepți că ești singura persoană responsabilă pentru deteriorarea televizorului sau pentru pierderea datelor, care pot avea loc în urma folosirii acesteia."</string>
+ <string name="anonymous_source_continue" msgid="4375745439457209366">"Continuă"</string>
<string name="external_sources_settings" msgid="4046964413071713807">"Setări"</string>
<string name="wear_app_channel" msgid="1960809674709107850">"Se (dez)instalează aplicațiile Wear"</string>
<string name="app_installed_notification_channel_description" msgid="2695385797601574123">"Notificare de aplicație instalată"</string>
diff --git a/packages/PrintSpooler/res/values-ro/strings.xml b/packages/PrintSpooler/res/values-ro/strings.xml
index e0fb0b89ea88..507088fdbe03 100644
--- a/packages/PrintSpooler/res/values-ro/strings.xml
+++ b/packages/PrintSpooler/res/values-ro/strings.xml
@@ -27,15 +27,15 @@
<string name="label_duplex" msgid="5370037254347072243">"Față-verso"</string>
<string name="label_orientation" msgid="2853142581990496477">"Orientare"</string>
<string name="label_pages" msgid="7768589729282182230">"Pagini"</string>
- <string name="destination_default_text" msgid="5422708056807065710">"Selectați imprimanta"</string>
+ <string name="destination_default_text" msgid="5422708056807065710">"Selectează imprimanta"</string>
<string name="template_all_pages" msgid="3322235982020148762">"Toate cele <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="template_page_range" msgid="428638530038286328">"Intervalul de <xliff:g id="PAGE_COUNT">%1$s</xliff:g>"</string>
<string name="pages_range_example" msgid="8558694453556945172">"de ex. 1-5, 8, 11-13"</string>
- <string name="print_preview" msgid="8010217796057763343">"Previzualizați printarea"</string>
- <string name="install_for_print_preview" msgid="6366303997385509332">"Instalați PDF viewer pentru previzualizare"</string>
+ <string name="print_preview" msgid="8010217796057763343">"Previzualizează printarea"</string>
+ <string name="install_for_print_preview" msgid="6366303997385509332">"Instalează PDF viewer pentru previzualizare"</string>
<string name="printing_app_crashed" msgid="854477616686566398">"Aplicația de printare s-a blocat"</string>
<string name="generating_print_job" msgid="3119608742651698916">"Se generează sarcină printare"</string>
- <string name="save_as_pdf" msgid="5718454119847596853">"Salvați ca PDF"</string>
+ <string name="save_as_pdf" msgid="5718454119847596853">"Salvează ca PDF"</string>
<string name="all_printers" msgid="5018829726861876202">"Toate imprimantele..."</string>
<string name="print_dialog" msgid="32628687461331979">"Caseta de dialog de printare"</string>
<string name="current_page_template" msgid="5145005201131935302">"<xliff:g id="CURRENT_PAGE">%1$d</xliff:g>/<xliff:g id="PAGE_COUNT">%2$d</xliff:g>"</string>
@@ -43,18 +43,18 @@
<string name="summary_template" msgid="8899734908625669193">"Rezumat, copii <xliff:g id="COPIES">%1$s</xliff:g>, dimensiunea paginii <xliff:g id="PAPER_SIZE">%2$s</xliff:g>"</string>
<string name="expand_handle" msgid="7282974448109280522">"Ghidaj de extindere"</string>
<string name="collapse_handle" msgid="6886637989442507451">"Ghidaj de restrângere"</string>
- <string name="print_button" msgid="645164566271246268">"Printați"</string>
- <string name="savetopdf_button" msgid="2976186791686924743">"Salvați în format PDF"</string>
+ <string name="print_button" msgid="645164566271246268">"Printează"</string>
+ <string name="savetopdf_button" msgid="2976186791686924743">"Salvează în format PDF"</string>
<string name="print_options_expanded" msgid="6944679157471691859">"Opțiuni de printare extinse"</string>
<string name="print_options_collapsed" msgid="7455930445670414332">"Opțiuni de printare restrânse"</string>
- <string name="search" msgid="5421724265322228497">"Căutați"</string>
+ <string name="search" msgid="5421724265322228497">"Caută"</string>
<string name="all_printers_label" msgid="3178848870161526399">"Toate imprimantele"</string>
- <string name="add_print_service_label" msgid="5356702546188981940">"Adăugați un serviciu"</string>
+ <string name="add_print_service_label" msgid="5356702546188981940">"Adaugă un serviciu"</string>
<string name="print_search_box_shown_utterance" msgid="7967404953901376090">"Caseta de căutare este afișată"</string>
<string name="print_search_box_hidden_utterance" msgid="5727755169343113351">"Caseta de căutare este ascunsă"</string>
- <string name="print_add_printer" msgid="1088656468360653455">"Adăugați o imprimantă"</string>
- <string name="print_select_printer" msgid="7388760939873368698">"Selectați imprimanta"</string>
- <string name="print_forget_printer" msgid="5035287497291910766">"Omiteți imprimanta"</string>
+ <string name="print_add_printer" msgid="1088656468360653455">"Adaugă o imprimantă"</string>
+ <string name="print_select_printer" msgid="7388760939873368698">"Selectează imprimanta"</string>
+ <string name="print_forget_printer" msgid="5035287497291910766">"Omite imprimanta"</string>
<plurals name="print_search_result_count_utterance" formatted="false" msgid="6997663738361080868">
<item quantity="few"><xliff:g id="COUNT_1">%1$s</xliff:g> imprimante găsite</item>
<item quantity="other"><xliff:g id="COUNT_1">%1$s</xliff:g> de imprimante găsite</item>
@@ -70,26 +70,26 @@
<string name="print_no_print_services" msgid="8561247706423327966">"Niciun serviciu de printare activat"</string>
<string name="print_no_printers" msgid="4869403323900054866">"Nu au fost găsite imprimante"</string>
<string name="cannot_add_printer" msgid="7840348733668023106">"Nu pot fi adăugate imprimante"</string>
- <string name="select_to_add_printers" msgid="3800709038689830974">"Selectați pentru a adăuga o imprimantă"</string>
- <string name="enable_print_service" msgid="3482815747043533842">"Selectați pentru a activa"</string>
+ <string name="select_to_add_printers" msgid="3800709038689830974">"Selectează pentru a adăuga o imprimantă"</string>
+ <string name="enable_print_service" msgid="3482815747043533842">"Selectează pentru a activa"</string>
<string name="enabled_services_title" msgid="7036986099096582296">"Servicii activate"</string>
<string name="recommended_services_title" msgid="3799434882937956924">"Servicii recomandate"</string>
<string name="disabled_services_title" msgid="7313253167968363211">"Servicii dezactivate"</string>
<string name="all_services_title" msgid="5578662754874906455">"Toate serviciile"</string>
<plurals name="print_services_recommendation_subtitle" formatted="false" msgid="5678487708807185138">
- <item quantity="few">Instalați pentru a descoperi <xliff:g id="COUNT_1">%1$s</xliff:g> imprimante</item>
- <item quantity="other">Instalați pentru a descoperi <xliff:g id="COUNT_1">%1$s</xliff:g> de imprimante</item>
- <item quantity="one">Instalați pentru a descoperi <xliff:g id="COUNT_0">%1$s</xliff:g> imprimantă</item>
+ <item quantity="few">Instalează pentru a descoperi <xliff:g id="COUNT_1">%1$s</xliff:g> imprimante</item>
+ <item quantity="other">Instalează pentru a descoperi <xliff:g id="COUNT_1">%1$s</xliff:g> de imprimante</item>
+ <item quantity="one">Instalează pentru a descoperi <xliff:g id="COUNT_0">%1$s</xliff:g> imprimantă</item>
</plurals>
<string name="printing_notification_title_template" msgid="295903957762447362">"Se printează <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="cancelling_notification_title_template" msgid="1821759594704703197">"Se anulează <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="failed_notification_title_template" msgid="2256217208186530973">"Eroare de printare: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
<string name="blocked_notification_title_template" msgid="1175435827331588646">"Printare blocată: <xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g>"</string>
- <string name="cancel" msgid="4373674107267141885">"Anulați"</string>
- <string name="restart" msgid="2472034227037808749">"Reporniți"</string>
+ <string name="cancel" msgid="4373674107267141885">"Anulează"</string>
+ <string name="restart" msgid="2472034227037808749">"Repornește"</string>
<string name="no_connection_to_printer" msgid="2159246915977282728">"Nu există conexiune la o imprimantă"</string>
<string name="reason_unknown" msgid="5507940196503246139">"necunoscut"</string>
- <string name="print_service_security_warning_title" msgid="2160752291246775320">"Folosiți <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
+ <string name="print_service_security_warning_title" msgid="2160752291246775320">"Folosești <xliff:g id="SERVICE">%1$s</xliff:g>?"</string>
<string name="print_service_security_warning_summary" msgid="1427434625361692006">"Documentul poate trece prin unul sau mai multe servere pe calea spre imprimantă."</string>
<string-array name="color_mode_labels">
<item msgid="7602948745415174937">"Alb-negru"</item>
@@ -105,8 +105,8 @@
<item msgid="3199660090246166812">"Peisaj"</item>
</string-array>
<string name="print_write_error_message" msgid="5787642615179572543">"Nu s-a putut scrie în fișier."</string>
- <string name="print_error_default_message" msgid="8602678405502922346">"Ne pare rău, operațiunea nu a reușit. Încercați din nou."</string>
- <string name="print_error_retry" msgid="1426421728784259538">"Reîncercați"</string>
+ <string name="print_error_default_message" msgid="8602678405502922346">"Ne pare rău, operațiunea nu a reușit. Încearcă din nou."</string>
+ <string name="print_error_retry" msgid="1426421728784259538">"Reîncearcă"</string>
<string name="print_error_printer_unavailable" msgid="8985614415253203381">"Această imprimantă nu este disponibilă momentan."</string>
<string name="print_cannot_load_page" msgid="6179560924492912009">"Previzualizarea nu se poate afișa"</string>
<string name="print_preparing_preview" msgid="3939930735671364712">"Se pregătește previzualizarea..."</string>
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 1df9059a7c73..c659525d42a5 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -56,8 +56,12 @@ android_library {
"SettingsLibDeviceStateRotationLock",
"setupdesign",
"zxing-core-1.7",
+ "androidx.room_room-runtime",
+
],
+ plugins: ["androidx.room_room-compiler-plugin"],
+
resource_dirs: ["res"],
srcs: [
diff --git a/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
index 2b5011764508..66196840976d 100644
--- a/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
+++ b/packages/SettingsLib/FooterPreference/res/values-ro/strings.xml
@@ -17,5 +17,5 @@
<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>
+ <string name="settingslib_learn_more_text" msgid="7385478101223578464">"Află mai multe"</string>
</resources>
diff --git a/packages/SettingsLib/Spa/TEST_MAPPING b/packages/SettingsLib/Spa/TEST_MAPPING
index ef3db4aef66d..b4b65d4c0ddc 100644
--- a/packages/SettingsLib/Spa/TEST_MAPPING
+++ b/packages/SettingsLib/Spa/TEST_MAPPING
@@ -2,6 +2,9 @@
"presubmit": [
{
"name": "SpaLibTests"
+ },
+ {
+ "name": "SpaPrivilegedLibTests"
}
]
}
diff --git a/packages/SettingsLib/Spa/build.gradle b/packages/SettingsLib/Spa/build.gradle
index 5dc2aa0d61cb..6384cad7d620 100644
--- a/packages/SettingsLib/Spa/build.gradle
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -16,13 +16,13 @@
buildscript {
ext {
- spa_min_sdk = 31
+ spa_min_sdk = 21
jetpack_compose_version = '1.2.0-alpha04'
jetpack_compose_material3_version = '1.0.0-alpha06'
}
}
plugins {
- id 'com.android.application' version '7.3.0-rc01' apply false
- id 'com.android.library' version '7.3.0-rc01' apply false
+ id 'com.android.application' version '7.3.0' apply false
+ id 'com.android.library' version '7.3.0' apply false
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}
diff --git a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
index e5bf8ca60576..e583138fd88a 100644
--- a/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
+++ b/packages/SettingsLib/Spa/gallery/AndroidManifest.xml
@@ -20,7 +20,8 @@
<application
android:icon="@mipmap/ic_launcher"
android:label="@string/app_label"
- android:supportsRtl="true">
+ android:supportsRtl="true"
+ android:enableOnBackInvokedCallback="true">
<activity
android:name=".MainActivity"
android:exported="true">
@@ -34,5 +35,13 @@
android:name=".GalleryDebugActivity"
android:exported="true">
</activity>
+
+ <provider
+ android:name=".GalleryEntryProvider"
+ android:authorities="com.android.spa.gallery.provider"
+ android:enabled="true"
+ android:exported="false">
+ </provider>
+
</application>
</manifest>
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
index 317346d26f89..332d5a836c9f 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryDebugActivity.kt
@@ -18,4 +18,4 @@ package com.android.settingslib.spa.gallery
import com.android.settingslib.spa.framework.DebugActivity
-class GalleryDebugActivity : DebugActivity(SpaEnvironment.EntryRepository, MainActivity::class.java)
+class GalleryDebugActivity : DebugActivity(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
new file mode 100644
index 000000000000..5e048612292d
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GalleryEntryProvider.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.gallery
+
+import com.android.settingslib.spa.framework.EntryProvider
+
+class GalleryEntryProvider : EntryProvider(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
index 787d0961fb21..33c4d7712753 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/SpaEnvironment.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/GallerySpaEnvironment.kt
@@ -16,9 +16,12 @@
package com.android.settingslib.spa.gallery
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
+import android.os.Bundle
+import androidx.navigation.NamedNavArgument
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
+import com.android.settingslib.spa.framework.common.SpaEnvironment
+import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
import com.android.settingslib.spa.gallery.home.HomePageProvider
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
@@ -30,11 +33,37 @@ import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
import com.android.settingslib.spa.gallery.preference.PreferencePageProvider
import com.android.settingslib.spa.gallery.preference.SwitchPreferencePageProvider
import com.android.settingslib.spa.gallery.preference.TwoTargetSwitchPreferencePageProvider
+import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
-object SpaEnvironment {
- val PageProviderRepository: SettingsPageProviderRepository by
- lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
+/**
+ * Enum to define all SPP name here.
+ * Since the SPP name would be used in log, DO NOT change it once it is set. One can still change
+ * the display name for better readability if necessary.
+ */
+enum class SettingsPageProviderEnum(val displayName: String) {
+ HOME("home"),
+ PREFERENCE("preference"),
+ ARGUMENT("argument"),
+
+ // Add your SPPs
+}
+
+fun createSettingsPage(
+ SppName: SettingsPageProviderEnum,
+ parameter: List<NamedNavArgument> = emptyList(),
+ arguments: Bundle? = null
+): SettingsPage {
+ return SettingsPage.create(
+ name = SppName.name,
+ displayName = SppName.displayName,
+ parameter = parameter,
+ arguments = arguments,
+ )
+}
+
+object GallerySpaEnvironment : SpaEnvironment() {
+ override val pageProviderRepository = lazy {
SettingsPageProviderRepository(
allPageProviders = listOf(
HomePageProvider,
@@ -49,16 +78,16 @@ object SpaEnvironment {
SettingsPagerPageProvider,
FooterPageProvider,
IllustrationPageProvider,
+ CategoryPageProvider,
+ ActionButtonPageProvider,
),
rootPages = listOf(
- SettingsPage.create(HomePageProvider.name)
+ createSettingsPage(SettingsPageProviderEnum.HOME)
)
)
}
- val EntryRepository: SettingsEntryRepository by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
- SettingsEntryRepository(PageProviderRepository)
- }
+ override val browseActivityClass = MainActivity::class.java
- // TODO: add other environment setup here.
+ override val entryProviderAuthorities = "com.android.spa.gallery.provider"
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
index a06384762372..5e859ce778cf 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/MainActivity.kt
@@ -18,4 +18,4 @@ package com.android.settingslib.spa.gallery
import com.android.settingslib.spa.framework.BrowseActivity
-class MainActivity : BrowseActivity(SpaEnvironment.PageProviderRepository)
+class MainActivity : BrowseActivity(GallerySpaEnvironment)
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt
new file mode 100644
index 000000000000..96e2498e39bf
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/button/ActionButtonsPage.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.gallery.button
+
+import android.os.Bundle
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material.icons.outlined.Launch
+import androidx.compose.material.icons.outlined.WarningAmber
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.button.ActionButton
+import com.android.settingslib.spa.widget.button.ActionButtons
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+
+private const val TITLE = "Sample ActionButton"
+
+object ActionButtonPageProvider : SettingsPageProvider {
+ override val name = "ActionButton"
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ ActionButtonPage()
+ }
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name))
+ .setIsAllowSearch(true)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+}
+
+@Composable
+fun ActionButtonPage() {
+ RegularScaffold(title = TITLE) {
+ val actionButtons = listOf(
+ ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
+ ActionButton(text = "Uninstall", imageVector = Icons.Outlined.Delete) {},
+ ActionButton(text = "Force stop", imageVector = Icons.Outlined.WarningAmber) {},
+ )
+ ActionButtons(actionButtons)
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun ActionButtonPagePreview() {
+ SettingsTheme {
+ ActionButtonPage()
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
index d8ef937b50c2..c68e918eb449 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/home/HomePage.kt
@@ -18,13 +18,18 @@ package com.android.settingslib.spa.gallery.home
import android.os.Bundle
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
-import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.util.getRuntimeArguments
+import com.android.settingslib.spa.framework.util.mergeArguments
import com.android.settingslib.spa.gallery.R
+import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
+import com.android.settingslib.spa.gallery.button.ActionButtonPageProvider
+import com.android.settingslib.spa.gallery.createSettingsPage
import com.android.settingslib.spa.gallery.page.ArgumentPageModel
import com.android.settingslib.spa.gallery.page.ArgumentPageProvider
import com.android.settingslib.spa.gallery.page.FooterPageProvider
@@ -32,14 +37,15 @@ import com.android.settingslib.spa.gallery.page.IllustrationPageProvider
import com.android.settingslib.spa.gallery.page.SettingsPagerPageProvider
import com.android.settingslib.spa.gallery.page.SliderPageProvider
import com.android.settingslib.spa.gallery.preference.PreferenceMainPageProvider
+import com.android.settingslib.spa.gallery.ui.CategoryPageProvider
import com.android.settingslib.spa.gallery.ui.SpinnerPageProvider
import com.android.settingslib.spa.widget.scaffold.HomeScaffold
object HomePageProvider : SettingsPageProvider {
- override val name = "Home"
+ override val name = SettingsPageProviderEnum.HOME.name
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
- val owner = SettingsPage.create(name)
+ val owner = createSettingsPage(SettingsPageProviderEnum.HOME)
return listOf(
PreferenceMainPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
ArgumentPageProvider.buildInjectEntry("foo")!!.setLink(fromPage = owner).build(),
@@ -48,17 +54,27 @@ object HomePageProvider : SettingsPageProvider {
SettingsPagerPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
FooterPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
IllustrationPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ CategoryPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
+ ActionButtonPageProvider.buildInjectEntry().setLink(fromPage = owner).build(),
)
}
@Composable
override fun Page(arguments: Bundle?) {
+ val globalRuntimeArgs = remember { getRuntimeArguments(arguments) }
HomeScaffold(title = stringResource(R.string.app_name)) {
for (entry in buildEntry(arguments)) {
- if (entry.name.startsWith(ArgumentPageModel.name)) {
- entry.UiLayout(ArgumentPageModel.buildArgument(intParam = 0))
+ if (entry.owner.isCreateBy(SettingsPageProviderEnum.ARGUMENT.name)) {
+ entry.UiLayout(
+ mergeArguments(
+ listOf(
+ globalRuntimeArgs,
+ ArgumentPageModel.buildArgument(intParam = 0)
+ )
+ )
+ )
} else {
- entry.UiLayout()
+ entry.UiLayout(globalRuntimeArgs)
}
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
index e32de7a67d9d..9bf7ad8fb8f7 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPage.kt
@@ -18,29 +18,47 @@ package com.android.settingslib.spa.gallery.page
import android.os.Bundle
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.util.getRuntimeArguments
+import com.android.settingslib.spa.framework.util.mergeArguments
+import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
+import com.android.settingslib.spa.gallery.createSettingsPage
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
object ArgumentPageProvider : SettingsPageProvider {
- override val name = ArgumentPageModel.name
+ // Defines all entry name in this page.
+ // Note that entry name would be used in log. DO NOT change it once it is set.
+ // One can still change the display name for better readability if necessary.
+ private enum class EntryEnum(val displayName: String) {
+ STRING_PARAM("string_param"),
+ INT_PARAM("int_param"),
+ }
+
+ private fun createEntry(owner: SettingsPage, entry: EntryEnum): SettingsEntryBuilder {
+ return SettingsEntryBuilder.create(owner, entry.name, entry.displayName)
+ }
+
+ override val name = SettingsPageProviderEnum.ARGUMENT.name
override val parameter = ArgumentPageModel.parameter
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
if (!ArgumentPageModel.isValidArgument(arguments)) return emptyList()
- val owner = SettingsPage.create(name, parameter, arguments)
+ val owner = createSettingsPage(SettingsPageProviderEnum.ARGUMENT, parameter, arguments)
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
- SettingsEntryBuilder.create("string_param", owner)
+ createEntry(owner, EntryEnum.STRING_PARAM)
// Set attributes
.setIsAllowSearch(true)
+ .setSearchDataFn { ArgumentPageModel.genStringParamSearchData() }
.setUiLayoutFn {
// Set ui rendering
Preference(ArgumentPageModel.create(it).genStringParamPreferenceModel())
@@ -48,9 +66,10 @@ object ArgumentPageProvider : SettingsPageProvider {
)
entryList.add(
- SettingsEntryBuilder.create("int_param", owner)
+ createEntry(owner, EntryEnum.INT_PARAM)
// Set attributes
.setIsAllowSearch(true)
+ .setSearchDataFn { ArgumentPageModel.genIntParamSearchData() }
.setUiLayoutFn {
// Set ui rendering
Preference(ArgumentPageModel.create(it).genIntParamPreferenceModel())
@@ -68,11 +87,12 @@ object ArgumentPageProvider : SettingsPageProvider {
if (!ArgumentPageModel.isValidArgument(arguments)) return null
return SettingsEntryBuilder.createInject(
- entryName = "${name}_$stringParam",
- owner = SettingsPage.create(name, parameter, arguments)
+ owner = createSettingsPage(SettingsPageProviderEnum.ARGUMENT, parameter, arguments),
+ displayName = "${name}_$stringParam",
)
// Set attributes
.setIsAllowSearch(false)
+ .setSearchDataFn { ArgumentPageModel.genInjectSearchData() }
.setUiLayoutFn {
// Set ui rendering
Preference(ArgumentPageModel.create(it).genInjectPreferenceModel())
@@ -81,12 +101,20 @@ object ArgumentPageProvider : SettingsPageProvider {
@Composable
override fun Page(arguments: Bundle?) {
+ val globalRuntimeArgs = remember { getRuntimeArguments(arguments) }
RegularScaffold(title = ArgumentPageModel.create(arguments).genPageTitle()) {
for (entry in buildEntry(arguments)) {
- if (entry.name.startsWith(name)) {
- entry.UiLayout(ArgumentPageModel.buildNextArgument(arguments))
+ if (entry.toPage != null) {
+ entry.UiLayout(
+ mergeArguments(
+ listOf(
+ globalRuntimeArgs,
+ ArgumentPageModel.buildNextArgument(arguments)
+ )
+ )
+ )
} else {
- entry.UiLayout()
+ entry.UiLayout(globalRuntimeArgs)
}
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
index 6e86fd785a0e..ee2bde4c2edb 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/page/ArgumentPageModel.kt
@@ -22,23 +22,28 @@ import androidx.compose.runtime.Composable
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavType
import androidx.navigation.navArgument
-import com.android.settingslib.spa.framework.BrowseActivity
+import com.android.settingslib.spa.framework.common.EntrySearchData
import com.android.settingslib.spa.framework.common.PageModel
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.stateOf
import com.android.settingslib.spa.framework.util.getIntArg
import com.android.settingslib.spa.framework.util.getStringArg
import com.android.settingslib.spa.framework.util.navLink
+import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
import com.android.settingslib.spa.widget.preference.PreferenceModel
-private const val TITLE = "Sample page with arguments"
+// Defines all the resources for this page.
+// In real Settings App, resources data is defined in xml, rather than SPP.
+private const val PAGE_TITLE = "Sample page with arguments"
+private const val STRING_PARAM_TITLE = "String param value"
+private const val INT_PARAM_TITLE = "Int param value"
private const val STRING_PARAM_NAME = "stringParam"
private const val INT_PARAM_NAME = "intParam"
+private val ARGUMENT_PAGE_KEYWORDS = listOf("argument keyword1", "argument keyword2")
class ArgumentPageModel : PageModel() {
companion object {
- const val name = "Argument"
val parameter = listOf(
navArgument(STRING_PARAM_NAME) { type = NavType.StringType },
navArgument(INT_PARAM_NAME) { type = NavType.IntType },
@@ -62,6 +67,18 @@ class ArgumentPageModel : PageModel() {
return (stringParam != null && listOf("foo", "bar").contains(stringParam))
}
+ fun genStringParamSearchData(): EntrySearchData {
+ return EntrySearchData(title = STRING_PARAM_TITLE)
+ }
+
+ fun genIntParamSearchData(): EntrySearchData {
+ return EntrySearchData(title = INT_PARAM_TITLE)
+ }
+
+ fun genInjectSearchData(): EntrySearchData {
+ return EntrySearchData(title = PAGE_TITLE, keyword = ARGUMENT_PAGE_KEYWORDS)
+ }
+
@Composable
fun create(arguments: Bundle?): ArgumentPageModel {
val pageModel: ArgumentPageModel = viewModel(key = arguments.toString())
@@ -70,18 +87,16 @@ class ArgumentPageModel : PageModel() {
}
}
- private val title = TITLE
+ private val title = PAGE_TITLE
private var arguments: Bundle? = null
private var stringParam: String? = null
private var intParam: Int? = null
- private var highlightName: String? = null
override fun initialize(arguments: Bundle?) {
logMsg("init with args " + arguments.toString())
this.arguments = arguments
stringParam = parameter.getStringArg(STRING_PARAM_NAME, arguments)
intParam = parameter.getIntArg(INT_PARAM_NAME, arguments)
- highlightName = arguments?.getString(BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME)
}
@Composable
@@ -92,7 +107,7 @@ class ArgumentPageModel : PageModel() {
@Composable
fun genStringParamPreferenceModel(): PreferenceModel {
return object : PreferenceModel {
- override val title = "String param value"
+ override val title = STRING_PARAM_TITLE
override val summary = stateOf(stringParam!!)
}
}
@@ -100,7 +115,7 @@ class ArgumentPageModel : PageModel() {
@Composable
fun genIntParamPreferenceModel(): PreferenceModel {
return object : PreferenceModel {
- override val title = "Int param value"
+ override val title = INT_PARAM_TITLE
override val summary = stateOf(intParam!!.toString())
}
}
@@ -114,7 +129,9 @@ class ArgumentPageModel : PageModel() {
return object : PreferenceModel {
override val title = genPageTitle()
override val summary = stateOf(summaryArray.joinToString(", "))
- override val onClick = navigator(name + parameter.navLink(arguments))
+ override val onClick = navigator(
+ SettingsPageProviderEnum.ARGUMENT.displayName + parameter.navLink(arguments)
+ )
}
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
index cbd028d17cba..e3416c6f14ca 100644
--- a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePage.kt
@@ -18,134 +18,170 @@ package com.android.settingslib.spa.gallery.preference
import android.os.Bundle
import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Autorenew
import androidx.compose.material.icons.outlined.DisabledByDefault
import androidx.compose.material.icons.outlined.TouchApp
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.derivedStateOf
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.produceState
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.remember
import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.EntrySearchData
import com.android.settingslib.spa.framework.common.SettingsEntry
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
-import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
-import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.util.getRuntimeArguments
+import com.android.settingslib.spa.gallery.SettingsPageProviderEnum
+import com.android.settingslib.spa.gallery.createSettingsPage
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.ASYNC_PREFERENCE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.AUTO_UPDATE_PREFERENCE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.DISABLE_PREFERENCE_SUMMARY
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.DISABLE_PREFERENCE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.MANUAL_UPDATE_PREFERENCE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.PAGE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_KEYWORDS
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_SUMMARY
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.SIMPLE_PREFERENCE_TITLE
+import com.android.settingslib.spa.gallery.preference.PreferencePageModel.Companion.logMsg
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.preference.SimplePreferenceMacro
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.SettingsIcon
-import kotlinx.coroutines.delay
-
-private const val TITLE = "Sample Preference"
object PreferencePageProvider : SettingsPageProvider {
- override val name = "Preference"
+ // Defines all entry name in this page.
+ // Note that entry name would be used in log. DO NOT change it once it is set.
+ // One can still change the display name for better readability if necessary.
+ enum class EntryEnum(val displayName: String) {
+ SIMPLE_PREFERENCE("preference"),
+ SUMMARY_PREFERENCE("preference_with_summary"),
+ DISABLED_PREFERENCE("preference_disable"),
+ ASYNC_SUMMARY_PREFERENCE("preference_with_async_summary"),
+ MANUAL_UPDATE_PREFERENCE("preference_actionable"),
+ AUTO_UPDATE_PREFERENCE("preference_auto_update"),
+ }
+
+ override val name = SettingsPageProviderEnum.PREFERENCE.name
+ private val owner = createSettingsPage(SettingsPageProviderEnum.PREFERENCE)
+
+ private fun createEntry(entry: EntryEnum): SettingsEntryBuilder {
+ return SettingsEntryBuilder.create(owner, entry.name, entry.displayName)
+ }
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
- val owner = SettingsPage.create(name)
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
- SettingsEntryBuilder.create("Preference", owner)
+ createEntry(EntryEnum.SIMPLE_PREFERENCE)
.setIsAllowSearch(true)
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "Preference"
- })
- }.build()
+ .setMacro {
+ logMsg("create macro for ${EntryEnum.SIMPLE_PREFERENCE}")
+ SimplePreferenceMacro(title = SIMPLE_PREFERENCE_TITLE)
+ }
+ .build()
)
entryList.add(
- SettingsEntryBuilder.create("Preference with summary", owner)
+ createEntry(EntryEnum.SUMMARY_PREFERENCE)
.setIsAllowSearch(true)
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "Preference"
- override val summary = "With summary".toState()
- })
- }.build()
+ .setMacro {
+ logMsg("create macro for ${EntryEnum.SUMMARY_PREFERENCE}")
+ SimplePreferenceMacro(
+ title = SIMPLE_PREFERENCE_TITLE,
+ summary = SIMPLE_PREFERENCE_SUMMARY,
+ searchKeywords = SIMPLE_PREFERENCE_KEYWORDS,
+ )
+ }
+ .build()
)
entryList.add(
- SettingsEntryBuilder.create("Preference with async summary", owner)
+ createEntry(EntryEnum.DISABLED_PREFERENCE)
.setIsAllowSearch(true)
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "Preference"
- override val summary = produceState(initialValue = " ") {
- delay(1000L)
- value = "Async summary"
- }
- })
- }.build()
+ .setMacro {
+ logMsg("create macro for ${EntryEnum.DISABLED_PREFERENCE}")
+ SimplePreferenceMacro(
+ title = DISABLE_PREFERENCE_TITLE,
+ summary = DISABLE_PREFERENCE_SUMMARY,
+ disabled = true,
+ icon = Icons.Outlined.DisabledByDefault,
+ )
+ }
+ .build()
)
entryList.add(
- SettingsEntryBuilder.create("Click me", owner)
+ createEntry(EntryEnum.ASYNC_SUMMARY_PREFERENCE)
.setIsAllowSearch(true)
+ .setSearchDataFn {
+ EntrySearchData(title = ASYNC_PREFERENCE_TITLE)
+ }
.setUiLayoutFn {
- var count by rememberSaveable { mutableStateOf(0) }
- Preference(object : PreferenceModel {
- override val title = "Click me"
- override val summary = derivedStateOf { count.toString() }
- override val onClick: (() -> Unit) = { count++ }
- override val icon = @Composable {
- SettingsIcon(imageVector = Icons.Outlined.TouchApp)
+ val model = PreferencePageModel.create()
+ val asyncSummary = remember { model.getAsyncSummary() }
+ Preference(
+ object : PreferenceModel {
+ override val title = ASYNC_PREFERENCE_TITLE
+ override val summary = asyncSummary
}
- })
+ )
}.build()
)
entryList.add(
- SettingsEntryBuilder.create("Ticker", owner)
+ createEntry(EntryEnum.MANUAL_UPDATE_PREFERENCE)
.setIsAllowSearch(true)
.setUiLayoutFn {
- var ticks by rememberSaveable { mutableStateOf(0) }
- LaunchedEffect(ticks) {
- delay(1000L)
- ticks++
- }
- Preference(object : PreferenceModel {
- override val title = "Ticker"
- override val summary = derivedStateOf { ticks.toString() }
- })
+ val model = PreferencePageModel.create()
+ val manualUpdaterSummary = remember { model.getManualUpdaterSummary() }
+ Preference(
+ object : PreferenceModel {
+ override val title = MANUAL_UPDATE_PREFERENCE_TITLE
+ override val summary = manualUpdaterSummary
+ override val onClick = { model.manualUpdaterOnClick() }
+ override val icon = @Composable {
+ SettingsIcon(imageVector = Icons.Outlined.TouchApp)
+ }
+ }
+ )
}.build()
)
entryList.add(
- SettingsEntryBuilder.create("Disabled", owner)
+ createEntry(EntryEnum.AUTO_UPDATE_PREFERENCE)
.setIsAllowSearch(true)
.setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = "Disabled"
- override val summary = "Disabled".toState()
- override val enabled = false.toState()
- override val icon = @Composable {
- SettingsIcon(imageVector = Icons.Outlined.DisabledByDefault)
+ val model = PreferencePageModel.create()
+ val autoUpdaterSummary = remember { model.getAutoUpdaterSummary() }
+ Preference(
+ object : PreferenceModel {
+ override val title = AUTO_UPDATE_PREFERENCE_TITLE
+ override val summary = autoUpdaterSummary.observeAsState(" ")
+ override val icon = @Composable {
+ SettingsIcon(imageVector = Icons.Outlined.Autorenew)
+ }
}
- })
- }.build()
+ )
+ }
+ .build()
)
return entryList
}
fun buildInjectEntry(): SettingsEntryBuilder {
- return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name))
+ return SettingsEntryBuilder.createInject(owner = owner)
.setIsAllowSearch(true)
- .setUiLayoutFn {
- Preference(object : PreferenceModel {
- override val title = TITLE
- override val onClick = navigator(name)
- })
+ .setMacro {
+ logMsg("create macro for INJECT entry")
+ SimplePreferenceMacro(
+ title = PAGE_TITLE,
+ clickRoute = SettingsPageProviderEnum.PREFERENCE.name
+ )
}
}
@Composable
override fun Page(arguments: Bundle?) {
- RegularScaffold(title = TITLE) {
+ val globalRuntimeArgs = remember { getRuntimeArguments(arguments) }
+ RegularScaffold(title = PAGE_TITLE) {
for (entry in buildEntry(arguments)) {
- entry.UiLayout()
+ entry.UiLayout(globalRuntimeArgs)
}
}
}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt
new file mode 100644
index 000000000000..1188e1e006b2
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/preference/PreferencePageModel.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.gallery.preference
+
+import android.os.Bundle
+import android.util.Log
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.android.settingslib.spa.framework.common.PageModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+
+class PreferencePageModel : PageModel() {
+ companion object {
+ // Defines all the resources for this page.
+ // In real Settings App, resources data is defined in xml, rather than SPP.
+ const val PAGE_TITLE = "Sample Preference"
+ const val SIMPLE_PREFERENCE_TITLE = "Preference"
+ const val SIMPLE_PREFERENCE_SUMMARY = "Simple summary"
+ const val DISABLE_PREFERENCE_TITLE = "Disabled"
+ const val DISABLE_PREFERENCE_SUMMARY = "Disabled summary"
+ const val ASYNC_PREFERENCE_TITLE = "Async Preference"
+ private const val ASYNC_PREFERENCE_SUMMARY = "Async summary"
+ const val MANUAL_UPDATE_PREFERENCE_TITLE = "Manual Updater"
+ const val AUTO_UPDATE_PREFERENCE_TITLE = "Auto Updater"
+ val SIMPLE_PREFERENCE_KEYWORDS = listOf("simple keyword1", "simple keyword2")
+
+ @Composable
+ fun create(): PreferencePageModel {
+ val pageModel: PreferencePageModel = viewModel()
+ pageModel.initOnce()
+ return pageModel
+ }
+
+ fun logMsg(message: String) {
+ Log.d("PreferencePageModel", message)
+ }
+ }
+
+ private val asyncSummary = mutableStateOf(" ")
+
+ private val manualUpdater = mutableStateOf(0)
+
+ private val autoUpdater = object : MutableLiveData<String>(" ") {
+ private var tick = 0
+ private var updateJob: Job? = null
+ override fun onActive() {
+ logMsg("autoUpdater.active")
+ updateJob = viewModelScope.launch(Dispatchers.IO) {
+ while (true) {
+ delay(1000L)
+ tick++
+ logMsg("autoUpdater.value $tick")
+ postValue(tick.toString())
+ }
+ }
+ }
+
+ override fun onInactive() {
+ logMsg("autoUpdater.inactive")
+ updateJob?.cancel()
+ }
+ }
+
+ override fun initialize(arguments: Bundle?) {
+ logMsg("init with args " + arguments.toString())
+
+ viewModelScope.launch(Dispatchers.IO) {
+ delay(2000L)
+ asyncSummary.value = ASYNC_PREFERENCE_SUMMARY
+ }
+ }
+
+ fun getAsyncSummary(): State<String> {
+ logMsg("getAsyncSummary")
+ return asyncSummary
+ }
+
+ fun getManualUpdaterSummary(): State<String> {
+ logMsg("getManualUpdaterSummary")
+ return derivedStateOf { manualUpdater.value.toString() }
+ }
+
+ fun manualUpdaterOnClick() {
+ logMsg("manualUpdaterOnClick")
+ manualUpdater.value = manualUpdater.value + 1
+ }
+
+ fun getAutoUpdaterSummary(): LiveData<String> {
+ logMsg("getAutoUpdaterSummary")
+ return autoUpdater
+ }
+}
diff --git a/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt
new file mode 100644
index 000000000000..a4713b993af0
--- /dev/null
+++ b/packages/SettingsLib/Spa/gallery/src/com/android/settingslib/spa/gallery/ui/CategoryPage.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.gallery.ui
+
+import android.os.Bundle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.tooling.preview.Preview
+import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
+import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SettingsPageProvider
+import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import com.android.settingslib.spa.widget.scaffold.RegularScaffold
+import com.android.settingslib.spa.widget.ui.Category
+import com.android.settingslib.spa.widget.ui.CategoryTitle
+
+private const val TITLE = "Sample Category"
+
+object CategoryPageProvider : SettingsPageProvider {
+ override val name = "Category"
+
+ fun buildInjectEntry(): SettingsEntryBuilder {
+ return SettingsEntryBuilder.createInject(owner = SettingsPage.create(name))
+ .setIsAllowSearch(true)
+ .setUiLayoutFn {
+ Preference(object : PreferenceModel {
+ override val title = TITLE
+ override val onClick = navigator(name)
+ })
+ }
+ }
+
+ @Composable
+ override fun Page(arguments: Bundle?) {
+ CategoryPage()
+ }
+}
+
+@Composable
+private fun CategoryPage() {
+ RegularScaffold(title = TITLE) {
+ CategoryTitle("Category A")
+ Preference(remember {
+ object : PreferenceModel {
+ override val title = "Preference 1"
+ override val summary = stateOf("Summary 1")
+ }
+ })
+ Preference(remember {
+ object : PreferenceModel {
+ override val title = "Preference 2"
+ override val summary = stateOf("Summary 2")
+ }
+ })
+ Category("Category B") {
+ Preference(remember {
+ object : PreferenceModel {
+ override val title = "Preference 3"
+ override val summary = stateOf("Summary 3")
+ }
+ })
+ Preference(remember {
+ object : PreferenceModel {
+ override val title = "Preference 4"
+ override val summary = stateOf("Summary 4")
+ }
+ })
+ }
+ }
+}
+
+@Preview(showBackground = true)
+@Composable
+private fun SpinnerPagePreview() {
+ SettingsTheme {
+ SpinnerPageProvider.Page(null)
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/build.gradle b/packages/SettingsLib/Spa/spa/build.gradle
index 104966d9b097..418d6cb477fa 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle
+++ b/packages/SettingsLib/Spa/spa/build.gradle
@@ -59,6 +59,7 @@ android {
}
dependencies {
+ api "androidx.appcompat:appcompat:1.6.0-rc01"
api "androidx.compose.material3:material3:$jetpack_compose_material3_version"
api "androidx.compose.material:material-icons-extended:$jetpack_compose_version"
api "androidx.compose.runtime:runtime-livedata:$jetpack_compose_version"
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
index ae15da66d68b..138ea02b04d1 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/BrowseActivity.kt
@@ -22,21 +22,35 @@ import androidx.activity.compose.setContent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
import androidx.navigation.NavGraph.Companion.findStartDestination
+import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.android.settingslib.spa.R
-import com.android.settingslib.spa.framework.common.ROOT_PAGE_NAME
-import com.android.settingslib.spa.framework.common.SettingsPageProviderRepository
+import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.spa.framework.util.navRoute
-open class BrowseActivity(
- private val sppRepository: SettingsPageProviderRepository,
-) : ComponentActivity() {
+const val NULL_PAGE_NAME = "NULL"
+
+/**
+ * The Activity to render ALL SPA pages, and handles jumps between SPA pages.
+ * One can open any SPA page by:
+ * $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination <SpaPageRoute>
+ * For gallery, BrowseActivityComponent = com.android.settingslib.spa.gallery/.MainActivity
+ * For SettingsGoogle, BrowseActivityComponent = com.android.settings/.spa.SpaActivity
+ * Some examples:
+ * $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination HOME
+ * $ adb shell am start -n <BrowseActivityComponent> -e spa:SpaActivity:destination ARGUMENT/bar/5
+ */
+open class BrowseActivity(spaEnvironment: SpaEnvironment) : ComponentActivity() {
+ private val sppRepository by spaEnvironment.pageProviderRepository
+
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SpaLib_DayNight)
super.onCreate(savedInstanceState)
@@ -50,13 +64,10 @@ open class BrowseActivity(
@Composable
private fun MainContent() {
- val destination =
- intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPageName()
-
val navController = rememberNavController()
CompositionLocalProvider(navController.localNavController()) {
- NavHost(navController, ROOT_PAGE_NAME) {
- composable(ROOT_PAGE_NAME) {}
+ NavHost(navController, NULL_PAGE_NAME) {
+ composable(NULL_PAGE_NAME) {}
for (page in sppRepository.getAllProviders()) {
composable(
route = page.name + page.parameter.navRoute() +
@@ -70,13 +81,23 @@ open class BrowseActivity(
}
}
}
+ }
+
+ InitialDestinationNavigator(navController)
+ }
+ @Composable
+ private fun InitialDestinationNavigator(navController: NavHostController) {
+ val destinationNavigated = rememberSaveable { mutableStateOf(false) }
+ if (destinationNavigated.value) return
+ destinationNavigated.value = true
+ LaunchedEffect(Unit) {
+ val destination =
+ intent?.getStringExtra(KEY_DESTINATION) ?: sppRepository.getDefaultStartPage()
if (destination.isNotEmpty()) {
- LaunchedEffect(Unit) {
- navController.navigate(destination) {
- popUpTo(navController.graph.findStartDestination().id) {
- inclusive = true
- }
+ navController.navigate(destination) {
+ popUpTo(navController.graph.findStartDestination().id) {
+ inclusive = true
}
}
}
@@ -84,7 +105,7 @@ open class BrowseActivity(
}
companion object {
- const val KEY_DESTINATION = "spa:SpaActivity:destination"
+ const val KEY_DESTINATION = "spaActivityDestination"
const val HIGHLIGHT_ENTRY_PARAM_NAME = "highlightEntry"
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
index bd5aaa7e1744..85fc366dd3f5 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/DebugActivity.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spa.framework
import android.content.Intent
+import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
@@ -24,6 +25,7 @@ import androidx.activity.compose.setContent
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
@@ -33,8 +35,8 @@ import androidx.navigation.navArgument
import com.android.settingslib.spa.R
import com.android.settingslib.spa.framework.BrowseActivity.Companion.KEY_DESTINATION
import com.android.settingslib.spa.framework.common.SettingsEntry
-import com.android.settingslib.spa.framework.common.SettingsEntryRepository
import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SpaEnvironment
import com.android.settingslib.spa.framework.compose.localNavController
import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.toState
@@ -52,24 +54,20 @@ private const val ROUTE_ENTRY = "entry"
private const val PARAM_NAME_PAGE_ID = "pid"
private const val PARAM_NAME_ENTRY_ID = "eid"
-open class DebugActivity(
- private val entryRepository: SettingsEntryRepository,
- private val browseActivityClass: Class<*>,
-) : ComponentActivity() {
+/**
+ * The Debug Activity to display all Spa Pages & Entries.
+ * One can open the debug activity by:
+ * $ adb shell am start -n <Activity>
+ * For gallery, Activity = com.android.settingslib.spa.gallery/.GalleryDebugActivity
+ * For SettingsGoogle, Activity = com.android.settings/.spa.SpaDebugActivity
+ */
+open class DebugActivity(private val spaEnvironment: SpaEnvironment) : ComponentActivity() {
+ private val entryRepository by spaEnvironment.entryRepository
+
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.Theme_SpaLib_DayNight)
super.onCreate(savedInstanceState)
-
- val packageName = browseActivityClass.packageName
- val className = browseActivityClass.toString().removePrefix("class $packageName")
- for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
- if (pageWithEntry.page.hasRuntimeParam()) continue
- val route = pageWithEntry.page.buildRoute()
- Log.d(
- "DEBUG ACTIVITY",
- "adb shell am start -n $packageName/$className -e $KEY_DESTINATION $route"
- )
- }
+ displayDebugMessage()
setContent {
SettingsTheme {
@@ -78,6 +76,31 @@ open class DebugActivity(
}
}
+ private fun displayDebugMessage() {
+ val entryProviderAuthorities = spaEnvironment.entryProviderAuthorities ?: return
+
+ try {
+ val query = EntryProvider.QueryEnum.PAGE_INFO_QUERY
+ contentResolver.query(
+ Uri.parse("content://$entryProviderAuthorities/${query.queryPath}"),
+ null, null, null
+ ).use { cursor ->
+ while (cursor != null && cursor.moveToNext()) {
+ val route = cursor.getString(query, EntryProvider.ColumnEnum.PAGE_ROUTE)
+ val entryCount = cursor.getInt(query, EntryProvider.ColumnEnum.PAGE_ENTRY_COUNT)
+ val hasRuntimeParam =
+ cursor.getBoolean(query, EntryProvider.ColumnEnum.HAS_RUNTIME_PARAM)
+ Log.d(
+ "DEBUG ACTIVITY", "Page Info: $route ($entryCount) " +
+ (if (hasRuntimeParam) "with" else "no") + "-runtime-params"
+ )
+ }
+ }
+ } catch (e: Exception) {
+ Log.e("DEBUG ACTIVITY", "Provider querying exception:", e)
+ }
+ }
+
@Composable
private fun MainContent() {
val navController = rememberNavController()
@@ -89,13 +112,13 @@ open class DebugActivity(
composable(
route = "$ROUTE_PAGE/{$PARAM_NAME_PAGE_ID}",
arguments = listOf(
- navArgument(PARAM_NAME_PAGE_ID) { type = NavType.IntType },
+ navArgument(PARAM_NAME_PAGE_ID) { type = NavType.StringType },
)
) { navBackStackEntry -> OnePage(navBackStackEntry.arguments) }
composable(
route = "$ROUTE_ENTRY/{$PARAM_NAME_ENTRY_ID}",
arguments = listOf(
- navArgument(PARAM_NAME_ENTRY_ID) { type = NavType.IntType },
+ navArgument(PARAM_NAME_ENTRY_ID) { type = NavType.StringType },
)
) { navBackStackEntry -> OneEntry(navBackStackEntry.arguments) }
}
@@ -104,13 +127,15 @@ open class DebugActivity(
@Composable
fun RootPage() {
+ val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() }
+ val allEntry = remember { entryRepository.getAllEntries() }
HomeScaffold(title = "Settings Debug") {
Preference(object : PreferenceModel {
- override val title = "List All Pages"
+ override val title = "List All Pages (${allPageWithEntry.size})"
override val onClick = navigator(route = ROUTE_All_PAGES)
})
Preference(object : PreferenceModel {
- override val title = "List All Entries"
+ override val title = "List All Entries (${allEntry.size})"
override val onClick = navigator(route = ROUTE_All_ENTRIES)
})
}
@@ -118,8 +143,9 @@ open class DebugActivity(
@Composable
fun AllPages() {
- RegularScaffold(title = "All Pages") {
- for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
+ val allPageWithEntry = remember { entryRepository.getAllPageWithEntry() }
+ RegularScaffold(title = "All Pages (${allPageWithEntry.size})") {
+ for (pageWithEntry in allPageWithEntry) {
Preference(object : PreferenceModel {
override val title =
"${pageWithEntry.page.displayName} (${pageWithEntry.entries.size})"
@@ -133,16 +159,18 @@ open class DebugActivity(
@Composable
fun AllEntries() {
- RegularScaffold(title = "All Entries") {
- EntryList(entryRepository.getAllEntries())
+ val allEntry = remember { entryRepository.getAllEntries() }
+ RegularScaffold(title = "All Entries (${allEntry.size})") {
+ EntryList(allEntry)
}
}
@Composable
fun OnePage(arguments: Bundle?) {
- val id = arguments!!.getInt(PARAM_NAME_PAGE_ID)
+ val id = arguments!!.getString(PARAM_NAME_PAGE_ID, "")
val pageWithEntry = entryRepository.getPageWithEntry(id)!!
- RegularScaffold(title = "Page ${pageWithEntry.page.displayName}") {
+ RegularScaffold(title = "Page - ${pageWithEntry.page.displayName}") {
+ Text(text = "id = ${pageWithEntry.page.id}")
Text(text = pageWithEntry.page.formatArguments())
Text(text = "Entry size: ${pageWithEntry.entries.size}")
Preference(model = object : PreferenceModel {
@@ -156,15 +184,16 @@ open class DebugActivity(
@Composable
fun OneEntry(arguments: Bundle?) {
- val id = arguments!!.getInt(PARAM_NAME_ENTRY_ID)
+ val id = arguments!!.getString(PARAM_NAME_ENTRY_ID, "")
val entry = entryRepository.getEntry(id)!!
- RegularScaffold(title = "Entry ${entry.displayName}") {
+ val entryContent = remember { entry.formatContent() }
+ RegularScaffold(title = "Entry - ${entry.displayTitle()}") {
Preference(model = object : PreferenceModel {
override val title = "open entry"
override val enabled = (!entry.hasRuntimeParam()).toState()
override val onClick = openEntry(entry)
})
- Text(text = entry.formatAll())
+ Text(text = entryContent)
}
}
@@ -172,7 +201,7 @@ open class DebugActivity(
private fun EntryList(entries: Collection<SettingsEntry>) {
for (entry in entries) {
Preference(object : PreferenceModel {
- override val title = entry.displayName
+ override val title = entry.displayTitle()
override val summary =
"${entry.fromPage?.displayName} -> ${entry.toPage?.displayName}".toState()
override val onClick = navigator(route = ROUTE_ENTRY + "/${entry.id}")
@@ -183,9 +212,9 @@ open class DebugActivity(
@Composable
private fun openPage(page: SettingsPage): (() -> Unit)? {
if (page.hasRuntimeParam()) return null
- val route = page.buildRoute()
val context = LocalContext.current
- val intent = Intent(context, browseActivityClass).apply {
+ val route = page.buildRoute()
+ val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
putExtra(KEY_DESTINATION, route)
}
return {
@@ -197,9 +226,9 @@ open class DebugActivity(
@Composable
private fun openEntry(entry: SettingsEntry): (() -> Unit)? {
if (entry.hasRuntimeParam()) return null
- val route = entry.buildRoute()
val context = LocalContext.current
- val intent = Intent(context, browseActivityClass).apply {
+ val route = entry.buildRoute()
+ val intent = Intent(context, spaEnvironment.browseActivityClass).apply {
putExtra(KEY_DESTINATION, route)
}
return {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
new file mode 100644
index 000000000000..f0ec83b42498
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/EntryProvider.kt
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.framework
+
+import android.content.ComponentName
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.content.Context
+import android.content.Intent
+import android.content.Intent.URI_INTENT_SCHEME
+import android.content.UriMatcher
+import android.content.pm.ProviderInfo
+import android.database.Cursor
+import android.database.MatrixCursor
+import android.net.Uri
+import android.util.Log
+import com.android.settingslib.spa.framework.common.SettingsPage
+import com.android.settingslib.spa.framework.common.SpaEnvironment
+
+/**
+ * The content provider to return entry related data, which can be used for search and hierarchy.
+ * One can query the provider result by:
+ * $ adb shell content query --uri content://<AuthorityPath>/<QueryPath>
+ * For gallery, AuthorityPath = com.android.spa.gallery.provider
+ * For SettingsGoogle, AuthorityPath = com.android.settings.spa.provider
+ * Some examples:
+ * $ adb shell content query --uri content://<AuthorityPath>/page_debug
+ * $ adb shell content query --uri content://<AuthorityPath>/page_info
+ * $ adb shell content query --uri content://<AuthorityPath>/entry_info
+ */
+open class EntryProvider(spaEnvironment: SpaEnvironment) : ContentProvider() {
+ private val entryRepository by spaEnvironment.entryRepository
+ private val browseActivityClass = spaEnvironment.browseActivityClass
+
+ /**
+ * Enum to define all column names in provider.
+ */
+ enum class ColumnEnum(val id: String) {
+ // Columns related to page
+ PAGE_ID("pageId"),
+ PAGE_NAME("pageName"),
+ PAGE_ROUTE("pageRoute"),
+ PAGE_INTENT_URI("pageIntent"),
+ PAGE_ENTRY_COUNT("entryCount"),
+ HAS_RUNTIME_PARAM("hasRuntimeParam"),
+ PAGE_START_ADB("pageStartAdb"),
+
+ // Columns related to entry
+ ENTRY_ID("entryId"),
+ ENTRY_NAME("entryName"),
+ ENTRY_ROUTE("entryRoute"),
+ ENTRY_TITLE("entryTitle"),
+ ENTRY_SEARCH_KEYWORD("entrySearchKw"),
+ }
+
+ /**
+ * Enum to define all queries supported in the provider.
+ */
+ enum class QueryEnum(
+ val queryPath: String,
+ val queryMatchCode: Int,
+ val columnNames: List<ColumnEnum>
+ ) {
+ // For debug
+ PAGE_DEBUG_QUERY(
+ "page_debug", 1,
+ listOf(ColumnEnum.PAGE_START_ADB)
+ ),
+
+ // page related queries.
+ PAGE_INFO_QUERY(
+ "page_info", 100,
+ listOf(
+ ColumnEnum.PAGE_ID,
+ ColumnEnum.PAGE_NAME,
+ ColumnEnum.PAGE_ROUTE,
+ ColumnEnum.PAGE_INTENT_URI,
+ ColumnEnum.PAGE_ENTRY_COUNT,
+ ColumnEnum.HAS_RUNTIME_PARAM,
+ )
+ ),
+
+ // entry related queries
+ ENTRY_INFO_QUERY(
+ "entry_info", 200,
+ listOf(
+ ColumnEnum.ENTRY_ID,
+ ColumnEnum.ENTRY_NAME,
+ ColumnEnum.ENTRY_ROUTE,
+ ColumnEnum.ENTRY_TITLE,
+ ColumnEnum.ENTRY_SEARCH_KEYWORD,
+ )
+ )
+ }
+
+ private val uriMatcher = UriMatcher(UriMatcher.NO_MATCH)
+ private fun addUri(authority: String, query: QueryEnum) {
+ uriMatcher.addURI(authority, query.queryPath, query.queryMatchCode)
+ }
+
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
+ TODO("Implement this to handle requests to delete one or more rows")
+ }
+
+ override fun getType(uri: Uri): String? {
+ TODO(
+ "Implement this to handle requests for the MIME type of the data" +
+ "at the given URI"
+ )
+ }
+
+ override fun insert(uri: Uri, values: ContentValues?): Uri? {
+ TODO("Implement this to handle requests to insert a new row.")
+ }
+
+ override fun update(
+ uri: Uri,
+ values: ContentValues?,
+ selection: String?,
+ selectionArgs: Array<String>?
+ ): Int {
+ TODO("Implement this to handle requests to update one or more rows.")
+ }
+
+ override fun onCreate(): Boolean {
+ return true
+ }
+
+ override fun attachInfo(context: Context?, info: ProviderInfo?) {
+ if (info != null) {
+ addUri(info.authority, QueryEnum.PAGE_DEBUG_QUERY)
+ addUri(info.authority, QueryEnum.PAGE_INFO_QUERY)
+ addUri(info.authority, QueryEnum.ENTRY_INFO_QUERY)
+ }
+ super.attachInfo(context, info)
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array<String>?,
+ selection: String?,
+ selectionArgs: Array<String>?,
+ sortOrder: String?
+ ): Cursor? {
+ return try {
+ when (uriMatcher.match(uri)) {
+ QueryEnum.PAGE_DEBUG_QUERY.queryMatchCode -> queryPageDebug()
+ QueryEnum.PAGE_INFO_QUERY.queryMatchCode -> queryPageInfo()
+ QueryEnum.ENTRY_INFO_QUERY.queryMatchCode -> queryEntryInfo()
+ else -> throw UnsupportedOperationException("Unknown Uri $uri")
+ }
+ } catch (e: UnsupportedOperationException) {
+ throw e
+ } catch (e: Exception) {
+ Log.e("EntryProvider", "Provider querying exception:", e)
+ null
+ }
+ }
+
+ private fun queryPageDebug(): Cursor {
+ val cursor = MatrixCursor(QueryEnum.PAGE_DEBUG_QUERY.getColumns())
+ for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
+ val command = createBrowsePageAdbCommand(pageWithEntry.page)
+ if (command != null) {
+ cursor.newRow().add(ColumnEnum.PAGE_START_ADB.id, command)
+ }
+ }
+ return cursor
+ }
+
+ private fun queryPageInfo(): Cursor {
+ val cursor = MatrixCursor(QueryEnum.PAGE_INFO_QUERY.getColumns())
+ for (pageWithEntry in entryRepository.getAllPageWithEntry()) {
+ val page = pageWithEntry.page
+ cursor.newRow()
+ .add(ColumnEnum.PAGE_ID.id, page.id)
+ .add(ColumnEnum.PAGE_NAME.id, page.displayName)
+ .add(ColumnEnum.PAGE_ROUTE.id, page.buildRoute())
+ .add(ColumnEnum.PAGE_ENTRY_COUNT.id, pageWithEntry.entries.size)
+ .add(ColumnEnum.HAS_RUNTIME_PARAM.id, if (page.hasRuntimeParam()) 1 else 0)
+ .add(
+ ColumnEnum.PAGE_INTENT_URI.id,
+ createBrowsePageIntent(page).toUri(URI_INTENT_SCHEME)
+ )
+ }
+ return cursor
+ }
+
+ private fun queryEntryInfo(): Cursor {
+ val cursor = MatrixCursor(QueryEnum.ENTRY_INFO_QUERY.getColumns())
+ for (entry in entryRepository.getAllEntries()) {
+ // We can add runtime arguments if necessary
+ val searchData = entry.getSearchData()
+ cursor.newRow()
+ .add(ColumnEnum.ENTRY_ID.id, entry.id)
+ .add(ColumnEnum.ENTRY_NAME.id, entry.displayName)
+ .add(ColumnEnum.ENTRY_ROUTE.id, entry.buildRoute())
+ .add(ColumnEnum.ENTRY_TITLE.id, searchData?.title ?: "")
+ .add(
+ ColumnEnum.ENTRY_SEARCH_KEYWORD.id,
+ searchData?.keyword ?: emptyList<String>()
+ )
+ }
+ return cursor
+ }
+
+ private fun createBrowsePageIntent(page: SettingsPage): Intent {
+ if (context == null || page.hasRuntimeParam())
+ return Intent()
+
+ return Intent().setComponent(ComponentName(context!!, browseActivityClass)).apply {
+ putExtra(BrowseActivity.KEY_DESTINATION, page.buildRoute())
+ }
+ }
+
+ private fun createBrowsePageAdbCommand(page: SettingsPage): String? {
+ if (context == null || page.hasRuntimeParam()) return null
+ val packageName = context!!.packageName
+ val activityName = browseActivityClass.name.replace(packageName, "")
+ return "adb shell am start -n $packageName/$activityName" +
+ " -e ${BrowseActivity.KEY_DESTINATION} ${page.buildRoute()}"
+ }
+}
+
+fun EntryProvider.QueryEnum.getColumns(): Array<String> {
+ return columnNames.map { it.id }.toTypedArray()
+}
+
+fun EntryProvider.QueryEnum.getIndex(name: EntryProvider.ColumnEnum): Int {
+ return columnNames.indexOf(name)
+}
+
+fun Cursor.getString(query: EntryProvider.QueryEnum, columnName: EntryProvider.ColumnEnum): String {
+ return this.getString(query.getIndex(columnName))
+}
+
+fun Cursor.getInt(query: EntryProvider.QueryEnum, columnName: EntryProvider.ColumnEnum): Int {
+ return this.getInt(query.getIndex(columnName))
+}
+
+fun Cursor.getBoolean(
+ query: EntryProvider.QueryEnum,
+ columnName: EntryProvider.ColumnEnum
+): Boolean {
+ return this.getInt(query.getIndex(columnName)) == 1
+}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ExampleFeatureScreen.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntryMacro.kt
index 6e1721490f98..9ec0c01b4b38 100644
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ExampleFeatureScreen.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntryMacro.kt
@@ -14,15 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.compose.gallery
+package com.android.settingslib.spa.framework.common
-import androidx.compose.foundation.layout.Column
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import com.android.systemui.ExampleFeature
-/** The screen that shows ExampleFeature. */
-@Composable
-fun ExampleFeatureScreen(modifier: Modifier = Modifier) {
- Column(modifier) { ExampleFeature("This is an example feature!") }
+/**
+ * Defines interface of a entry macro, which contains entry functions to support different
+ * scenarios, such as browsing (UiLayout), search, etc.
+ */
+interface EntryMacro {
+ @Composable
+ fun UiLayout() {}
+ fun getSearchData(): EntrySearchData? = null
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntrySearchData.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntrySearchData.kt
new file mode 100644
index 000000000000..9b262afc9c53
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/EntrySearchData.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.framework.common
+
+/**
+ * Defines Search data of one Settings entry.
+ */
+data class EntrySearchData(
+ val title: String = "",
+ val keyword: List<String> = emptyList(),
+) {
+ fun format(): String {
+ val content = listOf(
+ "search_title = $title",
+ "search_keyword = $keyword",
+ )
+ return content.joinToString("\n")
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt
index ee12f02e5a7a..edcca18017c1 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/PageModel.kt
@@ -22,7 +22,7 @@ import androidx.lifecycle.ViewModel
open class PageModel : ViewModel() {
var initialized = false
- fun initOnce(arguments: Bundle?) {
+ fun initOnce(arguments: Bundle? = null) {
// Initialize only once
if (initialized) return
initialized = true
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
index b0a1cbe8c224..81d0bffca643 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntry.kt
@@ -17,97 +17,33 @@
package com.android.settingslib.spa.framework.common
import android.os.Bundle
+import android.widget.Toast
import androidx.compose.runtime.Composable
-import androidx.navigation.NamedNavArgument
-import androidx.navigation.NavType
-import com.android.settingslib.spa.framework.BrowseActivity
-import com.android.settingslib.spa.framework.util.navLink
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.ui.platform.LocalContext
+import com.android.settingslib.spa.framework.BrowseActivity.Companion.HIGHLIGHT_ENTRY_PARAM_NAME
const val INJECT_ENTRY_NAME = "INJECT"
const val ROOT_ENTRY_NAME = "ROOT"
-const val ROOT_PAGE_NAME = "Root"
-
-/**
- * Defines data of one Settings entry for Settings search.
- */
-data class SearchData(val keyword: String = "")
-
-/**
- * Defines data of one Settings entry for UI rendering.
- */
-data class UiData(val title: String = "")
-
-/**
- * Defines data to identify a Settings page.
- */
-data class SettingsPage(
- // The unique id of this page, which is computed by name + normalized(arguments)
- val id: Int,
-
- // The name of the page, which is used to compute the unique id, and need to be stable.
- val name: String,
-
- // The display name of the page, for better readability.
- // By default, it is the same as name.
- val displayName: String,
-
- // Defined parameters of this page.
- val parameter: List<NamedNavArgument> = emptyList(),
-
- // The arguments of this page.
- val arguments: Bundle? = null,
-) {
- companion object {
- fun create(
- name: String,
- parameter: List<NamedNavArgument> = emptyList(),
- arguments: Bundle? = null
- ): SettingsPage {
- return SettingsPageBuilder(name, parameter).setArguments(arguments).build()
- }
- }
-
- fun formatArguments(): String {
- val normalizedArguments = parameter.normalize(arguments)
- if (normalizedArguments == null || normalizedArguments.isEmpty) return "[No arguments]"
- return normalizedArguments.toString().removeRange(0, 6)
- }
-
- fun formatAll(): String {
- return "$displayName ${formatArguments()}"
- }
-
- fun buildRoute(highlightEntryName: String? = null): String {
- val highlightParam =
- if (highlightEntryName == null)
- ""
- else
- "?${BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME}=$highlightEntryName"
- return name + parameter.navLink(arguments) + highlightParam
- }
-
- fun hasRuntimeParam(): Boolean {
- return parameter.hasRuntimeParam(arguments)
- }
-}
/**
* Defines data of a Settings entry.
*/
data class SettingsEntry(
// The unique id of this entry, which is computed by name + owner + fromPage + toPage.
- val id: Int,
+ val id: String,
// The name of the page, which is used to compute the unique id, and need to be stable.
- val name: String,
+ private val name: String,
+
+ // The display name of the page, for better readability.
+ val displayName: String,
// The owner page of this entry.
val owner: SettingsPage,
- // The display name of the entry, for better readability.
- // By default, it is $owner:$name
- val displayName: String,
-
// Defines linking of Settings entries
val fromPage: SettingsPage? = null,
val toPage: SettingsPage? = null,
@@ -117,7 +53,7 @@ data class SettingsEntry(
* Defines entry attributes here.
* ========================================
*/
- val isAllowSearch: Boolean,
+ val isAllowSearch: Boolean = false,
/**
* ========================================
@@ -129,13 +65,7 @@ data class SettingsEntry(
* API to get Search related data for this entry.
* Returns null if this entry is not available for the search at the moment.
*/
- val searchData: () -> SearchData? = { null },
-
- /**
- * API to get UI related data for this entry.
- * Returns null if the entry is not render-able.
- */
- val uiData: () -> UiData? = { null },
+ private val searchDataImpl: (arguments: Bundle?) -> EntrySearchData? = { null },
/**
* API to Render UI of this entry directly. For now, we use it in the internal injection, to
@@ -143,65 +73,61 @@ data class SettingsEntry(
* injected entry. In the long term, we may deprecate the @Composable Page() API in SPP, and
* use each entries' UI rendering function in the page instead.
*/
- val uiLayoutImpl: (@Composable (arguments: Bundle?) -> Unit) = {},
+ private val uiLayoutImpl: (@Composable (arguments: Bundle?) -> Unit) = {},
) {
- fun formatAll(): String {
- val content = listOf<String>(
- "owner = ${owner.formatAll()}",
- "linkFrom = ${fromPage?.formatAll()}",
- "linkTo = ${toPage?.formatAll()}",
+ fun formatContent(): String {
+ val content = listOf(
+ "id = $id",
+ "owner = ${owner.formatDisplayTitle()}",
+ "linkFrom = ${fromPage?.formatDisplayTitle()}",
+ "linkTo = ${toPage?.formatDisplayTitle()}",
+ "${getSearchData()?.format()}",
)
return content.joinToString("\n")
}
- private fun getDisplayPage(): SettingsPage {
- // Display the entry on its from-page, or on its owner page if the from-page is unset.
+ fun displayTitle(): String {
+ return "${owner.displayName}:$displayName"
+ }
+
+ private fun containerPage(): SettingsPage {
+ // The Container page of the entry, which is the from-page or
+ // the owner-page if from-page is unset.
return fromPage ?: owner
}
fun buildRoute(): String {
- return getDisplayPage().buildRoute(name)
+ return containerPage().buildRoute(id)
}
fun hasRuntimeParam(): Boolean {
- return getDisplayPage().hasRuntimeParam()
+ return containerPage().hasRuntimeParam()
}
- @Composable
- fun UiLayout(runtimeArguments: Bundle? = null) {
+ private fun fullArgument(runtimeArguments: Bundle? = null): Bundle {
val arguments = Bundle()
if (owner.arguments != null) arguments.putAll(owner.arguments)
+ // Put runtime args later, which can override page args.
if (runtimeArguments != null) arguments.putAll(runtimeArguments)
- uiLayoutImpl(arguments)
+ return arguments
}
-}
-
-data class SettingsPageWithEntry(
- val page: SettingsPage,
- val entries: List<SettingsEntry>,
-)
-class SettingsPageBuilder(
- private val name: String,
- private val parameter: List<NamedNavArgument> = emptyList()
-) {
- private var displayName: String? = null
- private var arguments: Bundle? = null
-
- fun build(): SettingsPage {
- val normArguments = parameter.normalize(arguments)
- return SettingsPage(
- id = "$name:${normArguments?.toString()}".toUniqueId(),
- name = name,
- displayName = displayName ?: name,
- parameter = parameter,
- arguments = arguments,
- )
+ fun getSearchData(runtimeArguments: Bundle? = null): EntrySearchData? {
+ return searchDataImpl(fullArgument(runtimeArguments))
}
- fun setArguments(arguments: Bundle?): SettingsPageBuilder {
- this.arguments = arguments
- return this
+ @Composable
+ fun UiLayout(runtimeArguments: Bundle? = null) {
+ val context = LocalContext.current
+ val highlight = rememberSaveable {
+ mutableStateOf(runtimeArguments?.getString(HIGHLIGHT_ENTRY_PARAM_NAME) == id)
+ }
+ if (highlight.value) {
+ highlight.value = false
+ // TODO: Add highlight entry logic
+ Toast.makeText(context, "entry $id highlighted", Toast.LENGTH_SHORT).show()
+ }
+ uiLayoutImpl(fullArgument(runtimeArguments))
}
}
@@ -209,34 +135,42 @@ class SettingsPageBuilder(
* The helper to build a Settings Entry instance.
*/
class SettingsEntryBuilder(private val name: String, private val owner: SettingsPage) {
- private var displayName: String? = null
+ private var displayName = name
private var fromPage: SettingsPage? = null
private var toPage: SettingsPage? = null
- private var isAllowSearch: Boolean? = null
- private var searchDataFn: () -> SearchData? = { null }
- private var uiLayoutFn: (@Composable (arguments: Bundle?) -> Unit) = {}
+ // Attributes
+ private var isAllowSearch: Boolean = false
+
+ // Functions
+ private var searchDataFn: (arguments: Bundle?) -> EntrySearchData? = { null }
+ private var uiLayoutFn: (@Composable (arguments: Bundle?) -> Unit) = { }
fun build(): SettingsEntry {
return SettingsEntry(
- id = "$name:${owner.id}(${fromPage?.id}-${toPage?.id})".toUniqueId(),
- displayName = displayName ?: "${owner.displayName}:$name",
+ id = id(),
name = name,
owner = owner,
+ displayName = displayName,
// linking data
fromPage = fromPage,
toPage = toPage,
// attributes
- isAllowSearch = getIsSearchable(),
+ isAllowSearch = isAllowSearch,
// functions
- searchData = searchDataFn,
+ searchDataImpl = searchDataFn,
uiLayoutImpl = uiLayoutFn,
)
}
+ fun setDisplayName(displayName: String): SettingsEntryBuilder {
+ this.displayName = displayName
+ return this
+ }
+
fun setLink(
fromPage: SettingsPage? = null,
toPage: SettingsPage? = null
@@ -251,7 +185,16 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
return this
}
- fun setSearchDataFn(fn: () -> SearchData?): SettingsEntryBuilder {
+ fun setMacro(fn: (arguments: Bundle?) -> EntryMacro): SettingsEntryBuilder {
+ setSearchDataFn { fn(it).getSearchData() }
+ setUiLayoutFn {
+ val macro = remember { fn(it) }
+ macro.UiLayout()
+ }
+ return this
+ }
+
+ fun setSearchDataFn(fn: (arguments: Bundle?) -> EntrySearchData?): SettingsEntryBuilder {
this.searchDataFn = fn
return this
}
@@ -261,7 +204,10 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
return this
}
- private fun getIsSearchable(): Boolean = isAllowSearch ?: false
+ // The unique id of this entry, which is computed by name + owner + fromPage + toPage.
+ private fun id(): String {
+ return "$name:${owner.id}(${fromPage?.id}-${toPage?.id})".toHashId()
+ }
companion object {
fun create(entryName: String, owner: SettingsPage): SettingsEntryBuilder {
@@ -276,48 +222,19 @@ class SettingsEntryBuilder(private val name: String, private val owner: Settings
return create(entryName, owner).setLink(toPage = owner)
}
- fun createInject(owner: SettingsPage, entryName: String? = null): SettingsEntryBuilder {
- val name = entryName ?: "${INJECT_ENTRY_NAME}_${owner.name}"
- return createLinkTo(name, owner)
+ fun create(owner: SettingsPage, entryName: String, displayName: String? = null):
+ SettingsEntryBuilder {
+ return SettingsEntryBuilder(entryName, owner).setDisplayName(displayName ?: entryName)
}
- fun createRoot(owner: SettingsPage, entryName: String? = null): SettingsEntryBuilder {
- val name = entryName ?: "${ROOT_ENTRY_NAME}_${owner.name}"
- return createLinkTo(name, owner)
+ fun createInject(owner: SettingsPage, displayName: String? = null): SettingsEntryBuilder {
+ val name = displayName ?: "${INJECT_ENTRY_NAME}_${owner.displayName}"
+ return createLinkTo(INJECT_ENTRY_NAME, owner).setDisplayName(name)
}
- }
-}
-private fun String.toUniqueId(): Int {
- return this.hashCode()
-}
-
-private fun List<NamedNavArgument>.normalize(arguments: Bundle? = null): Bundle? {
- if (this.isEmpty()) return null
- val normArgs = Bundle()
- for (navArg in this) {
- when (navArg.argument.type) {
- NavType.StringType -> {
- val value = arguments?.getString(navArg.name)
- if (value != null)
- normArgs.putString(navArg.name, value)
- else
- normArgs.putString("unset_" + navArg.name, null)
- }
- NavType.IntType -> {
- if (arguments != null && arguments.containsKey(navArg.name))
- normArgs.putInt(navArg.name, arguments.getInt(navArg.name))
- else
- normArgs.putString("unset_" + navArg.name, null)
- }
+ fun createRoot(owner: SettingsPage, displayName: String? = null): SettingsEntryBuilder {
+ val name = displayName ?: "${ROOT_ENTRY_NAME}_${owner.displayName}"
+ return createLinkTo(ROOT_ENTRY_NAME, owner).setDisplayName(name)
}
}
- return normArgs
-}
-
-private fun List<NamedNavArgument>.hasRuntimeParam(arguments: Bundle? = null): Boolean {
- for (navArg in this) {
- if (arguments == null || !arguments.containsKey(navArg.name)) return true
- }
- return false
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
index a4e5a58246ff..b6f6203209b1 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsEntryRepository.kt
@@ -21,15 +21,20 @@ import java.util.LinkedList
private const val MAX_ENTRY_SIZE = 5000
+data class SettingsPageWithEntry(
+ val page: SettingsPage,
+ val entries: List<SettingsEntry>,
+)
+
/**
* The repository to maintain all Settings entries
*/
class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
// Map of entry unique Id to entry
- private val entryMap: Map<Int, SettingsEntry>
+ private val entryMap: Map<String, SettingsEntry>
// Map of Settings page to its contained entries.
- private val pageWithEntryMap: Map<Int, SettingsPageWithEntry>
+ private val pageWithEntryMap: Map<String, SettingsPageWithEntry>
init {
logMsg("Initialize")
@@ -67,7 +72,7 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
return pageWithEntryMap.values
}
- fun getPageWithEntry(pageId: Int): SettingsPageWithEntry? {
+ fun getPageWithEntry(pageId: String): SettingsPageWithEntry? {
return pageWithEntryMap[pageId]
}
@@ -75,7 +80,7 @@ class SettingsEntryRepository(sppRepository: SettingsPageProviderRepository) {
return entryMap.values
}
- fun getEntry(entryId: Int): SettingsEntry? {
+ fun getEntry(entryId: String): SettingsEntry? {
return entryMap[entryId]
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
new file mode 100644
index 000000000000..0c301b937cce
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPage.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.framework.common
+
+import android.os.Bundle
+import androidx.navigation.NamedNavArgument
+import androidx.navigation.NavType
+import com.android.settingslib.spa.framework.BrowseActivity
+import com.android.settingslib.spa.framework.util.navLink
+
+/**
+ * Defines data to identify a Settings page.
+ */
+data class SettingsPage(
+ // The unique id of this page, which is computed by name + normalized(arguments)
+ val id: String,
+
+ // The name of the page, which is used to compute the unique id, and need to be stable.
+ val name: String,
+
+ // The display name of the page, for better readability.
+ val displayName: String,
+
+ // Defined parameters of this page.
+ val parameter: List<NamedNavArgument> = emptyList(),
+
+ // The arguments of this page.
+ val arguments: Bundle? = null,
+) {
+ companion object {
+ fun create(
+ name: String,
+ displayName: String? = null,
+ parameter: List<NamedNavArgument> = emptyList(),
+ arguments: Bundle? = null
+ ): SettingsPage {
+ return SettingsPage(
+ id = id(name, parameter, arguments),
+ name = name,
+ displayName = displayName ?: name,
+ parameter = parameter,
+ arguments = arguments
+ )
+ }
+
+ // The unique id of this page, which is computed by name + normalized(arguments)
+ private fun id(
+ name: String,
+ parameter: List<NamedNavArgument> = emptyList(),
+ arguments: Bundle? = null
+ ): String {
+ val normArguments = parameter.normalize(arguments)
+ return "$name:${normArguments?.toString()}".toHashId()
+ }
+ }
+
+ // Returns if this Settings Page is created by the given Spp.
+ fun isCreateBy(SppName: String): Boolean {
+ return name == SppName
+ }
+
+ fun formatArguments(): String {
+ val normalizedArguments = parameter.normalize(arguments)
+ if (normalizedArguments == null || normalizedArguments.isEmpty) return "[No arguments]"
+ return normalizedArguments.toString().removeRange(0, 6)
+ }
+
+ fun formatDisplayTitle(): String {
+ return "$displayName ${formatArguments()}"
+ }
+
+ fun buildRoute(highlightEntryId: String? = null): String {
+ val highlightParam =
+ if (highlightEntryId == null)
+ ""
+ else
+ "?${BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME}=$highlightEntryId"
+ return name + parameter.navLink(arguments) + highlightParam
+ }
+
+ fun hasRuntimeParam(): Boolean {
+ return parameter.hasRuntimeParam(arguments)
+ }
+}
+
+private fun List<NamedNavArgument>.normalize(arguments: Bundle? = null): Bundle? {
+ if (this.isEmpty()) return null
+ val normArgs = Bundle()
+ for (navArg in this) {
+ when (navArg.argument.type) {
+ NavType.StringType -> {
+ val value = arguments?.getString(navArg.name)
+ if (value != null)
+ normArgs.putString(navArg.name, value)
+ else
+ normArgs.putString("unset_" + navArg.name, null)
+ }
+ NavType.IntType -> {
+ if (arguments != null && arguments.containsKey(navArg.name))
+ normArgs.putInt(navArg.name, arguments.getInt(navArg.name))
+ else
+ normArgs.putString("unset_" + navArg.name, null)
+ }
+ }
+ }
+ return normArgs
+}
+
+private fun List<NamedNavArgument>.hasRuntimeParam(arguments: Bundle? = null): Boolean {
+ for (navArg in this) {
+ if (arguments == null || !arguments.containsKey(navArg.name)) return true
+ }
+ return false
+}
+
+fun String.toHashId(): String {
+ return this.hashCode().toUInt().toString(36)
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt
index 6adda6b2aa01..77a157fe815b 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SettingsPageProviderRepository.kt
@@ -30,12 +30,8 @@ class SettingsPageProviderRepository(
logMsg("Initialize Completed: ${pageProviderMap.size} spp")
}
- fun getDefaultStartPageName(): String {
- return if (rootPages.isNotEmpty()) {
- rootPages[0].name
- } else {
- ""
- }
+ fun getDefaultStartPage(): String {
+ return if (rootPages.isEmpty()) "" else rootPages[0].buildRoute()
}
fun getAllRootPages(): Collection<SettingsPage> {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
new file mode 100644
index 000000000000..111555b468c1
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/common/SpaEnvironment.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.framework.common
+
+import android.app.Activity
+
+abstract class SpaEnvironment {
+ abstract val pageProviderRepository: Lazy<SettingsPageProviderRepository>
+
+ val entryRepository = lazy { SettingsEntryRepository(pageProviderRepository.value) }
+
+ abstract val browseActivityClass: Class<out Activity>
+
+ open val entryProviderAuthorities: String? = null
+
+ // TODO: add other environment setup here.
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
index 32ef0bb3d19b..13a2cc9d252c 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/compose/NavControllerWrapper.kt
@@ -49,7 +49,8 @@ val LocalNavController = compositionLocalOf<NavControllerWrapper> {
}
@Composable
-fun navigator(route: String): () -> Unit {
+fun navigator(route: String?): () -> Unit {
+ if (route == null) return {}
val navController = LocalNavController.current
return { navController.navigate(route) }
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/MaterialColors.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/MaterialColors.kt
index 4626f0bbed49..3fa8c658e198 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/MaterialColors.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/MaterialColors.kt
@@ -16,11 +16,15 @@
package com.android.settingslib.spa.framework.theme
+import android.os.Build
import androidx.compose.material3.ColorScheme
+import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
+import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
+import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
@Composable
@@ -28,8 +32,15 @@ internal fun materialColorScheme(isDarkTheme: Boolean): ColorScheme {
val context = LocalContext.current
return remember(isDarkTheme) {
when {
- isDarkTheme -> dynamicDarkColorScheme(context)
- else -> dynamicLightColorScheme(context)
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ if (isDarkTheme) dynamicDarkColorScheme(context)
+ else dynamicLightColorScheme(context)
+ }
+ isDarkTheme -> darkColorScheme()
+ else -> lightColorScheme()
}
}
}
+
+val ColorScheme.divider: Color
+ get() = onSurface.copy(SettingsOpacity.Divider)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt
index bc316f71cf23..fa17e08e9885 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsColors.kt
@@ -17,6 +17,7 @@
package com.android.settingslib.spa.framework.theme
import android.content.Context
+import android.os.Build
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
@@ -44,8 +45,12 @@ internal fun settingsColorScheme(isDarkTheme: Boolean): SettingsColorScheme {
val context = LocalContext.current
return remember(isDarkTheme) {
when {
- isDarkTheme -> dynamicDarkColorScheme(context)
- else -> dynamicLightColorScheme(context)
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ if (isDarkTheme) dynamicDarkColorScheme(context)
+ else dynamicLightColorScheme(context)
+ }
+ isDarkTheme -> darkColorScheme()
+ else -> lightColorScheme()
}
}
}
@@ -101,3 +106,37 @@ private fun dynamicDarkColorScheme(context: Context): SettingsColorScheme {
onSpinnerItemContainer = tonalPalette.neutralVariant30,
)
}
+
+private fun darkColorScheme(): SettingsColorScheme {
+ val tonalPalette = tonalPalette()
+ return SettingsColorScheme(
+ background = tonalPalette.neutral10,
+ categoryTitle = tonalPalette.primary90,
+ surface = tonalPalette.neutral20,
+ surfaceHeader = tonalPalette.neutral30,
+ secondaryText = tonalPalette.neutralVariant80,
+ primaryContainer = tonalPalette.secondary90,
+ onPrimaryContainer = tonalPalette.neutral10,
+ spinnerHeaderContainer = tonalPalette.primary90,
+ onSpinnerHeaderContainer = tonalPalette.neutral10,
+ spinnerItemContainer = tonalPalette.secondary90,
+ onSpinnerItemContainer = tonalPalette.neutralVariant30,
+ )
+}
+
+private fun lightColorScheme(): SettingsColorScheme {
+ val tonalPalette = tonalPalette()
+ return SettingsColorScheme(
+ background = tonalPalette.neutral95,
+ categoryTitle = tonalPalette.primary40,
+ surface = tonalPalette.neutral99,
+ surfaceHeader = tonalPalette.neutral90,
+ secondaryText = tonalPalette.neutralVariant30,
+ primaryContainer = tonalPalette.primary90,
+ onPrimaryContainer = tonalPalette.neutral10,
+ spinnerHeaderContainer = tonalPalette.primary90,
+ onSpinnerHeaderContainer = tonalPalette.neutral10,
+ spinnerItemContainer = tonalPalette.secondary90,
+ onSpinnerItemContainer = tonalPalette.neutralVariant30,
+ )
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt
index f81f5e734fb4..c205aae84fd0 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsTonalPalette.kt
@@ -118,6 +118,84 @@ internal class SettingsTonalPalette(
val tertiary0: Color,
)
+/** Static colors in Material. */
+internal fun tonalPalette() = SettingsTonalPalette(
+ // The neutral static tonal range.
+ neutral100 = Color(red = 255, green = 255, blue = 255),
+ neutral99 = Color(red = 255, green = 251, blue = 254),
+ neutral95 = Color(red = 244, green = 239, blue = 244),
+ neutral90 = Color(red = 230, green = 225, blue = 229),
+ neutral80 = Color(red = 201, green = 197, blue = 202),
+ neutral70 = Color(red = 174, green = 170, blue = 174),
+ neutral60 = Color(red = 147, green = 144, blue = 148),
+ neutral50 = Color(red = 120, green = 117, blue = 121),
+ neutral40 = Color(red = 96, green = 93, blue = 98),
+ neutral30 = Color(red = 72, green = 70, blue = 73),
+ neutral20 = Color(red = 49, green = 48, blue = 51),
+ neutral10 = Color(red = 28, green = 27, blue = 31),
+ neutral0 = Color(red = 0, green = 0, blue = 0),
+
+ // The neutral variant static tonal range, sometimes called "neutral 2".
+ neutralVariant100 = Color(red = 255, green = 255, blue = 255),
+ neutralVariant99 = Color(red = 255, green = 251, blue = 254),
+ neutralVariant95 = Color(red = 245, green = 238, blue = 250),
+ neutralVariant90 = Color(red = 231, green = 224, blue = 236),
+ neutralVariant80 = Color(red = 202, green = 196, blue = 208),
+ neutralVariant70 = Color(red = 174, green = 169, blue = 180),
+ neutralVariant60 = Color(red = 147, green = 143, blue = 153),
+ neutralVariant50 = Color(red = 121, green = 116, blue = 126),
+ neutralVariant40 = Color(red = 96, green = 93, blue = 102),
+ neutralVariant30 = Color(red = 73, green = 69, blue = 79),
+ neutralVariant20 = Color(red = 50, green = 47, blue = 55),
+ neutralVariant10 = Color(red = 29, green = 26, blue = 34),
+ neutralVariant0 = Color(red = 0, green = 0, blue = 0),
+
+ // The primary static tonal range.
+ primary100 = Color(red = 255, green = 255, blue = 255),
+ primary99 = Color(red = 255, green = 251, blue = 254),
+ primary95 = Color(red = 246, green = 237, blue = 255),
+ primary90 = Color(red = 234, green = 221, blue = 255),
+ primary80 = Color(red = 208, green = 188, blue = 255),
+ primary70 = Color(red = 182, green = 157, blue = 248),
+ primary60 = Color(red = 154, green = 130, blue = 219),
+ primary50 = Color(red = 127, green = 103, blue = 190),
+ primary40 = Color(red = 103, green = 80, blue = 164),
+ primary30 = Color(red = 79, green = 55, blue = 139),
+ primary20 = Color(red = 56, green = 30, blue = 114),
+ primary10 = Color(red = 33, green = 0, blue = 93),
+ primary0 = Color(red = 33, green = 0, blue = 93),
+
+ // The secondary static tonal range.
+ secondary100 = Color(red = 255, green = 255, blue = 255),
+ secondary99 = Color(red = 255, green = 251, blue = 254),
+ secondary95 = Color(red = 246, green = 237, blue = 255),
+ secondary90 = Color(red = 232, green = 222, blue = 248),
+ secondary80 = Color(red = 204, green = 194, blue = 220),
+ secondary70 = Color(red = 176, green = 167, blue = 192),
+ secondary60 = Color(red = 149, green = 141, blue = 165),
+ secondary50 = Color(red = 122, green = 114, blue = 137),
+ secondary40 = Color(red = 98, green = 91, blue = 113),
+ secondary30 = Color(red = 74, green = 68, blue = 88),
+ secondary20 = Color(red = 51, green = 45, blue = 65),
+ secondary10 = Color(red = 29, green = 25, blue = 43),
+ secondary0 = Color(red = 0, green = 0, blue = 0),
+
+ // The tertiary static tonal range.
+ tertiary100 = Color(red = 255, green = 255, blue = 255),
+ tertiary99 = Color(red = 255, green = 251, blue = 250),
+ tertiary95 = Color(red = 255, green = 236, blue = 241),
+ tertiary90 = Color(red = 255, green = 216, blue = 228),
+ tertiary80 = Color(red = 239, green = 184, blue = 200),
+ tertiary70 = Color(red = 210, green = 157, blue = 172),
+ tertiary60 = Color(red = 181, green = 131, blue = 146),
+ tertiary50 = Color(red = 152, green = 105, blue = 119),
+ tertiary40 = Color(red = 125, green = 82, blue = 96),
+ tertiary30 = Color(red = 99, green = 59, blue = 72),
+ tertiary20 = Color(red = 73, green = 37, blue = 50),
+ tertiary10 = Color(red = 49, green = 17, blue = 29),
+ tertiary0 = Color(red = 0, green = 0, blue = 0),
+)
+
/** Dynamic colors in Material. */
internal fun dynamicTonalPalette(context: Context) = SettingsTonalPalette(
// The neutral tonal range from the generated dynamic color palette.
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
index 999d8d7dd16a..d801840565ed 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
@@ -19,25 +19,33 @@ package com.android.settingslib.spa.framework.util
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
-import androidx.compose.runtime.snapshotFlow
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChangedBy
import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
+/**
+ * Returns a [Flow] whose values are a list which containing the results of asynchronously applying
+ * the given [transform] function to each element in the original flow's list.
+ */
inline fun <T, R> Flow<List<T>>.asyncMapItem(crossinline transform: (T) -> R): Flow<List<R>> =
map { list -> list.asyncMap(transform) }
-@OptIn(ExperimentalCoroutinesApi::class)
-inline fun <T, R> Flow<T>.mapState(crossinline block: (T) -> State<R>): Flow<R> =
- flatMapLatest { snapshotFlow { block(it).value } }
+/**
+ * Delays the flow a little bit, wait the other flow's first value.
+ */
+fun <T1, T2> Flow<T1>.waitFirst(otherFlow: Flow<T2>): Flow<T1> =
+ combine(otherFlow.distinctUntilChangedBy {}) { value, _ -> value }
-fun <T1, T2> Flow<T1>.waitFirst(flow: Flow<T2>): Flow<T1> =
- combine(flow.distinctUntilChangedBy {}) { value, _ -> value }
+/**
+ * Returns a [Flow] whose values are generated list by combining the most recently emitted non null
+ * values by each flow.
+ */
+inline fun <reified T : Any> combineToList(vararg flows: Flow<T?>): Flow<List<T>> = combine(
+ flows.asList(),
+) { array: Array<T?> -> array.filterNotNull() }
class StateFlowBridge<T> {
private val stateFlow = MutableStateFlow<T?>(null)
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Parameter.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Parameter.kt
index aaf8107b723d..452f76abb984 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Parameter.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Parameter.kt
@@ -19,15 +19,19 @@ package com.android.settingslib.spa.framework.util
import android.os.Bundle
import androidx.navigation.NamedNavArgument
import androidx.navigation.NavType
+import com.android.settingslib.spa.framework.BrowseActivity
fun List<NamedNavArgument>.navRoute(): String {
return this.joinToString("") { argument -> "/{${argument.name}}" }
}
fun List<NamedNavArgument>.navLink(arguments: Bundle? = null): String {
- if (arguments == null) return ""
val argsArray = mutableListOf<String>()
for (navArg in this) {
+ if (arguments == null || !arguments.containsKey(navArg.name)) {
+ argsArray.add("[rt]")
+ continue
+ }
when (navArg.argument.type) {
NavType.StringType -> {
argsArray.add(arguments.getString(navArg.name, ""))
@@ -67,3 +71,21 @@ fun List<NamedNavArgument>.containsIntArg(name: String): Boolean {
}
return false
}
+
+fun getRuntimeArguments(arguments: Bundle? = null): Bundle {
+ val res = Bundle()
+ val highlightEntry = arguments?.getString(BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME)
+ if (highlightEntry != null) {
+ res.putString(BrowseActivity.HIGHLIGHT_ENTRY_PARAM_NAME, highlightEntry)
+ }
+ // Append more general runtime arguments here
+ return res
+}
+
+fun mergeArguments(argsList: List<Bundle?>): Bundle {
+ val res = Bundle()
+ for (args in argsList) {
+ if (args != null) res.putAll(args)
+ }
+ return res
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
new file mode 100644
index 000000000000..a9d1c3735ab7
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/button/ActionButtons.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.widget.button
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material.icons.outlined.Launch
+import androidx.compose.material.icons.outlined.WarningAmber
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.FilledTonalButton
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.RectangleShape
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.theme.divider
+
+data class ActionButton(
+ val text: String,
+ val imageVector: ImageVector,
+ val enabled: Boolean = true,
+ val onClick: () -> Unit,
+)
+
+@Composable
+fun ActionButtons(actionButtons: List<ActionButton>) {
+ Row(
+ Modifier
+ .padding(SettingsDimension.itemPaddingVertical)
+ .clip(SettingsShape.CornerLarge)
+ .height(IntrinsicSize.Min)
+ ) {
+ for ((index, actionButton) in actionButtons.withIndex()) {
+ if (index > 0) ButtonDivider()
+ ActionButton(actionButton)
+ }
+ }
+}
+
+@Composable
+private fun RowScope.ActionButton(actionButton: ActionButton) {
+ FilledTonalButton(
+ onClick = actionButton.onClick,
+ modifier = Modifier
+ .weight(1f)
+ .fillMaxHeight(),
+ enabled = actionButton.enabled,
+ // Because buttons could appear, disappear or change positions, reset the interaction source
+ // to prevent highlight the wrong button.
+ interactionSource = remember(actionButton) { MutableInteractionSource() },
+ shape = RectangleShape,
+ colors = ButtonDefaults.filledTonalButtonColors(
+ containerColor = SettingsTheme.colorScheme.surface,
+ contentColor = SettingsTheme.colorScheme.categoryTitle,
+ disabledContainerColor = SettingsTheme.colorScheme.surface,
+ ),
+ contentPadding = PaddingValues(horizontal = 4.dp, vertical = 20.dp),
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Icon(
+ imageVector = actionButton.imageVector,
+ contentDescription = null,
+ modifier = Modifier.size(SettingsDimension.itemIconSize),
+ )
+ Spacer(Modifier.height(4.dp))
+ Text(
+ text = actionButton.text,
+ style = MaterialTheme.typography.labelLarge,
+ )
+ }
+ }
+}
+
+@Composable
+private fun ButtonDivider() {
+ Box(
+ Modifier
+ .width(1.dp)
+ .background(color = MaterialTheme.colorScheme.divider)
+ )
+}
+
+@Preview
+@Composable
+private fun ActionButtonsPreview() {
+ SettingsTheme {
+ ActionButtons(
+ listOf(
+ ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
+ ActionButton(text = "Uninstall", imageVector = Icons.Outlined.Delete) {},
+ ActionButton(text = "Force stop", imageVector = Icons.Outlined.WarningAmber) {},
+ )
+ )
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
index 0e6f53abca40..47abc87327ad 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/Preference.kt
@@ -21,7 +21,39 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.vector.ImageVector
+import com.android.settingslib.spa.framework.common.EntryMacro
+import com.android.settingslib.spa.framework.common.EntrySearchData
+import com.android.settingslib.spa.framework.compose.navigator
import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.widget.ui.createSettingsIcon
+
+data class SimplePreferenceMacro(
+ val title: String,
+ val summary: String? = null,
+ val icon: ImageVector? = null,
+ val disabled: Boolean = false,
+ val clickRoute: String? = null,
+ val searchKeywords: List<String> = emptyList(),
+) : EntryMacro {
+ @Composable
+ override fun UiLayout() {
+ Preference(model = object : PreferenceModel {
+ override val title: String = this@SimplePreferenceMacro.title
+ override val summary = stateOf(this@SimplePreferenceMacro.summary ?: "")
+ override val icon = createSettingsIcon(this@SimplePreferenceMacro.icon)
+ override val enabled = stateOf(!this@SimplePreferenceMacro.disabled)
+ override val onClick = navigator(clickRoute)
+ })
+ }
+
+ override fun getSearchData(): EntrySearchData {
+ return EntrySearchData(
+ title = this@SimplePreferenceMacro.title,
+ keyword = searchKeywords
+ )
+ }
+}
/**
* The widget model for [Preference] widget.
@@ -71,9 +103,9 @@ interface PreferenceModel {
@Composable
fun Preference(model: PreferenceModel) {
val modifier = remember(model.enabled.value, model.onClick) {
- model.onClick?.let { onClick ->
- Modifier.clickable(enabled = model.enabled.value, onClick = onClick)
- } ?: Modifier
+ model.onClick?.let { onClick ->
+ Modifier.clickable(enabled = model.enabled.value, onClick = onClick)
+ } ?: Modifier
}
BasePreference(
title = model.title,
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
index 77ea3c5448e4..566361029ff6 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
@@ -30,7 +30,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
-import com.android.settingslib.spa.framework.theme.SettingsOpacity
+import com.android.settingslib.spa.framework.theme.divider
@Composable
internal fun TwoTargetPreference(
@@ -67,6 +67,6 @@ private fun PreferenceDivider() {
Modifier
.padding(horizontal = SettingsDimension.itemPaddingEnd)
.size(width = 1.dp, height = SettingsDimension.itemDividerHeight)
- .background(color = MaterialTheme.colorScheme.onSurface.copy(SettingsOpacity.Divider))
+ .background(color = MaterialTheme.colorScheme.divider)
)
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
new file mode 100644
index 000000000000..6aac5bf3839a
--- /dev/null
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.widget.ui
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ColumnScope
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.onGloballyPositioned
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsTheme
+
+/**
+ * A category title that is placed before a group of similar items.
+ */
+@Composable
+fun CategoryTitle(title: String) {
+ Text(
+ text = title,
+ modifier = Modifier.padding(
+ start = SettingsDimension.itemPaddingStart,
+ top = 20.dp,
+ end = SettingsDimension.itemPaddingEnd,
+ bottom = 8.dp,
+ ),
+ color = SettingsTheme.colorScheme.categoryTitle,
+ style = MaterialTheme.typography.labelMedium,
+ )
+}
+
+/**
+ * A container that is used to group similar items. A [Category] displays a [CategoryTitle] and
+ * visually separates groups of items.
+ */
+@Composable
+fun Category(title: String, content: @Composable ColumnScope.() -> Unit) {
+ Column {
+ var displayTitle by remember { mutableStateOf(false) }
+ if (displayTitle) CategoryTitle(title = title)
+ Column(
+ modifier = Modifier.onGloballyPositioned { coordinates ->
+ displayTitle = coordinates.size.height > 0
+ },
+ content = content,
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun CategoryPreview() {
+ SettingsTheme {
+ CategoryTitle("Appearance")
+ }
+}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt
index 4f28e378e510..25a4e7088a1a 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Icon.kt
@@ -33,3 +33,8 @@ fun SettingsIcon(imageVector: ImageVector) {
tint = MaterialTheme.colorScheme.onSurface,
)
}
+
+fun createSettingsIcon(imageVector: ImageVector?): (@Composable () -> Unit)? {
+ if (imageVector == null) return null
+ return { SettingsIcon(imageVector = imageVector) }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt
new file mode 100644
index 000000000000..8101a94a9461
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/button/ActionButtonsTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.widget.button
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Launch
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.performClick
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ActionButtonsTest {
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun button_displayed() {
+ composeTestRule.setContent {
+ ActionButtons(
+ listOf(
+ ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {},
+ )
+ )
+ }
+
+ composeTestRule.onNodeWithText("Open").assertIsDisplayed()
+ }
+
+ @Test
+ fun button_clickable() {
+ var clicked by mutableStateOf(false)
+ composeTestRule.setContent {
+ ActionButtons(
+ listOf(
+ ActionButton(text = "Open", imageVector = Icons.Outlined.Launch) {
+ clicked = true
+ },
+ )
+ )
+ }
+
+ composeTestRule.onNodeWithText("Open").performClick()
+
+ assertThat(clicked).isTrue()
+ }
+}
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt
new file mode 100644
index 000000000000..16e09ee854d8
--- /dev/null
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spa.widget.ui
+
+import androidx.compose.runtime.remember
+import androidx.compose.ui.test.assertIsDisplayed
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onNodeWithText
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class CategoryTest {
+
+ @get:Rule
+ val composeTestRule = createComposeRule()
+
+ @Test
+ fun categoryTitle() {
+ composeTestRule.setContent {
+ CategoryTitle(title = "CategoryTitle")
+ }
+
+ composeTestRule.onNodeWithText("CategoryTitle").assertIsDisplayed()
+ }
+
+ @Test
+ fun category_hasContent_titleDisplayed() {
+ composeTestRule.setContent {
+ Category(title = "CategoryTitle") {
+ Preference(remember {
+ object : PreferenceModel {
+ override val title = "Some Preference"
+ override val summary = stateOf("Some summary")
+ }
+ })
+ }
+ }
+
+ composeTestRule.onNodeWithText("CategoryTitle").assertIsDisplayed()
+ }
+
+ @Test
+ fun category_noContent_titleNotDisplayed() {
+ composeTestRule.setContent {
+ Category(title = "CategoryTitle") {}
+ }
+
+ composeTestRule.onNodeWithText("CategoryTitle").assertDoesNotExist()
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/Android.bp b/packages/SettingsLib/SpaPrivileged/Android.bp
index 082ce97e3273..e7e37e418185 100644
--- a/packages/SettingsLib/SpaPrivileged/Android.bp
+++ b/packages/SettingsLib/SpaPrivileged/Android.bp
@@ -45,3 +45,9 @@ java_defaults {
"-J-Xmx4G",
],
}
+
+// Expose the srcs to tests, so the tests can access the internal classes.
+filegroup {
+ name: "SpaPrivilegedLib_srcs",
+ srcs: ["src/**/*.kt"],
+}
diff --git a/packages/SettingsLib/SpaPrivileged/res/values/strings.xml b/packages/SettingsLib/SpaPrivileged/res/values/strings.xml
index b2302a58229c..25dbe007bac7 100644
--- a/packages/SettingsLib/SpaPrivileged/res/values/strings.xml
+++ b/packages/SettingsLib/SpaPrivileged/res/values/strings.xml
@@ -14,7 +14,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<resources>
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- [CHAR LIMIT=25] Text shown when there are no applications to display. -->
<string name="no_applications">No apps.</string>
<!-- [CHAR LIMIT=NONE] Menu for manage apps to control whether system processes are shown -->
@@ -25,4 +25,6 @@
<string name="app_permission_summary_allowed">Allowed</string>
<!-- Preference summary text for an app when it is disallowed for a permission. [CHAR LIMIT=45] -->
<string name="app_permission_summary_not_allowed">Not allowed</string>
+ <!-- Manage applications, version string displayed in app snippet -->
+ <string name="version_text">version <xliff:g id="version_num">%1$s</xliff:g></string>
</resources>
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
new file mode 100644
index 000000000000..a7de4ce18b32
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/compose/DisposableBroadcastReceiverAsUser.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spaprivileged.framework.compose
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisposableEffect
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalLifecycleOwner
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleEventObserver
+
+/**
+ * A [BroadcastReceiver] which registered when on start and unregistered when on stop.
+ */
+@Composable
+fun DisposableBroadcastReceiverAsUser(
+ userId: Int,
+ intentFilter: IntentFilter,
+ onReceive: (Intent) -> Unit,
+) {
+ val broadcastReceiver = remember {
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ onReceive(intent)
+ }
+ }
+ }
+ val context = LocalContext.current
+ val lifecycleOwner = LocalLifecycleOwner.current
+ DisposableEffect(lifecycleOwner) {
+ val observer = LifecycleEventObserver { _, event ->
+ if (event == Lifecycle.Event.ON_START) {
+ context.registerReceiverAsUser(
+ broadcastReceiver, UserHandle.of(userId), intentFilter, null, null)
+ } else if (event == Lifecycle.Event.ON_STOP) {
+ context.unregisterReceiver(broadcastReceiver)
+ }
+ }
+
+ lifecycleOwner.lifecycle.addObserver(observer)
+
+ onDispose {
+ lifecycleOwner.lifecycle.removeObserver(observer)
+ }
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
index bb94b33bfa28..ee8900352cf2 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppsRepository.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListRepository.kt
@@ -21,7 +21,6 @@ import android.content.Intent
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
-import android.content.pm.UserInfo
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
@@ -30,14 +29,25 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
-class AppsRepository(context: Context) {
+/**
+ * The config used to load the App List.
+ */
+internal data class AppListConfig(
+ val userId: Int,
+ val showInstantApps: Boolean,
+)
+
+/**
+ * The repository to load the App List data.
+ */
+internal class AppListRepository(context: Context) {
private val packageManager = context.packageManager
- fun loadApps(userInfoFlow: Flow<UserInfo>): Flow<List<ApplicationInfo>> = userInfoFlow
+ fun loadApps(configFlow: Flow<AppListConfig>): Flow<List<ApplicationInfo>> = configFlow
.map { loadApps(it) }
.flowOn(Dispatchers.Default)
- private suspend fun loadApps(userInfo: UserInfo): List<ApplicationInfo> {
+ private suspend fun loadApps(config: AppListConfig): List<ApplicationInfo> {
return coroutineScope {
val hiddenSystemModulesDeferred = async {
packageManager.getInstalledModules(0)
@@ -50,11 +60,11 @@ class AppsRepository(context: Context) {
PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS).toLong()
)
val installedApplicationsAsUser =
- packageManager.getInstalledApplicationsAsUser(flags, userInfo.id)
+ packageManager.getInstalledApplicationsAsUser(flags, config.userId)
val hiddenSystemModules = hiddenSystemModulesDeferred.await()
installedApplicationsAsUser.filter { app ->
- app.isInAppList(hiddenSystemModules)
+ app.isInAppList(config.showInstantApps, hiddenSystemModules)
}
}
}
@@ -63,9 +73,7 @@ class AppsRepository(context: Context) {
userIdFlow: Flow<Int>,
showSystemFlow: Flow<Boolean>,
): Flow<(app: ApplicationInfo) -> Boolean> =
- userIdFlow.combine(showSystemFlow) { userId, showSystem ->
- showSystemPredicate(userId, showSystem)
- }
+ userIdFlow.combine(showSystemFlow, ::showSystemPredicate)
private suspend fun showSystemPredicate(
userId: Int,
@@ -102,12 +110,15 @@ class AppsRepository(context: Context) {
}
companion object {
- private fun ApplicationInfo.isInAppList(hiddenSystemModules: Set<String>) =
- when {
- packageName in hiddenSystemModules -> false
- enabled -> true
- enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER -> true
- else -> false
- }
+ private fun ApplicationInfo.isInAppList(
+ showInstantApps: Boolean,
+ hiddenSystemModules: Set<String>,
+ ) = when {
+ !showInstantApps && isInstantApp -> false
+ packageName in hiddenSystemModules -> false
+ enabled -> true
+ isDisabledUntilUsed -> true
+ else -> false
+ }
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
index 9265158b3b4a..1e487daa36fb 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/AppListViewModel.kt
@@ -18,7 +18,6 @@ package com.android.settingslib.spaprivileged.model.app
import android.app.Application
import android.content.pm.ApplicationInfo
-import android.content.pm.UserInfo
import android.icu.text.Collator
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
@@ -48,28 +47,29 @@ internal data class AppListData<T : AppRecord>(
internal class AppListViewModel<T : AppRecord>(
application: Application,
) : AndroidViewModel(application) {
- val userInfo = StateFlowBridge<UserInfo>()
+ val appListConfig = StateFlowBridge<AppListConfig>()
val listModel = StateFlowBridge<AppListModel<T>>()
val showSystem = StateFlowBridge<Boolean>()
val option = StateFlowBridge<Int>()
val searchQuery = StateFlowBridge<String>()
- private val appsRepository = AppsRepository(application)
+ private val appListRepository = AppListRepository(application)
private val appRepository = AppRepositoryImpl(application)
private val collator = Collator.getInstance().freeze()
private val labelMap = ConcurrentHashMap<String, String>()
private val scope = viewModelScope + Dispatchers.Default
- private val userIdFlow = userInfo.flow.map { it.id }
+ private val userIdFlow = appListConfig.flow.map { it.userId }
private val recordListFlow = listModel.flow
- .flatMapLatest { it.transform(userIdFlow, appsRepository.loadApps(userInfo.flow)) }
+ .flatMapLatest { it.transform(userIdFlow, appListRepository.loadApps(appListConfig.flow)) }
.shareIn(scope = scope, started = SharingStarted.Eagerly, replay = 1)
- private val systemFilteredFlow = appsRepository.showSystemPredicate(userIdFlow, showSystem.flow)
- .combine(recordListFlow) { showAppPredicate, recordList ->
- recordList.filter { showAppPredicate(it.app) }
- }
+ private val systemFilteredFlow =
+ appListRepository.showSystemPredicate(userIdFlow, showSystem.flow)
+ .combine(recordListFlow) { showAppPredicate, recordList ->
+ recordList.filter { showAppPredicate(it.app) }
+ }
val appListDataFlow = option.flow.flatMapLatest(::filterAndSort)
.combine(searchQuery.flow) { appListData, searchQuery ->
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt
new file mode 100644
index 000000000000..f9f75fb55d39
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/ApplicationInfos.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spaprivileged.model.app
+
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.os.UserHandle
+
+/** The user id for a given application. */
+val ApplicationInfo.userId: Int
+ get() = UserHandle.getUserId(uid)
+
+/** The [UserHandle] for a given application. */
+val ApplicationInfo.userHandle: UserHandle
+ get() = UserHandle.getUserHandleForUid(uid)
+
+/** Checks whether a flag is associated with the application. */
+fun ApplicationInfo.hasFlag(flag: Int): Boolean = (flags and flag) > 0
+
+/** Checks whether the application is disabled until used. */
+val ApplicationInfo.isDisabledUntilUsed: Boolean
+ get() = enabledSetting == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED
+
+/** Converts to the route string which used in navigation. */
+fun ApplicationInfo.toRoute() = "$packageName/$userId"
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
index ba8af5400e18..5c5caf598717 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PackageManagers.kt
@@ -55,7 +55,7 @@ object PackageManagers {
iPackageManager.isPackageAvailable(it, userId)
}.toSet()
- private fun getPackageInfoAsUser(packageName: String, flags: Int, userId: Int): PackageInfo? =
+ fun getPackageInfoAsUser(packageName: String, flags: Int, userId: Int): PackageInfo? =
try {
PackageManager.getPackageInfoAsUserCached(packageName, flags.toLong(), userId)
} catch (e: PackageManager.NameNotFoundException) {
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
new file mode 100644
index 000000000000..061580702327
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/enterprise/RestrictionsProvider.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spaprivileged.model.enterprise
+
+import android.app.admin.DevicePolicyResources.Strings.Settings
+import android.content.Context
+import android.os.UserHandle
+import android.os.UserManager
+import androidx.lifecycle.LiveData
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
+import com.android.settingslib.RestrictedLockUtilsInternal
+import com.android.settingslib.spaprivileged.R
+
+data class Restrictions(
+ val userId: Int,
+ val keys: List<String>,
+)
+
+sealed class RestrictedMode
+
+object NoRestricted : RestrictedMode()
+
+object BaseUserRestricted : RestrictedMode()
+
+data class BlockedByAdmin(
+ val enterpriseRepository: EnterpriseRepository,
+ val enforcedAdmin: EnforcedAdmin,
+) : RestrictedMode() {
+ fun getSummary(checked: Boolean?): String = when (checked) {
+ true -> enterpriseRepository.getEnterpriseString(
+ Settings.ENABLED_BY_ADMIN_SWITCH_SUMMARY, R.string.enabled_by_admin
+ )
+ false -> enterpriseRepository.getEnterpriseString(
+ Settings.DISABLED_BY_ADMIN_SWITCH_SUMMARY, R.string.disabled_by_admin
+ )
+ else -> ""
+ }
+}
+
+class RestrictionsProvider(
+ private val context: Context,
+ private val restrictions: Restrictions,
+) {
+ private val userManager by lazy { UserManager.get(context) }
+ private val enterpriseRepository by lazy { EnterpriseRepository(context) }
+
+ val restrictedMode = object : LiveData<RestrictedMode>() {
+ override fun onActive() {
+ postValue(getRestrictedMode())
+ }
+
+ override fun onInactive() {
+ }
+ }
+
+ private fun getRestrictedMode(): RestrictedMode {
+ for (key in restrictions.keys) {
+ if (userManager.hasBaseUserRestriction(key, UserHandle.of(restrictions.userId))) {
+ return BaseUserRestricted
+ }
+ }
+ for (key in restrictions.keys) {
+ RestrictedLockUtilsInternal
+ .checkIfRestrictionEnforced(context, key, restrictions.userId)
+ ?.let {
+ return BlockedByAdmin(
+ enterpriseRepository = enterpriseRepository,
+ enforcedAdmin = it,
+ )
+ }
+ }
+ return NoRestricted
+ }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
index f51d2dbfdeef..9611b136d881 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfo.kt
@@ -17,6 +17,8 @@
package com.android.settingslib.spaprivileged.template.app
import android.content.pm.ApplicationInfo
+import android.content.pm.PackageInfo
+import android.text.BidiFormatter
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -25,49 +27,69 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.material3.Divider
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.compose.rememberDrawablePainter
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.ui.SettingsBody
import com.android.settingslib.spa.widget.ui.SettingsTitle
-import com.android.settingslib.spaprivileged.model.app.PackageManagers
+import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.model.app.rememberAppRepository
-@Composable
-fun AppInfo(packageName: String, userId: Int) {
- Column(
- modifier = Modifier
- .fillMaxWidth()
- .padding(
- horizontal = SettingsDimension.itemPaddingStart,
- vertical = SettingsDimension.itemPaddingVertical,
- ),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- val packageInfo =
- remember { PackageManagers.getPackageInfoAsUser(packageName, userId) } ?: return
- Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
- AppIcon(app = packageInfo.applicationInfo, size = SettingsDimension.appIconInfoSize)
+class AppInfoProvider(private val packageInfo: PackageInfo) {
+ @Composable
+ fun AppInfo(displayVersion: Boolean = false) {
+ Column(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(
+ horizontal = SettingsDimension.itemPaddingStart,
+ vertical = SettingsDimension.itemPaddingVertical,
+ ),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ val app = packageInfo.applicationInfo
+ Box(modifier = Modifier.padding(SettingsDimension.itemPaddingAround)) {
+ AppIcon(app = app, size = SettingsDimension.appIconInfoSize)
+ }
+ AppLabel(app)
+ InstallType(app)
+ if (displayVersion) AppVersion()
}
- AppLabel(packageInfo.applicationInfo)
- AppVersion(packageInfo.versionName)
}
-}
-@Composable
-private fun AppVersion(versionName: String?) {
- if (versionName == null) return
- Spacer(modifier = Modifier.height(4.dp))
- SettingsBody(versionName)
+ @Composable
+ private fun InstallType(app: ApplicationInfo) {
+ if (!app.isInstantApp) return
+ Spacer(modifier = Modifier.height(4.dp))
+ SettingsBody(stringResource(R.string.install_type_instant))
+ }
+
+ @Composable
+ private fun AppVersion() {
+ if (packageInfo.versionName == null) return
+ Spacer(modifier = Modifier.height(4.dp))
+ SettingsBody(packageInfo.versionName)
+ }
+
+ @Composable
+ fun FooterAppVersion() {
+ if (packageInfo.versionName == null) return
+ Divider()
+ Box(modifier = Modifier.padding(SettingsDimension.itemPadding)) {
+ val versionName = BidiFormatter.getInstance().unicodeWrap(packageInfo.versionName)
+ SettingsBody(stringResource(R.string.version_text, versionName))
+ }
+ }
}
@Composable
-fun AppIcon(app: ApplicationInfo, size: Dp) {
+internal fun AppIcon(app: ApplicationInfo, size: Dp) {
val appRepository = rememberAppRepository()
Image(
painter = rememberDrawablePainter(appRepository.produceIcon(app).value),
@@ -77,7 +99,7 @@ fun AppIcon(app: ApplicationInfo, size: Dp) {
}
@Composable
-fun AppLabel(app: ApplicationInfo) {
+internal fun AppLabel(app: ApplicationInfo) {
val appRepository = rememberAppRepository()
SettingsTitle(appRepository.produceLabel(app))
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfoPage.kt
index 9b45318ffd82..4f88398b9550 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppInfoPage.kt
@@ -17,8 +17,10 @@
package com.android.settingslib.spaprivileged.template.app
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
import com.android.settingslib.spa.widget.ui.Footer
+import com.android.settingslib.spaprivileged.model.app.PackageManagers
@Composable
fun AppInfoPage(
@@ -29,7 +31,12 @@ fun AppInfoPage(
content: @Composable () -> Unit,
) {
RegularScaffold(title = title) {
- AppInfo(packageName, userId)
+ val appInfoProvider = remember {
+ val packageInfo = PackageManagers.getPackageInfoAsUser(packageName, userId)
+ ?: return@RegularScaffold
+ AppInfoProvider(packageInfo)
+ }
+ appInfoProvider.AppInfo(displayVersion = true)
content()
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
index 315dc5d6ff70..6318b4e9c186 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppList.kt
@@ -16,7 +16,6 @@
package com.android.settingslib.spaprivileged.template.app
-import android.content.pm.UserInfo
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn
@@ -33,6 +32,7 @@ import com.android.settingslib.spa.framework.compose.toState
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.ui.PlaceholderTitle
import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.model.app.AppListConfig
import com.android.settingslib.spaprivileged.model.app.AppListData
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppListViewModel
@@ -41,17 +41,22 @@ import kotlinx.coroutines.Dispatchers
private const val TAG = "AppList"
+/**
+ * The template to render an App List.
+ *
+ * This UI element will take the remaining space on the screen to show the App List.
+ */
@Composable
internal fun <T : AppRecord> AppList(
- userInfo: UserInfo,
+ appListConfig: AppListConfig,
listModel: AppListModel<T>,
showSystem: State<Boolean>,
option: State<Int>,
searchQuery: State<String>,
appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
) {
- LogCompositions(TAG, userInfo.id.toString())
- val appListData = loadAppEntries(userInfo, listModel, showSystem, option, searchQuery)
+ LogCompositions(TAG, appListConfig.userId.toString())
+ val appListData = loadAppEntries(appListConfig, listModel, showSystem, option, searchQuery)
AppListWidget(appListData, listModel, appItem)
}
@@ -85,14 +90,14 @@ private fun <T : AppRecord> AppListWidget(
@Composable
private fun <T : AppRecord> loadAppEntries(
- userInfo: UserInfo,
+ appListConfig: AppListConfig,
listModel: AppListModel<T>,
showSystem: State<Boolean>,
option: State<Int>,
searchQuery: State<String>,
): State<AppListData<T>?> {
- val viewModel: AppListViewModel<T> = viewModel(key = userInfo.id.toString())
- viewModel.userInfo.setIfAbsent(userInfo)
+ val viewModel: AppListViewModel<T> = viewModel(key = appListConfig.userId.toString())
+ viewModel.appListConfig.setIfAbsent(appListConfig)
viewModel.listModel.setIfAbsent(listModel)
viewModel.showSystem.Sync(showSystem)
viewModel.option.Sync(option)
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
index d537ec258cad..fb03d2c2e2cb 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/AppListPage.kt
@@ -36,14 +36,19 @@ import com.android.settingslib.spa.widget.scaffold.MoreOptionsAction
import com.android.settingslib.spa.widget.scaffold.SettingsScaffold
import com.android.settingslib.spa.widget.ui.Spinner
import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.model.app.AppListConfig
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.template.common.WorkProfilePager
+/**
+ * The full screen template for an App List page.
+ */
@Composable
fun <T : AppRecord> AppListPage(
title: String,
listModel: AppListModel<T>,
+ showInstantApps: Boolean = false,
primaryUserOnly: Boolean = false,
appItem: @Composable (itemState: AppListItemModel<T>) -> Unit,
) {
@@ -62,7 +67,10 @@ fun <T : AppRecord> AppListPage(
val selectedOption = rememberSaveable { mutableStateOf(0) }
Spinner(options, selectedOption.value) { selectedOption.value = it }
AppList(
- userInfo = userInfo,
+ appListConfig = AppListConfig(
+ userId = userInfo.id,
+ showInstantApps = showInstantApps,
+ ),
listModel = listModel,
showSystem = showSystem,
option = selectedOption,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
index c031fe808346..883eddff8054 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPage.kt
@@ -34,11 +34,14 @@ import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
-import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import com.android.settingslib.spaprivileged.model.app.PackageManagers
import com.android.settingslib.spaprivileged.model.app.toRoute
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
import kotlinx.coroutines.Dispatchers
private const val ENTRY_NAME = "AllowControl"
@@ -60,7 +63,7 @@ internal class TogglePermissionAppInfoPageProvider(
override val parameter = PAGE_PARAMETER
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
- val owner = SettingsPage.create(name, parameter, arguments)
+ val owner = SettingsPage.create(name, parameter = parameter, arguments = arguments)
val entryList = mutableListOf<SettingsEntry>()
entryList.add(
SettingsEntryBuilder.create(ENTRY_NAME, owner).setIsAllowSearch(false).build()
@@ -79,12 +82,36 @@ internal class TogglePermissionAppInfoPageProvider(
companion object {
@Composable
- internal fun navigator(permissionType: String, app: ApplicationInfo) =
+ fun navigator(permissionType: String, app: ApplicationInfo) =
navigator(route = "$PAGE_NAME/$permissionType/${app.toRoute()}")
- internal fun buildPageData(permissionType: String): SettingsPage {
+ @Composable
+ fun <T : AppRecord> EntryItem(
+ permissionType: String,
+ app: ApplicationInfo,
+ listModel: TogglePermissionAppListModel<T>,
+ ) {
+ val context = LocalContext.current
+ val internalListModel = remember {
+ TogglePermissionInternalAppListModel(context, listModel)
+ }
+ val record = remember { listModel.transformItem(app) }
+ if (!remember { listModel.isChangeable(record) }) return
+ Preference(
+ object : PreferenceModel {
+ override val title = stringResource(listModel.pageTitleResId)
+ override val summary = internalListModel.getSummary(record)
+ override val onClick = navigator(permissionType, app)
+ }
+ )
+ }
+
+ fun buildPageData(permissionType: String): SettingsPage {
return SettingsPage.create(
- PAGE_NAME, PAGE_PARAMETER, bundleOf(PERMISSION to permissionType))
+ name = PAGE_NAME,
+ parameter = PAGE_PARAMETER,
+ arguments = bundleOf(PERMISSION to permissionType)
+ )
}
}
}
@@ -105,7 +132,7 @@ private fun TogglePermissionAppInfoPage(
LaunchedEffect(model, Dispatchers.Default) {
model.initState()
}
- SwitchPreference(model)
+ RestrictedSwitchPreference(model, Restrictions(userId, listModel.switchRestrictionKeys))
}
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
index 4c748b8d0fc8..5d49d24ecf8e 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppList.kt
@@ -20,13 +20,10 @@ import android.content.Context
import android.content.pm.ApplicationInfo
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
-import androidx.compose.ui.res.stringResource
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.framework.util.asyncMapItem
-import com.android.settingslib.spa.widget.preference.Preference
-import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
import kotlinx.coroutines.flow.Flow
@@ -37,6 +34,8 @@ interface TogglePermissionAppListModel<T : AppRecord> {
val pageTitleResId: Int
val switchTitleResId: Int
val footerResId: Int
+ val switchRestrictionKeys: List<String>
+ get() = emptyList()
/**
* Loads the extra info for the App List, and generates the [AppRecord] List.
@@ -83,28 +82,22 @@ interface TogglePermissionAppListProvider {
fun createModel(context: Context): TogglePermissionAppListModel<out AppRecord>
- fun buildInjectEntry(): SettingsEntryBuilder {
- return TogglePermissionAppListPageProvider.buildInjectEntry(permissionType)
- }
-
- @Composable
- fun EntryItem() {
- val listModel = rememberContext(::createModel)
- Preference(
- object : PreferenceModel {
- override val title = stringResource(listModel.pageTitleResId)
- override val onClick = TogglePermissionAppListPageProvider.navigator(permissionType)
- }
- )
- }
+ fun buildAppListInjectEntry(): SettingsEntryBuilder =
+ TogglePermissionAppListPageProvider.buildInjectEntry(permissionType) { createModel(it) }
/**
* Gets the route to the toggle permission App List page.
*
* Expose route to enable enter from non-SPA pages.
*/
- fun getRoute(): String =
+ fun getAppListRoute(): String =
TogglePermissionAppListPageProvider.getRoute(permissionType)
+
+ @Composable
+ fun InfoPageEntryItem(app: ApplicationInfo) {
+ val listModel = rememberContext(::createModel)
+ TogglePermissionAppInfoPageProvider.EntryItem(permissionType, app, listModel)
+ }
}
class TogglePermissionAppListTemplate(
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
index 65cc4f9a165c..ec7d75e969df 100644
--- a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppListPage.kt
@@ -22,6 +22,7 @@ import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
@@ -33,10 +34,17 @@ import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPage
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.compose.navigator
+import com.android.settingslib.spa.framework.compose.rememberContext
import com.android.settingslib.spa.framework.util.getStringArg
+import com.android.settingslib.spa.widget.preference.Preference
+import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spaprivileged.R
import com.android.settingslib.spaprivileged.model.app.AppListModel
import com.android.settingslib.spaprivileged.model.app.AppRecord
+import com.android.settingslib.spaprivileged.model.app.userId
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvider
+import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference
import kotlinx.coroutines.flow.Flow
private const val ENTRY_NAME = "AppList"
@@ -55,7 +63,7 @@ internal class TogglePermissionAppListPageProvider(
override fun buildEntry(arguments: Bundle?): List<SettingsEntry> {
val permissionType = parameter.getStringArg(PERMISSION, arguments)!!
- val appListPage = SettingsPage.create(name, parameter, arguments)
+ val appListPage = SettingsPage.create(name, parameter = parameter, arguments = arguments)
val appInfoPage = TogglePermissionAppInfoPageProvider.buildPageData(permissionType)
val entryList = mutableListOf<SettingsEntry>()
// TODO: add more categories, such as personal, work, cloned, etc.
@@ -102,20 +110,32 @@ internal class TogglePermissionAppListPageProvider(
*
* Expose route to enable enter from non-SPA pages.
*/
- internal fun getRoute(permissionType: String) = "$PAGE_NAME/$permissionType"
+ fun getRoute(permissionType: String) = "$PAGE_NAME/$permissionType"
- @Composable
- internal fun navigator(permissionType: String) = navigator(route = getRoute(permissionType))
-
- internal fun buildInjectEntry(permissionType: String): SettingsEntryBuilder {
+ fun buildInjectEntry(
+ permissionType: String,
+ listModelSupplier: (Context) -> TogglePermissionAppListModel<out AppRecord>,
+ ): SettingsEntryBuilder {
val appListPage = SettingsPage.create(
- PAGE_NAME, PAGE_PARAMETER, bundleOf(PERMISSION to permissionType))
+ name = PAGE_NAME,
+ parameter = PAGE_PARAMETER,
+ arguments = bundleOf(PERMISSION to permissionType)
+ )
return SettingsEntryBuilder.createInject(owner = appListPage).setIsAllowSearch(false)
+ .setUiLayoutFn {
+ val listModel = rememberContext(listModelSupplier)
+ Preference(
+ object : PreferenceModel {
+ override val title = stringResource(listModel.pageTitleResId)
+ override val onClick = navigator(route = getRoute(permissionType))
+ }
+ )
+ }
}
}
}
-private class TogglePermissionInternalAppListModel<T : AppRecord>(
+internal class TogglePermissionInternalAppListModel<T : AppRecord>(
private val context: Context,
private val listModel: TogglePermissionAppListModel<T>,
) : AppListModel<T> {
@@ -127,15 +147,37 @@ private class TogglePermissionInternalAppListModel<T : AppRecord>(
@Composable
override fun getSummary(option: Int, record: T): State<String> {
+ return getSummary(record)
+ }
+
+ @Composable
+ fun getSummary(record: T): State<String> {
+ val restrictionsProvider = remember {
+ val restrictions = Restrictions(
+ userId = record.app.userId,
+ keys = listModel.switchRestrictionKeys,
+ )
+ RestrictionsProvider(context, restrictions)
+ }
+ val restrictedMode = restrictionsProvider.restrictedMode.observeAsState()
val allowed = listModel.isAllowed(record)
return remember {
derivedStateOf {
- when (allowed.value) {
- true -> context.getString(R.string.app_permission_summary_allowed)
- false -> context.getString(R.string.app_permission_summary_not_allowed)
- else -> ""
- }
+ RestrictedSwitchPreference.getSummary(
+ context = context,
+ restrictedMode = restrictedMode.value,
+ noRestrictedSummary = getNoRestrictedSummary(allowed),
+ checked = allowed,
+ ).value
}
}
}
+
+ private fun getNoRestrictedSummary(allowed: State<Boolean?>) = derivedStateOf {
+ when (allowed.value) {
+ true -> context.getString(R.string.app_permission_summary_allowed)
+ false -> context.getString(R.string.app_permission_summary_not_allowed)
+ else -> context.getString(R.string.summary_placeholder)
+ }
+ }
}
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
new file mode 100644
index 000000000000..31fd3ad6d521
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/template/preference/RestrictedSwitchPreference.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spaprivileged.template.preference
+
+import android.content.Context
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.State
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.semantics.Role
+import com.android.settingslib.RestrictedLockUtils
+import com.android.settingslib.spa.framework.compose.stateOf
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import com.android.settingslib.spaprivileged.R
+import com.android.settingslib.spaprivileged.model.enterprise.BaseUserRestricted
+import com.android.settingslib.spaprivileged.model.enterprise.BlockedByAdmin
+import com.android.settingslib.spaprivileged.model.enterprise.NoRestricted
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictedMode
+import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
+import com.android.settingslib.spaprivileged.model.enterprise.RestrictionsProvider
+
+@Composable
+fun RestrictedSwitchPreference(model: SwitchPreferenceModel, restrictions: Restrictions) {
+ if (restrictions.keys.isEmpty()) {
+ SwitchPreference(model)
+ return
+ }
+ val context = LocalContext.current
+ val restrictionsProvider = remember { RestrictionsProvider(context, restrictions) }
+ val restrictedMode = restrictionsProvider.restrictedMode.observeAsState().value ?: return
+ val restrictedSwitchModel = remember(restrictedMode) {
+ RestrictedSwitchPreferenceModel(context, model, restrictedMode)
+ }
+ Box(remember { restrictedSwitchModel.getModifier() }) {
+ SwitchPreference(restrictedSwitchModel)
+ }
+}
+
+object RestrictedSwitchPreference {
+ fun getSummary(
+ context: Context,
+ restrictedMode: RestrictedMode?,
+ noRestrictedSummary: State<String>,
+ checked: State<Boolean?>,
+ ): State<String> = when (restrictedMode) {
+ is NoRestricted -> noRestrictedSummary
+ is BaseUserRestricted -> stateOf(context.getString(R.string.disabled))
+ is BlockedByAdmin -> derivedStateOf { restrictedMode.getSummary(checked.value) }
+ null -> stateOf(context.getString(R.string.summary_placeholder))
+ }
+}
+
+private class RestrictedSwitchPreferenceModel(
+ private val context: Context,
+ model: SwitchPreferenceModel,
+ private val restrictedMode: RestrictedMode,
+) : SwitchPreferenceModel {
+ override val title = model.title
+
+ override val summary = RestrictedSwitchPreference.getSummary(
+ context = context,
+ restrictedMode = restrictedMode,
+ noRestrictedSummary = model.summary,
+ checked = model.checked,
+ )
+
+ override val checked = when (restrictedMode) {
+ is NoRestricted -> model.checked
+ is BaseUserRestricted -> stateOf(false)
+ is BlockedByAdmin -> model.checked
+ }
+
+ override val changeable = when (restrictedMode) {
+ is NoRestricted -> model.changeable
+ is BaseUserRestricted -> stateOf(false)
+ is BlockedByAdmin -> stateOf(false)
+ }
+
+ override val onCheckedChange = when (restrictedMode) {
+ is NoRestricted -> model.onCheckedChange
+ is BaseUserRestricted -> null
+ is BlockedByAdmin -> null
+ }
+
+ fun getModifier(): Modifier = when (restrictedMode) {
+ is BlockedByAdmin -> Modifier.clickable(role = Role.Switch) {
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
+ context, restrictedMode.enforcedAdmin
+ )
+ }
+ else -> Modifier
+ }
+}
diff --git a/packages/SystemUI/compose/gallery/tests/Android.bp b/packages/SettingsLib/SpaPrivileged/tests/Android.bp
index 3e01f7d2c431..940a1fed817b 100644
--- a/packages/SystemUI/compose/gallery/tests/Android.bp
+++ b/packages/SettingsLib/SpaPrivileged/tests/Android.bp
@@ -1,3 +1,4 @@
+//
// Copyright (C) 2022 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -11,37 +12,35 @@
// WITHOUT WARRANTIES 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_packages_SystemUI_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
+ default_applicable_licenses: ["frameworks_base_license"],
}
android_test {
- name: "SystemUIComposeGalleryTests",
- manifest: "AndroidManifest.xml",
- test_suites: ["device-tests"],
- sdk_version: "current",
+ name: "SpaPrivilegedLibTests",
certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
srcs: [
+ ":SpaPrivilegedLib_srcs",
"src/**/*.kt",
],
static_libs: [
- "SystemUIComposeGalleryLib",
-
- "androidx.test.runner",
- "androidx.test.ext.junit",
-
+ "SpaPrivilegedLib",
"androidx.compose.runtime_runtime",
"androidx.compose.ui_ui-test-junit4",
"androidx.compose.ui_ui-test-manifest",
+ "androidx.test.ext.junit",
+ "androidx.test.runner",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ ],
+ kotlincflags: [
+ "-Xjvm-default=all",
+ "-Xopt-in=kotlin.RequiresOptIn",
],
-
- kotlincflags: ["-Xjvm-default=enable"],
}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/AndroidManifest.xml b/packages/SettingsLib/SpaPrivileged/tests/AndroidManifest.xml
new file mode 100644
index 000000000000..c4f490ed398b
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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.spaprivileged.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:label="Tests for SpaPrivilegedLib"
+ android:targetPackage="com.android.settingslib.spaprivileged.tests" />
+</manifest>
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
new file mode 100644
index 000000000000..c010c68f37a4
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/AppListRepositoryTest.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spaprivileged.model.app
+
+import android.content.Context
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ApplicationInfoFlags
+import android.content.pm.PackageManager.ResolveInfoFlags
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.toList
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+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.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+
+private const val USER_ID = 0
+
+@RunWith(AndroidJUnit4::class)
+class AppListRepositoryTest {
+
+ @JvmField
+ @Rule
+ val mockito: MockitoRule = MockitoJUnit.rule()
+
+ @Mock
+ private lateinit var context: Context
+
+ @Mock
+ private lateinit var packageManager: PackageManager
+
+ private lateinit var repository: AppListRepository
+
+ private val normalApp = ApplicationInfo().apply {
+ packageName = "normal"
+ enabled = true
+ }
+
+ private val instantApp = ApplicationInfo().apply {
+ packageName = "instant"
+ enabled = true
+ privateFlags = ApplicationInfo.PRIVATE_FLAG_INSTANT
+ }
+
+ @Before
+ fun setUp() {
+ whenever(context.packageManager).thenReturn(packageManager)
+ whenever(packageManager.getInstalledModules(anyInt())).thenReturn(emptyList())
+ whenever(
+ packageManager.getInstalledApplicationsAsUser(any<ApplicationInfoFlags>(), eq(USER_ID))
+ ).thenReturn(listOf(normalApp, instantApp))
+ whenever(
+ packageManager.queryIntentActivitiesAsUser(any(), any<ResolveInfoFlags>(), eq(USER_ID))
+ ).thenReturn(emptyList())
+
+ repository = AppListRepository(context)
+ }
+
+ @Test
+ fun notShowInstantApps(): Unit = runBlocking {
+ val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = false)
+
+ val appListFlow = repository.loadApps(flowOf(appListConfig))
+
+ launch {
+ val flowValues = mutableListOf<List<ApplicationInfo>>()
+ appListFlow.toList(flowValues)
+ assertThat(flowValues).hasSize(1)
+
+ assertThat(flowValues[0]).containsExactly(normalApp)
+ }
+ }
+
+ @Test
+ fun showInstantApps(): Unit = runBlocking {
+ val appListConfig = AppListConfig(userId = USER_ID, showInstantApps = true)
+
+ val appListFlow = repository.loadApps(flowOf(appListConfig))
+
+ launch {
+ val flowValues = mutableListOf<List<ApplicationInfo>>()
+ appListFlow.toList(flowValues)
+ assertThat(flowValues).hasSize(1)
+
+ assertThat(flowValues[0]).containsExactly(normalApp, instantApp)
+ }
+ }
+}
diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
deleted file mode 100644
index c8a80ac57c00..000000000000
--- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_background.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:alpha="0.24" android:color="?android:attr/colorBackground" />
-</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
deleted file mode 100644
index 8dcfdbb8cf1e..000000000000
--- a/packages/SettingsLib/res/color/dark_mode_icon_color_dual_tone_fill.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:alpha="0.47" android:color="?android:attr/colorBackground" />
-</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
deleted file mode 100644
index 34de5489a28b..000000000000
--- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_background.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:alpha="0.3" android:color="?android:attr/colorForeground" />
-</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml b/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
deleted file mode 100644
index 15944c3a2a07..000000000000
--- a/packages/SettingsLib/res/color/light_mode_icon_color_dual_tone_fill.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?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.
--->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?android:attr/colorForeground" />
-</selector> \ No newline at end of file
diff --git a/packages/SettingsLib/res/drawable/ic_5g_uc_mobiledata.xml b/packages/SettingsLib/res/drawable/ic_5g_uc_mobiledata.xml
deleted file mode 100644
index 93fcad298c3f..000000000000
--- a/packages/SettingsLib/res/drawable/ic_5g_uc_mobiledata.xml
+++ /dev/null
@@ -1,33 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="27dp"
- android:height="16dp"
- android:viewportWidth="27.0"
- android:viewportHeight="16.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M5.3,13.22c-0.57,0 -1.1,-0.11 -1.58,-0.34c-0.48,-0.22 -0.87,-0.55 -1.18,-0.98C2.24,11.47 2.06,10.93 2,10.3l1.48,-0.2c0.07,0.5 0.25,0.92 0.56,1.25c0.32,0.32 0.74,0.48 1.26,0.48c0.57,0 1.02,-0.18 1.34,-0.55c0.33,-0.37 0.49,-0.87 0.49,-1.48c0,-0.61 -0.16,-1.09 -0.49,-1.46C6.32,7.96 5.88,7.78 5.32,7.78C5,7.78 4.7,7.85 4.42,8C4.15,8.14 3.93,8.33 3.76,8.56L2.28,7.92l0.6,-4.94h5.26v1.36H4.1L3.72,7.02l0.08,0.03C4,6.87 4.25,6.73 4.55,6.62c0.3,-0.12 0.63,-0.18 1.01,-0.18c0.6,0 1.13,0.14 1.6,0.41C7.62,7.11 7.98,7.5 8.24,8c0.27,0.5 0.41,1.1 0.41,1.79c0,0.66 -0.14,1.26 -0.43,1.78c-0.28,0.51 -0.67,0.92 -1.18,1.22C6.55,13.08 5.97,13.22 5.3,13.22z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M14.51,13.22c-0.88,0 -1.66,-0.21 -2.34,-0.64c-0.67,-0.44 -1.2,-1.05 -1.6,-1.83C10.19,9.95 10,9.03 10,7.99c0,-1.05 0.21,-1.96 0.62,-2.74c0.41,-0.79 0.97,-1.4 1.67,-1.83c0.7,-0.44 1.5,-0.66 2.39,-0.66c1.09,0 2.01,0.25 2.74,0.76c0.75,0.5 1.22,1.21 1.41,2.11l-1.47,0.39c-0.17,-0.56 -0.48,-1 -0.94,-1.32c-0.45,-0.33 -1.03,-0.49 -1.75,-0.49c-0.57,0 -1.09,0.14 -1.57,0.43c-0.48,0.29 -0.85,0.71 -1.13,1.27s-0.42,1.25 -0.42,2.07c0,0.81 0.14,1.5 0.41,2.07c0.28,0.56 0.65,0.98 1.11,1.27c0.47,0.29 0.98,0.43 1.54,0.43c0.57,0 1.06,-0.11 1.47,-0.34c0.42,-0.23 0.75,-0.55 0.99,-0.94c0.25,-0.4 0.41,-0.85 0.46,-1.36h-2.88V7.79h4.37c0,0.87 0,1.74 0,2.6c0,0.87 0,1.74 0,2.6H17.6v-1.4h-0.08c-0.28,0.49 -0.67,0.88 -1.18,1.18C15.85,13.07 15.24,13.22 14.51,13.22z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M23,7.39c-0.53,0 -0.94,-0.16 -1.25,-0.47C21.45,6.6 21.3,6.16 21.3,5.6V3h0.8v2.66c0,0.3 0.08,0.54 0.23,0.71C22.48,6.54 22.7,6.62 23,6.62c0.3,0 0.52,-0.08 0.67,-0.25c0.15,-0.17 0.23,-0.41 0.23,-0.71V3h0.8v2.6c0,0.36 -0.07,0.67 -0.2,0.94s-0.33,0.48 -0.58,0.62C23.65,7.32 23.35,7.39 23,7.39z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M22.99,13.1c-0.39,0 -0.73,-0.09 -1.03,-0.28c-0.3,-0.19 -0.53,-0.45 -0.7,-0.79C21.08,11.7 21,11.3 21,10.85c0,-0.46 0.08,-0.85 0.25,-1.19c0.17,-0.34 0.41,-0.6 0.71,-0.78c0.3,-0.18 0.65,-0.28 1.04,-0.28c0.31,0 0.59,0.05 0.86,0.16c0.26,0.11 0.48,0.27 0.65,0.48c0.18,0.21 0.28,0.48 0.32,0.8l-0.83,0.14c-0.06,-0.26 -0.17,-0.46 -0.35,-0.6C23.49,9.44 23.27,9.37 23,9.37c-0.22,0 -0.42,0.06 -0.61,0.17c-0.18,0.11 -0.32,0.28 -0.43,0.5c-0.1,0.22 -0.16,0.49 -0.16,0.81c0,0.32 0.05,0.58 0.16,0.8s0.25,0.39 0.43,0.5c0.18,0.11 0.38,0.17 0.61,0.17c0.26,0 0.47,-0.07 0.65,-0.21c0.18,-0.14 0.3,-0.34 0.35,-0.59l0.85,0.09c-0.06,0.29 -0.17,0.54 -0.32,0.77c-0.15,0.22 -0.36,0.39 -0.61,0.52C23.66,13.03 23.35,13.1 22.99,13.1z"/>
-</vector>
diff --git a/packages/SettingsLib/res/drawable/ic_5g_uw_mobiledata.xml b/packages/SettingsLib/res/drawable/ic_5g_uw_mobiledata.xml
deleted file mode 100644
index ca47b6ff2edb..000000000000
--- a/packages/SettingsLib/res/drawable/ic_5g_uw_mobiledata.xml
+++ /dev/null
@@ -1,33 +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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="27dp"
- android:height="16dp"
- android:viewportWidth="27.0"
- android:viewportHeight="16.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M5.3,13.22c-0.57,0 -1.1,-0.11 -1.58,-0.34c-0.48,-0.22 -0.87,-0.55 -1.18,-0.98C2.24,11.47 2.06,10.93 2,10.3l1.48,-0.2c0.07,0.5 0.25,0.92 0.56,1.25c0.32,0.32 0.74,0.48 1.26,0.48c0.57,0 1.02,-0.18 1.34,-0.55c0.33,-0.37 0.49,-0.87 0.49,-1.48c0,-0.61 -0.16,-1.09 -0.49,-1.46C6.32,7.96 5.88,7.78 5.32,7.78C5,7.78 4.7,7.85 4.42,8C4.15,8.14 3.93,8.33 3.76,8.56L2.28,7.92l0.6,-4.94h5.26v1.36H4.1L3.72,7.02l0.08,0.03C4,6.87 4.25,6.73 4.55,6.62c0.3,-0.12 0.63,-0.18 1.01,-0.18c0.6,0 1.13,0.14 1.6,0.41C7.62,7.11 7.98,7.5 8.24,8c0.27,0.5 0.41,1.1 0.41,1.79c0,0.66 -0.14,1.26 -0.43,1.78c-0.28,0.51 -0.67,0.92 -1.18,1.22C6.55,13.08 5.97,13.22 5.3,13.22z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M14.51,13.22c-0.88,0 -1.66,-0.21 -2.34,-0.64c-0.67,-0.44 -1.2,-1.05 -1.6,-1.83C10.19,9.95 10,9.03 10,7.99c0,-1.05 0.21,-1.96 0.62,-2.74c0.41,-0.79 0.97,-1.4 1.67,-1.83c0.7,-0.44 1.5,-0.66 2.39,-0.66c1.09,0 2.01,0.25 2.74,0.76c0.75,0.5 1.22,1.21 1.41,2.11l-1.47,0.39c-0.17,-0.56 -0.48,-1 -0.94,-1.32c-0.45,-0.33 -1.03,-0.49 -1.75,-0.49c-0.57,0 -1.09,0.14 -1.57,0.43c-0.48,0.29 -0.85,0.71 -1.13,1.27s-0.42,1.25 -0.42,2.07c0,0.81 0.14,1.5 0.41,2.07c0.28,0.56 0.65,0.98 1.11,1.27c0.47,0.29 0.98,0.43 1.54,0.43c0.57,0 1.06,-0.11 1.47,-0.34c0.42,-0.23 0.75,-0.55 0.99,-0.94c0.25,-0.4 0.41,-0.85 0.46,-1.36h-2.88V7.79h4.37c0,0.87 0,1.74 0,2.6c0,0.87 0,1.74 0,2.6H17.6v-1.4h-0.08c-0.28,0.49 -0.67,0.88 -1.18,1.18C15.85,13.07 15.24,13.22 14.51,13.22z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M23,7.39c-0.53,0 -0.94,-0.16 -1.25,-0.47C21.45,6.6 21.3,6.16 21.3,5.6V3h0.8v2.66c0,0.3 0.08,0.54 0.23,0.71C22.48,6.54 22.7,6.62 23,6.62c0.3,0 0.52,-0.08 0.67,-0.25c0.15,-0.17 0.23,-0.41 0.23,-0.71V3h0.8v2.6c0,0.36 -0.07,0.67 -0.2,0.94s-0.33,0.48 -0.58,0.62C23.65,7.32 23.35,7.39 23,7.39z"/>
- <path
- android:fillColor="#FF000000"
- android:pathData="M21.41,13L20.3,8.7h0.73l0.64,2.78l0.07,0.38h0.04l0.09,-0.38l0.81,-2.78h0.66l0.79,2.78l0.09,0.37h0.04l0.07,-0.37l0.65,-2.78h0.72L24.59,13H23.9l-0.78,-2.84l-0.1,-0.41h-0.04l-0.1,0.41L22.08,13H21.41z"/>
-</vector>
diff --git a/packages/SettingsLib/res/layout/preference_category_divider.xml b/packages/SettingsLib/res/layout/preference_category_divider.xml
deleted file mode 100644
index 6644eecd71fa..000000000000
--- a/packages/SettingsLib/res/layout/preference_category_divider.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?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.
- -->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/two_target_divider"
- android:layout_height="wrap_content"
- android:layout_width="match_parent"
- android:gravity="start|center_vertical"
- android:orientation="horizontal">
- <View
- android:layout_height="1dp"
- android:layout_width="match_parent"
- android:background="?android:attr/dividerHorizontal" />
-</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/res/layout/preference_category_material_settings_with_divider.xml b/packages/SettingsLib/res/layout/preference_category_material_settings_with_divider.xml
deleted file mode 100644
index 5b5d474b13f9..000000000000
--- a/packages/SettingsLib/res/layout/preference_category_material_settings_with_divider.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?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.
- -->
-
-<!-- Similar to preference_category_material_settings.xml, except that this always adds
- a divider above category. -->
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <include layout="@layout/preference_category_divider"/>
- <include layout="@layout/preference_category_material"/>
-</LinearLayout>
diff --git a/packages/SettingsLib/res/layout/restricted_popup_menu_item.xml b/packages/SettingsLib/res/layout/restricted_popup_menu_item.xml
deleted file mode 100644
index 52d77758051f..000000000000
--- a/packages/SettingsLib/res/layout/restricted_popup_menu_item.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 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.
-*/
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:minHeight="?android:attr/listPreferredItemHeightSmall"
- android:paddingEnd="16dp"
- android:paddingStart="16dp">
-
- <TextView
- android:id="@+id/text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:ellipsize="marquee"
- android:textAppearance="?android:attr/textAppearanceListItemSmall"
- android:textColor="?android:attr/textColorAlertDialogListItem" />
-
-</RelativeLayout>
diff --git a/packages/SettingsLib/res/layout/settings_dialog_title.xml b/packages/SettingsLib/res/layout/settings_dialog_title.xml
deleted file mode 100644
index 1e065e0dbb4d..000000000000
--- a/packages/SettingsLib/res/layout/settings_dialog_title.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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
- -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/settings_title_panel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <LinearLayout
- android:id="@+id/settings_title_template"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center"
- android:paddingStart="?android:attr/dialogPreferredPadding"
- android:paddingEnd="?android:attr/dialogPreferredPadding"
- android:paddingTop="@*android:dimen/dialog_padding_top_material">
-
- <ImageView
- android:id="@+id/settings_icon"
- android:layout_width="24dip"
- android:layout_height="24dip"
- android:layout_marginBottom="12dip" />
-
- <TextView
- android:id="@+id/settings_title"
- android:singleLine="true"
- android:ellipsize="end"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="center"
- style="?android:attr/windowTitleStyle" />
- </LinearLayout>
-
- <Space
- android:id="@+id/settings_title_divider"
- android:visibility="gone"
- android:layout_width="match_parent"
- android:layout_height="@*android:dimen/dialog_title_divider_material" />
-</LinearLayout> \ No newline at end of file
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 9578045fdfe0..a39a2b9cc12a 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Ontkoppel"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Gedeaktiveer"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP-opstelling het misluk"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Nie gekoppel nie weens laegehalte-netwerk"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi-verbinding het misluk"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Stawingsprobleem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Kan nie koppel nie"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Kan nie aan \"<xliff:g id="AP_NAME">%1$s</xliff:g>\" koppel nie"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sal nie outomaties koppel nie"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang nie"</string>
<string name="saved_network" msgid="7143698034077223645">"Gestoor deur <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Gekoppel aan beperkte netwerk"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Outomaties deur %1$s gekoppel"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Outomaties deur netwerkgraderingverskaffer gekoppel"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Gekoppel via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Gekoppel via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Beskikbaar via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tik om aan te meld"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Geen internet nie"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Geen internet nie"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Aanmelding word vereis"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Toegangspunt is tydelik vol"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Gekoppel via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Beskikbaar via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Maak tans <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> oop"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Kon nie koppel nie"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Voltooi tans aanmelding …"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Kon nie aanmelding voltooi nie. Tik om weer te probeer."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Aanmelding is voltooi. Koppel tans …"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Baie stadig"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Stadig"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Middelmatig"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Vinnig"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Baie vinnig"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Verval"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Verbind tans…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Gekoppel (geen foon nie)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Gekoppel (geen media nie)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Gekoppel (geen boodskaptoegang nie)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Gekoppel (geen foon of media nie)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Gekoppel, battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Gekoppel (geen foon nie), battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 494ec1e53a64..67f43c5a376b 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"ተቋርጧል"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ተሰናክሏል"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"የአይ.ፒ. ውቅረት መሰናከል"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"በዝቅተኛ አውታረ መረብ ምክንያት አልተገናኘም"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"የWiFi ግንኙነት መሰናከል"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"የማረጋገጫ ችግር"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"መገናኘት አልተቻለም"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"ወደ «<xliff:g id="AP_NAME">%1$s</xliff:g>» ማገናኘት አይቻልም"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"በራስ-ሰር አይገናኝም"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ምንም የበይነመረብ መዳረሻ ያለም"</string>
<string name="saved_network" msgid="7143698034077223645">"የተቀመጠው በ<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ከሚለካ አውታረ መረብ ጋር ተገናኝቷል"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"በ%1$s በኩል በራስ-ሰር ተገናኝቷል"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"በአውታረ መረብ ደረጃ ሰጪ አቅራቢ በኩል በራስ-ሰር ተገናኝቷል"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"በ%1$s በኩል መገናኘት"</string>
<string name="connected_via_app" msgid="3532267661404276584">"በ <xliff:g id="NAME">%1$s</xliff:g> በኩል ተገናኝተዋል"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"በ%1$s በኩል የሚገኝ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ለመመዝገብ መታ ያድርጉ"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ምንም በይነመረብ የለም"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ምንም በይነመረብ የለም"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"ወደ መለያ መግባት ያስፈልጋል"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"የመዳረሻ ነጥብ ለጊዜው ሞልቷል"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"በ%1$s በኩል ተገናኝቷል"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"በ%1$s በኩል የሚገኝ"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>ን በመክፈት ላይ"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"መገናኘት አልተቻለም"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"መመዝገብን በማጠናቀቅ ላይ…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"መመዝገብን ማጠናቀቅ አልተቻለም። እንደገና ለመሞከር መታ ያድርጉ።"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ምዝገባ ተጠናቋል። በማገናኘት ላይ…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"በጣም ቀርፋፋ"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"አዘግይ"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"እሺ"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"መካከለኛ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ፈጣን"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"እጅግ በጣም ፈጣን"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"ጊዜው አልፏል"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"በማገናኘት ላይ..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"ተገናኝቷል (ምንም ስልክ የለም)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"ተገናኝቷል (ምንም ማህደረ መረጃ የለም)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"ተገናኝቷል (ምንም የመልዕክት መዳረሻ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"ተገናኝቷል (ምንም ስልክ ወይም ማህደረ መረጃ የለም)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"ተገናኝቷል፣ ባትሪ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"ተገናኝቷል (ምንም ስልክ የለም)፣ ባትሪ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 5171e6f0e332..705cccbbd0d7 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"غير متصلة"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"غير مفعّلة"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"‏تعذّر إعداد عنوان IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"الجهاز غير متصل بسبب انخفاض جودة الشبكة"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"‏تعذّر اتصال WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"حدثت مشكلة في المصادقة"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"تعذَّر الاتصال"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"تعذَّر الاتصال بـ \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"لن يتم الاتصال تلقائيًا"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"لا يتوفّر اتصال بالإنترنت"</string>
<string name="saved_network" msgid="7143698034077223645">"تم الحفظ بواسطة <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"تم الاتصال بشبكة تفرض تكلفة استخدام."</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"‏تم الاتصال تلقائيًا عبر %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"تم الاتصال تلقائيًا عبر مقدم خدمة تقييم الشبكة"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"‏تم الاتصال عبر %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"تم الاتصال عبر <xliff:g id="NAME">%1$s</xliff:g>."</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"‏متوفرة عبر %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"انقر للاشتراك."</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"لا يتوفر اتصال إنترنت."</string>
<string name="private_dns_broken" msgid="1984159464346556931">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"لا يتوفر اتصال إنترنت."</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"يلزم تسجيل الدخول"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"نقطة الوصول ممتلئة مؤقتًا"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"‏تم الاتصال عبر %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"‏متوفرة عبر %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"فتح <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"تعذّر الاتصال."</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"جارٍ إكمال الاشتراك…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"تعذّر إكمال الاشتراك. انقر للمحاولة مرة أخرى."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"اكتمل الاشتراك. جارٍ الاتصال…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"بطيئة جدًا"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"بطيئة"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"جيدة"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"متوسطة"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"سريعة"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"سريعة جدًا"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"منتهية الصلاحية"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"جارٍ الاقتران..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"الجهاز متصل (بدون هاتف)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"الجهاز متصل (بدون وسائط)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"الجهاز متصل (بدون وصول إلى الرسائل)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"الجهاز متصل (بدون هاتف أو وسائط)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"الجهاز متصل، ومستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"الجهاز متصل (بدون هاتف)، ومستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index d7d09dd76736..ba0d30283443 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"সংযোগ বিচ্ছিন্ন"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"নিষ্ক্ৰিয় হৈ আছে"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP কনফিগাৰেশ্বন বিফল হৈছে"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"নিম্নমানৰ নেটৱৰ্কৰ বাবে সংযোগ কৰা হোৱা নাই"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"ৱাই-ফাই সংযোগ বিফল হৈছে"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"সত্যাপন কৰাত সমস্যা হৈছে"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"সংযোগ কৰিব নোৱাৰে"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'ৰ সৈতে সংযোগ কৰিব পৰা নাই"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"স্বয়ংক্ৰিয়ভাৱে সংযোগ নহ’ব"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ইণ্টাৰনেট সংযোগ নাই"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>এ ছেভ কৰিছে"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"নিৰিখ-নিৰ্দিষ্ট নেটৱৰ্কৰ সৈতে সংযোগ কৰা হৈছে"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s মাধ্যমেদি স্বয়ংক্ৰিয়ভাৱে সংযোগ কৰা হৈছে"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ’ল"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ৰ মাধ্যমেদি সংযোগ কৰা হৈছে"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>ৰ জৰিয়তে সংযুক্ত"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ছাইন আপ কৰিবলৈ টিপক"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ইণ্টাৰনেট সংযোগ নাই"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"ব্যক্তিগত DNS ছাৰ্ভাৰ এক্সেছ কৰিব নোৱাৰি"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ইণ্টাৰনেট সংযোগ নাই"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"ছাইন ইন কৰা দৰকাৰী"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"একচেছ পইণ্ট কিছু সময়ৰ বাবে পূৰ্ণ হৈ আছে"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$sৰ যোগেৰে সংযোজিত"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> খুলি থকা হৈছে"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"সংযোগ কৰিব পৰা নগ’ল"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"ছাইন আপ সম্পূৰ্ণ কৰি থকা হৈছে…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"ছাইন আপ সম্পূৰ্ণ কৰিব পৰা নগ’ল। আকৌ চেষ্টা কৰিবলৈ টিপক।"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ছাইন আপ সম্পূৰ্ণ হৈছে সংযোগ কৰি থকা হৈছে…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"অতি লেহেম"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"লেহেমীয়া"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ঠিক"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"মধ্যমীয়া"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"দ্ৰুত"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"অতি দ্ৰুত"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"ম্যাদ উকলিছে"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"যোৰা লগোৱা হৈছে…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"সংযোগ কৰা হ’ল (ফ\'ন নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"সংযোগ কৰা হ’ল (মিডিয়া নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"সংযোগ কৰা হ’ল (বাৰ্তাত প্ৰৱেশাধিকাৰ নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"সংযোগ কৰা হ’ল (কোনো ফ\'ন বা মিডিয়া নাই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"সংযোগ কৰা হ’ল, বেটাৰীৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"সংযোগ কৰা হ’ল (ফ\'ন নাই), বেটাৰীৰ স্তৰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 0d2f883fca60..33efb4fdeb98 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Bağlantı kəsildi"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Deaktiv"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP Konfiqurasiya Uğursuzluğu"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Şəbəkə keyfiyyəti aşağı olduğuna görə qoşulmadı"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi Bağlantı Uğursuzluğu"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Autentifikasiya problemi"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Qoşulmaq mümkün deyil"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\"<xliff:g id="AP_NAME">%1$s</xliff:g>\" şəbəkəsinə qoşulmaq mümkün deyil"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik qoşulmayacaq"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"İnternet girişi yoxdur"</string>
<string name="saved_network" msgid="7143698034077223645">"Yadda saxlayan: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ölçülən şəbəkəyə qoşulub"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzərindən avtomatik qoşuldu"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Avtomatik olaraq şəbəkə reytinq provayderi ilə qoşuludur"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s vasitəsilə qoşuludur"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ilə qoşulub"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s vasitəsilə əlçatandır"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Qeydiyyatdan keçmək üçün klikləyin"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"İnternet yoxdur"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Özəl DNS serverinə giriş mümkün deyil"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"İnternet yoxdur"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Giriş tələb olunur"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Giriş nöqtəsi müvəqqəti olaraq doludur"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s ilə qoşuludur"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s vasitəsilə əlçatandır"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> açılır"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Qoşulmaq mümkün olmadı"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Qeydiyyat tamamlanır…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Qeydiyyat tamamlanmadı. Yenidən cəhd etmək üçün klikləyin."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Qeydiyyat tamamlandı. Qoşulur…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Çox Yavaş"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Yavaş"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Orta"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Sürətli"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Çox Sürətli"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vaxtı keçib"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Birləşdirilir..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Qoşuludur (telefon yoxdur)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Qoşuludur (media yoxdur)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Qoşuludur (mesaj girişi yoxdur)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Qoşuludur (telefon və ya media yoxdur)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Qoşuludur, batareya <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Qoşuludur (telefon yoxdur), batareya <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 927493272bec..108a1365421f 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Veza je prekinuta"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP konfiguracija je otkazala"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Nije povezano zbog lošeg kvaliteta mreže"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi veza je otkazala"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problem sa potvrdom identiteta"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Povezivanje nije uspelo"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Povezivanje sa „<xliff:g id="AP_NAME">%1$s</xliff:g>“ nije uspelo"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automatsko povezivanje nije uspelo"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Sačuvao/la je <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezani ste na mrežu sa ograničenjem"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano preko %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano preko dobavljača ocene mreže"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Veza je uspostavljena preko pristupne tačke %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Povezano preko: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Dostupna je preko pristupne tačke %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite da biste se registrovali"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Nema interneta"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Pristup privatnom DNS serveru nije uspeo"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema interneta"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Treba da se prijavite"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Pristupna tačka je privremeno zauzeta"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Povezano preko %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Dostupno preko %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Otvara se <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Povezivanje nije uspelo"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Registracija se dovršava…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Dovršavanje registracije nije uspelo. Dodirnite da biste probali ponovo."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registracija je dovršena. Povezuje se…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Veoma spora"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Spora"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Potvrdi"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Srednja"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Brza"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veoma brza"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Isteklo"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Uparivanje..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Povezano (bez telefona): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Povezano (bez medija): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Povezano je (bez pristupa porukama): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Povezano (bez telefona ili medija): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Povezano, nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Povezano (bez telefona), nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 2f1b7d71c3ae..5a7020216594 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Адключана"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Адключана"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Збой канфігурацыі IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Няма падключэння з-за нізкай якасці сеткі"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Збой падлучэння Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Праблема аўтэнтыфікацыі"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Немагчыма падключыцца"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Немагчыма падключыцца да сеткі \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не будзе аўтаматычна падключацца"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Няма доступу да інтэрнэту"</string>
<string name="saved_network" msgid="7143698034077223645">"Захавана праз: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Падключана да сеткі з падлікам трафіка"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Аўтаматычна падключана праз %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аўтаматычна падключана праз пастаўшчыка паслугі ацэнкі сеткі"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Падключана праз %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Падключана праз праграму \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Даступна праз %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Націсніце, каб зарэгістравацца"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Няма падключэння да інтэрнэту"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Няма падключэння да інтэрнэту"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Трэба выканаць уваход"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Пункт доступу часова заняты"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Падключана праз %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Даступна праз %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Адкрываецца <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Не ўдалося падключыцца"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Рэгістрацыя завяршаецца…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Не ўдалося выканаць рэгістрацыю. Дакраніцеся, каб паўтарыць спробу."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Рэгістрацыя завершана. Ідзе падключэнне…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Вельмі павольная"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Павольная"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ОК"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Сярэдняя"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Хуткая"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Вельмі хуткая"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Тэрмін скончыўся"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Спалучэнне..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Падключана прылада <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без званкоў)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Падключана прылада <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без аўдыя)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Падключана прылада <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без паведамленняў)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Падкл. прылада <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без званкоў і аўдыя)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Падключана прылада <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>. Узровень зараду яе акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Падключана прылада <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (без званкоў). Узровень зараду яе акумулятара: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index c839f531e065..7e97dd366bb8 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Няма връзка"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Деактивирани"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Неуспешно конфигуриране на IP адреса"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Не е установена връзка поради ниското качество на мрежата"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Неуспешна връзка с Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Проблем при удостоверяването"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Не може да се установи връзка"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Не може да се установи връзка с/ъс <xliff:g id="AP_NAME">%1$s</xliff:g>"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Няма да се свърже автоматично"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Няма достъп до интернет"</string>
<string name="saved_network" msgid="7143698034077223645">"Запазено от <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Установена е връзка с мрежа с отчитане"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично е установена връзка чрез %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично е установена връзка чрез доставчик на услуги за оценяване на мрежите"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Установена е връзка през „%1$s“"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Установена е връзка през <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Мрежата е достъпна през „%1$s“"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Докоснете, за да се регистрирате"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Няма интернет"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Не може да се осъществи достъп до частния DNS сървър"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Няма връзка с интернет"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Изисква се вход в профила"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Точката за достъп временно е пълна"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Установена е връзка през %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Мрежата е достъпна през %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> се отваря"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Не можа да се установи връзка"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Регистрацията се завършва…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Регистрацията не можа да бъде завършена. Докоснете, за да опитате отново."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Регистрацията е завършена. Установява се връзка…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Много бавна"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Бавна"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ОK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Средна"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Бърза"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Много бърза"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Изтекло"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Сдвояване..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Свързано (без телефон)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Свързано (без мултимедия)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Свързано (без достъп до съобщенията)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Свързано (без телефон или мултимедия)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Свързано, батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Свързано (без телефон), батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 7a71dc871d98..023be24d29a2 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"কানেকশন নেই"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"অক্ষম হয়েছে"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP কনফিগারেশনের ব্যর্থতা"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"খারাপ নেটওয়ার্কের কারণে কানেক্ট নয়"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"ওয়াই ফাই সংযোগের ব্যর্থতা"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"যাচাইকরণ সমস্যা"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"কানেক্ট স্থাপন করা যাচ্ছে না"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'এ যোগ করা যায়নি"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"অটোমেটিক কানেক্ট করবে না"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ইন্টারনেট অ্যাক্সেস নেই"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সেভ করা"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"মিটার্ড নেটওয়ার্কের সঙ্গে কানেক্ট করা"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"স্বয়ংক্রিয়ভাবে %1$s এর মাধ্যমে কানেক্ট হয়েছে"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটওয়ার্কের রেটিং প্রদানকারীর মাধ্যমে অটোমেটিক কানেক্ট"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s মাধ্যমে কানেক্ট হয়েছে"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>-এর মাধ্যমে কানেক্ট করা আছে"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s এর মাধ্যমে উপলব্ধ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"সাইন-আপ করতে ট্যাপ করুন"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ইন্টারনেট কানেকশন নেই"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ইন্টারনেট কানেকশন নেই"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"সাইন-ইন করা দরকার"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"এই মুহূর্তে অ্যাক্সেস পয়েন্টের কোনও কানেকশন ফাঁকা নেই"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s এর মাধ্যমে কানেক্ট হয়েছে"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s এর মাধ্যমে পাওয়া যাচ্ছে"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> খোলা হচ্ছে"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"কানেক্ট করা যায়নি"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"সাইন-আপ সম্পূর্ণ করা হচ্ছে…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"সাইন-আপ করা যায়নি। আবার চেষ্টা করতে ট্যাপ করুন।"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"সাইন-আপ করা হয়ে গেছে। কানেক্ট করা হচ্ছে…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"খুব ধীরে"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"ধীরে"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ঠিক আছে"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"মাঝারি"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"দ্রুত"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"খুব দ্রুত"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"মেয়াদ শেষ হয়ে গেছে"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"যুক্ত করা হচ্ছে..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"কানেক্ট করা আছে (ফোনের অডিও ছাড়া)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"কানেক্ট করা আছে (মিডিয়ার অডিও ছাড়া)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"কানেক্ট করা আছে (মেসেজে অ্যাকসেস নেই)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"কানেক্ট করা আছে (ফোনের বা মিডিয়ার অডিও ছাড়া)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"কানেক্ট করা আছে, ব্যাটারি <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"কানেক্ট করা আছে (ফোনের অডিও ছাড়া), ব্যাটারি <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 74325cacdc4d..3cb419033866 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Nije povezano"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Greška u konfiguraciji IP-a"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Niste povezani zbog slabog kvaliteta mreže"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Greška pri povezivanju na WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problem pri autentifikaciji"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nije se moguće povezati"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Nije se moguće povezati na aplikaciju \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se automatski povezati"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Sačuvano: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezani ste s mrežom s naplatom"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano koristeći %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano putem ocjenjivača mreže"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Povezani preko %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Povezano preko <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Dostupan preko %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite za prijavu"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Nema internetske veze"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Nije moguće pristupiti privatnom DNS serveru"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema internetske veze"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Potrebna je prijava"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Pristupna tačka je privremeno puna"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Povezano koristeći %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Dostupna koristeći %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Otvaranje <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Povezivanje nije uspjelo"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Završavanje registracije…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Registraciju nije moguće izvršiti. Dodirnite da pokušate ponovo."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registracija je završena. Povezivanje…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Veoma sporo"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Sporo"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Uredu"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Srednja brzina"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Brzo"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veoma brzo"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Isteklo"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Uparivanje…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Povezano (bez telefona)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Povezano (bez medija)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Povezano (bez pristupa porukama)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Povezano (bez telefona ili medija)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Povezano, baterija <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Povezano (bez telefona), baterija <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 94c3d56fc3da..efa29ee1ca38 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconnectada"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desactivat"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Error de configuració d\'IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"No s\'ha connectat a la xarxa perquè la qualitat és baixa"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Error de connexió Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problema d\'autenticació"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"No es pot connectar"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"No es pot connectar a <xliff:g id="AP_NAME">%1$s</xliff:g>"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No es connectarà automàticament"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No hi ha accés a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Desada per <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connectat a una xarxa d\'ús mesurat"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Connectada automàticament a través de: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connectada automàticament a través d\'un proveïdor de valoració de xarxes"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Connectada mitjançant %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Connectat mitjançant <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Disponible mitjançant %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toca per registrar-te"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Sense connexió a Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"No es pot accedir al servidor DNS privat"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sense connexió a Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Cal iniciar la sessió"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"El punt d\'accés està temporalment ple"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Connectat mitjançant %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Disponible mitjançant %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"S\'està obrint <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"No s\'ha pogut connectar"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"S\'està completant el registre…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"No s\'ha pogut completar el registre. Toca per tornar-ho a provar."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"S\'ha completat el registre. S\'està connectant…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Molt lenta"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lenta"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Correcta"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Mitjana"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Ràpida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Molt ràpida"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Caducada"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"S\'està vinculant..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense accés al telèfon)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense contingut multimèdia)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense accés als missatges)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connectat (sense telèfon ni multimèdia)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> connectat, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> connectat (sense accés al telèfon), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria"</string>
@@ -668,7 +658,7 @@
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Si emets <xliff:g id="SWITCHAPP">%1$s</xliff:g> o canvies la sortida, l\'emissió actual s\'aturarà"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"Emet <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
<string name="bt_le_audio_broadcast_dialog_different_output" msgid="2638402023060391333">"Canvia la sortida"</string>
- <string name="back_navigation_animation" msgid="8105467568421689484">"Animacions per a les accions de tornada predictives"</string>
- <string name="back_navigation_animation_summary" msgid="741292224121599456">"Activa animacions del sistema per a la tornada predictiva."</string>
+ <string name="back_navigation_animation" msgid="8105467568421689484">"Animacions de retrocés predictiu"</string>
+ <string name="back_navigation_animation_summary" msgid="741292224121599456">"Activa animacions del sistema de retrocés predictiu."</string>
<string name="back_navigation_animation_dialog" msgid="8696966520944625596">"Aquesta configuració activa animacions del sistema per a accions gestuals predictives. Requereix definir enableOnBackInvokedCallback com a \"true\" en cada aplicació al fitxer de manifest."</string>
</resources>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 6746ddf0bdc0..3736f93e4a14 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Odpojeno"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Vypnuto"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Selhání konfigurace protokolu IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Nejste připojeni, protože síť je příliš slabá"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Selhání připojení Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problém s ověřením"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nelze se připojit"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"K síti <xliff:g id="AP_NAME">%1$s</xliff:g> se nelze připojit"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Připojení nebude automaticky navázáno"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nebyl zjištěn žádný přístup k internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Uloženo uživatelem <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Připojeno k měřené síti"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky připojeno přes poskytovatele %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky připojeno přes poskytovatele hodnocení sítí"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Připojeno prostřednictvím %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Připojeno přes <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Dostupné prostřednictvím %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Klepnutím se zaregistrujete"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Nejste připojeni k internetu"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Nelze získat přístup k soukromému serveru DNS"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nejste připojeni k internetu"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Je vyžadováno přihlášení"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Přístupový bod je dočasně zaplněn"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Připojeno prostřednictvím %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Dostupné prostřednictvím %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Spouštění aplikace <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Nelze se připojit"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Dokončování registrace…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Registraci se nepodařilo dokončit. Klepnutím opakujte akci."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registrace byla dokončena. Připojování…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Velmi pomalá"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Pomalá"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Střední"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rychlá"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Velmi rychlá"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Platnost vypršela"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Párování..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Připojeno k zařízení <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (bez telefonu)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Připojeno k zařízení <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (bez médií)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Připojeno k zařízení <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (bez přístupu ke zprávám)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Připojeno k zařízení <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (bez telefonu a médií)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Připojeno k zařízení <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>, úroveň baterie <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Připojeno k zařízení <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (bez telefonu), úroveň baterie <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index bee5b0f09d76..849604decc5a 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Afbrudt"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Deaktiveret"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP-konfigurationsfejl"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Ingen forbindelse på grund af lav netværkskvalitet"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi-forbindelsesfejl"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problem med godkendelse"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Kan ikke forbinde"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Der kan ikke oprettes forbindelse til \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Der oprettes ikke automatisk forbindelse"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetadgang"</string>
<string name="saved_network" msgid="7143698034077223645">"Gemt af <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Der er oprettet forbindelse til det forbrugsbaserede netværk"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilsluttet via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk forbundet via udbyder af netværksvurdering"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Tilsluttet via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Forbundet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Tilgængelig via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tryk for at registrere dig"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Intet internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Der er ikke adgang til den private DNS-server"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Intet internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Login er påkrævet"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Adgangspunktet er midlertidigt fuldt"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Tilsluttet via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Tilgængelig via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Åbner <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Der kunne ikke oprettes forbindelse"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Fuldfører registrering…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Registreringen kunne ikke fuldføres. Tryk for at prøve igen."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registreringen er fuldført. Opretter forbindelse…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Meget langsom"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Langsom"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Middel"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Hurtig"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Meget hurtig"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Udløbet"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Parrer..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen telefon)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen medier)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen adgang til beskeder)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (ingen telefon eller medier)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Tilsluttet <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (ingen telefon) – batteriniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index 9082210990ec..d736d5977838 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Nicht verbunden"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Deaktiviert"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP-Konfigurationsfehler"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Keine Verbindung aufgrund der geringen Netzwerkqualität"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WLAN-Verbindungsfehler"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Authentifizierungsproblem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Verbindung nicht möglich"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Keine Verbindung zu \"<xliff:g id="AP_NAME">%1$s</xliff:g>\" möglich"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kein automatischer Verbindungsaufbau"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Kein Internetzugriff"</string>
<string name="saved_network" msgid="7143698034077223645">"Gespeichert durch <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Mit kostenpflichtigem Netzwerk verbunden"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch über %1$s verbunden"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch über Anbieter von Netzwerkbewertungen verbunden"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Über %1$s verbunden"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Verbunden über <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Verfügbar über %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Zum Anmelden tippen"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Kein Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Kein Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Anmeldung erforderlich"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Zugangspunkt vorübergehend voll belegt"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Über %1$s verbunden"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Verfügbar über %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> wird geöffnet"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Verbindung nicht möglich"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Anmeldung wird abgeschlossen…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Anmeldung konnte nicht abgeschlossen werden. Tippe, um es noch einmal zu versuchen."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Anmeldung abgeschlossen. Verbindung wird hergestellt…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Sehr langsam"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Langsam"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Mittel"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Mittel"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Schnell"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Sehr schnell"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Abgelaufen"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Wird gekoppelt…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden (kein Telefon-Audio)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden (kein Medien-Audio)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden (kein Nachrichtenzugriff)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Mit <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> verbunden (weder Telefon- noch Medien-Audio)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Mit <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> verbunden, Akkustand bei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Mit <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> verbunden (kein Telefon-Audio), Akkustand bei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 37be5b09876b..e76b933e06ff 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Αποσυνδεδεμένο"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Απενεργοποιημένο"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Αποτυχία διαμόρφωσης διεύθυνσης IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Δεν υπάρχει σύνδεση λόγω χαμηλής ποιότητας δικτύου"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Αποτυχία σύνδεσης Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Πρόβλημα ελέγχου ταυτότητας"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Αδυναμία σύνδεσης"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Δεν είναι δυνατή η σύνδεση στο δίκτυο \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Δεν θα συνδεθεί αυτόματα"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Δεν υπάρχει πρόσβαση στο διαδίκτυο"</string>
<string name="saved_network" msgid="7143698034077223645">"Αποθηκεύτηκε από <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Σύνδεση σε δίκτυο με ογκοχρέωση"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Συνδέθηκε αυτόματα μέσω %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Συνδέθηκε αυτόματα μέσω παρόχου αξιολόγησης δικτύου"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Συνδέθηκε μέσω %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Συνδέθηκε μέσω <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Διαθέσιμο μέσω %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Πατήστε για εγγραφή"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Χωρίς σύνδεση στο διαδίκτυο"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Δεν υπάρχει σύνδεση στο διαδίκτυο"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Απαιτείται σύνδεση"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Το σημείο πρόσβασης είναι προσωρινά πλήρες"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Συνδέθηκε μέσω %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Διαθέσιμο μέσω %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Άνοιγμα <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Δεν ήταν δυνατή η σύνδεση"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Ολοκλήρωση εγγραφής…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Δεν ήταν δυνατή η ολοκλήρωση της εγγραφής. Πατήστε για να δοκιμάσετε ξανά."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Η εγγραφή ολοκληρώθηκε. Σύνδεση…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Πολύ αργή"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Αργή"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ΟΚ"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Μέτρια"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Γρήγορη"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Πολύ γρήγορη"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Έληξε"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Σύζευξη..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Σε σύνδεση (χωρίς τηλέφωνο)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Σε σύνδεση (χωρίς μέσα)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Σε σύνδεση (χωρίς πρόσβαση σε μηνύματα)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Σε σύνδεση (χωρίς τηλέφωνο ή μέσα)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Σε σύνδεση, μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Σε σύνδεση (χωρίς τηλέφωνο), μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index cc24ba4c63fd..09bc0656e970 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Disconnected"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP Configuration Failure"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Not connected due to low quality network"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi Connection Failure"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Authentication problem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Can\'t connect"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Can\'t connect to \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string>
<string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"No Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Sign-in required"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Access point temporarily full"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Connected via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Available via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Opening <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Couldn’t connect"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Completing sign-up…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Couldn’t complete sign-up. Tap to try again"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Sign-up complete. Connecting…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Very slow"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Slow"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Medium"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Fast"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Very fast"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expired"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Pairing…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Connected (no phone)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Connected (no media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Connected (no message access)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Connected (no phone or media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Connected, battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Connected (no phone), battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index ef72b120830e..77b20a630461 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Disconnected"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP Configuration Failure"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Not connected due to low quality network"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi Connection Failure"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Authentication problem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Can\'t connect"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Can\'t connect to \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string>
<string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"No Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Sign-in required"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Access point temporarily full"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Connected via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Available via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Opening <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Couldn’t connect"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Completing sign-up…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Couldn’t complete sign-up. Tap to try again"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Sign-up complete. Connecting…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Very slow"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Slow"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Medium"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Fast"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Very fast"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expired"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Pairing…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Connected (no phone)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Connected (no media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Connected (no message access)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Connected (no phone or media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Connected, battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Connected (no phone), battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index cc24ba4c63fd..09bc0656e970 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Disconnected"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP Configuration Failure"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Not connected due to low quality network"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi Connection Failure"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Authentication problem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Can\'t connect"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Can\'t connect to \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string>
<string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"No Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Sign-in required"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Access point temporarily full"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Connected via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Available via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Opening <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Couldn’t connect"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Completing sign-up…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Couldn’t complete sign-up. Tap to try again"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Sign-up complete. Connecting…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Very slow"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Slow"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Medium"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Fast"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Very fast"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expired"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Pairing…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Connected (no phone)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Connected (no media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Connected (no message access)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Connected (no phone or media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Connected, battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Connected (no phone), battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index cc24ba4c63fd..09bc0656e970 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Disconnected"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Disabled"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP Configuration Failure"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Not connected due to low quality network"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi Connection Failure"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Authentication problem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Can\'t connect"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Can\'t connect to \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string>
<string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"No Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Sign-in required"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Access point temporarily full"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Connected via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Available via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Opening <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Couldn’t connect"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Completing sign-up…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Couldn’t complete sign-up. Tap to try again"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Sign-up complete. Connecting…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Very slow"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Slow"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Medium"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Fast"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Very fast"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expired"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Pairing…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Connected (no phone)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Connected (no media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Connected (no message access)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Connected (no phone or media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Connected, battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Connected (no phone), battery <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 0fedf4bee7fc..f82a7573cec5 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‎‏‏‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‎‏‎Disconnected‎‏‎‎‏‎"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‎‎‏‏‎‏‏‎‎‎‎‎‎‎‎‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‎‎‏‏‏‎‏‏‎‎‏‏‏‏‏‏‏‏‎‎‏‏‏‏‎Disabled‎‏‎‎‏‎"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‎‏‎‏‏‏‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‎‎‎‎‏‎IP Configuration Failure‎‏‎‎‏‎"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‎‎‎‏‎‏‎‎‏‏‏‏‎‏‏‏‎‏‏‎‏‏‏‏‎‎‎‎‎‏‏‎‎‎‎‏‏‎‎‏‏‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎Not connected due to low quality network‎‏‎‎‏‎"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‏‎‎‏‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‏‏‎‎‎WiFi Connection Failure‎‏‎‎‏‎"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‎‏‏‎‎‎‏‎‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‏‏‎‏‎‎‏‏‏‏‎‏‏‏‏‎‎‏‎‎Authentication problem‎‏‎‎‏‎"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‎‏‎‏‏‎‏‏‏‏‏‎‎‏‏‎‎‏‏‏‎‏‎‏‎‎‏‎‎‎‎‏‎‏‏‎‏‎‎‎‏‏‎‏‎‎‏‏‏‎‎‎‏‏‏‏‎Can\'t connect‎‏‎‎‏‎"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‎‎‎‎‏‏‏‎‏‏‏‎‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‎Can\'t connect to \'‎‏‎‎‏‏‎<xliff:g id="AP_NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎\'‎‏‎‎‏‎"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‎‏‎‏‏‎‏‏‎‎‏‏‎‏‏‏‎‎‎‎‏‎‏‏‎‎‎‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‏‎‏‎‏‎‎‎‎‎‏‎‏‎‎‎‎‏‏‎Won\'t automatically connect‎‏‎‎‏‎"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‎‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎‏‏‎‎‏‎‎‏‎‎‎‏‎‎‎‏‎‎‎‎‏‎‏‏‎‏‏‎No internet access‎‏‎‎‏‎"</string>
<string name="saved_network" msgid="7143698034077223645">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‏‏‎‎‎‎‎‏‏‏‎‎‎‎‎‏‏‎‏‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‏‎‎‏‎‏‎‏‏‎‏‏‏‎‏‎Saved by ‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‎‏‏‎‏‎‎‏‎‎‏‎‏‎‏‏‎‎‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎‎‎‏‏‎‏‎‏‎Connected to metered network‎‏‎‎‏‎"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‏‏‎‎‎‏‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‏‎‎‏‏‎‏‏‏‎‎‎‎‎‏‏‎‏‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎Automatically connected via %1$s‎‏‎‎‏‎"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‏‎‎‏‏‏‏‎‏‎‏‎‏‏‎‎‎‎‎‏‏‎‏‎‎‎‎‎‏‏‎‏‎‏‏‎‎‎‏‎‎‏‎‎‏‏‏‏‎‎‏‏‎‏‎Automatically connected via network rating provider‎‏‎‎‏‎"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‏‎‏‎‏‏‎‎‏‏‏‎‏‎‎‎‎‎‏‎‏‏‏‎‎‏‏‏‎‎‏‏‏‏‎‏‏‏‏‎‏‏‎‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎Connected via %1$s‎‏‎‎‏‎"</string>
<string name="connected_via_app" msgid="3532267661404276584">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎Connected via ‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‎Available via %1$s‎‏‎‎‏‎"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‏‏‎Tap to sign up‎‏‎‎‏‎"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‏‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‏‏‎‎‎‎‎‏‎‏‎‏‏‎‎‎‎‏‏‏‎‏‏‎‎No internet‎‏‎‎‏‎"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎Private DNS server cannot be accessed‎‏‎‎‏‎"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎No internet‎‏‎‎‏‎"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‏‎‎‎‎‏‎‎‎‏‏‎‏‎‎‎‏‎‏‏‏‎‎‎‏‏‎‎‎‎‎‏‏‏‎‏‎‎‏‎‎‎‎‎‎‏‎‏‎‏‏‎‎‎‏‏‏‎Sign in required‎‏‎‎‏‎"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‎‏‎‏‏‎‎‎‎‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‎‎‏‏‎‏‎‏‏‏‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‎‏‏‏‎Access point temporarily full‎‏‎‎‏‎"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‎‏‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‏‏‎‎‎‎‏‎‏‎‏‏‏‎‏‏‎‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‏‎‏‎‎Connected via %1$s‎‏‎‎‏‎"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‏‎‎‎‏‎‎‎‏‏‎‏‎‎‎‎‏‏‎‎‏‎‎‏‎‏‏‎‏‎‏‏‎‎Available via %1$s‎‏‎‎‏‎"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‏‏‏‎‏‏‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‏‎‎‏‎‏‎‏‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‎‏‎‏‏‎‏‎Opening ‎‏‎‎‏‏‎<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‎‏‎‏‏‎‏‎‏‎‏‎‏‎‎‏‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‏‎‎‎‏‏‎‏‏‎‎‏‏‎‎‏‎Couldn’t connect‎‏‎‎‏‎"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‏‎‎‏‏‎‎‎‏‏‎‎‎‎‏‎‏‎‎‎‏‏‎‎‏‎‏‎‏‏‏‏‎‏‎‏‎Completing sign-up…‎‏‎‎‏‎"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‏‎‎‏‎‎‏‎‏‏‎‎‏‏‎‎‏‎‎‎‎‎‏‎‎‎‎‏‎‎‎‏‎‎‎‏‏‏‎‎‎‎‎‎‏‎Couldn’t complete sign-up. Tap to try again.‎‏‎‎‏‎"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‎‎‎‏‎‏‎‎‏‏‎‏‎‏‎‎‎‎‏‎‎‎‎‎‏‏‏‎‏‏‏‏‎Sign-up complete. Connecting…‎‏‎‎‏‎"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‎‏‎‎‏‎‎‏‏‏‎‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎‏‏‎‎‎‏‎‎Very Slow‎‏‎‎‏‎"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‎‎‎‎‏‏‏‏‎‎‏‎‏‎‏‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‎‏‎‏‏‎‏‏‎‏‎‏‎‎‎‎‏‎Slow‎‏‎‎‏‎"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‎‎‏‎‏‏‏‎‎‏‏‎‏‎‎‎‏‏‏‏‎‏‎‎‎‏‏‏‏‏‎‏‏‏‏‏‎‏‏‏‎‎‎‎OK‎‏‎‎‏‎"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‎‎‎‏‎‎‎‎‏‎‏‎‏‏‎‎‎‏‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‎‎‎‎‎‎Medium‎‏‎‎‏‎"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‎‏‎‎‏‎‏‎‏‏‎‏‎‏‏‏‎‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‎‏‎‏‎‎‎‏‎‎‏‏‎Fast‎‏‎‎‏‎"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‎‏‎‎‎‎‎‎‏‎‎‎‎‎‏‏‎‎‎‎‎‎‎‎‏‎‎‏‏‏‎‎‏‎‏‎‎‎‏‏‎‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‏‏‏‎Very Fast‎‏‎‎‏‎"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‏‏‎‎‎‏‎‏‏‏‎‏‎‏‎‎‎‎‎‏‏‏‏‎‏‎‎‏‏‎‏‏‏‏‎‏‏‎‏‎‏‏‎‎‏‎‎‎‏‎‏‎‏‎‎‏‎Expired‎‏‎‎‏‎"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‏‎‏‏‎‎‏‏‏‏‏‎‏‎‏‏‎‎‏‏‏‎‎‏‎‎‏‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‏‏‏‏‎‎‏‎‏‏‎‏‎‎‎‎‎‎‎Pairing…‎‏‎‎‏‎"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‏‎‏‏‎‎‏‏‎‎‎‎‎‏‏‎‎‏‏‏‎‏‎‏‎‏‎‎‏‎‏‏‏‏‎‏‎‎‎‎‎‏‎‎‏‏‏‎‏‎‎‎Connected (no phone)‎‏‎‎‏‏‎<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‏‎‏‏‎‏‏‏‎‎‎‏‏‏‎‏‎‎‏‏‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‎‏‏‏‏‎‎‎‎‏‎‎‏‎‎‏‏‎‏‏‎‏‎‏‎‎‎Connected (no media)‎‏‎‎‏‏‎<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‏‏‎‏‏‏‎‏‏‏‎‏‏‎‎‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‏‏‏‎‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‎‏‎‎‎‏‎Connected (no message access)‎‏‎‎‏‏‎<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‏‎‎‎‎‎‏‎‎‏‏‎‏‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‏‏‎‏‏‏‏‎‎‏‎‎‏‎‎‎‎‏‎‎‏‎Connected (no phone or media)‎‏‎‎‏‏‎<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‏‎‏‎‏‎‏‎‏‎‏‏‎‏‎‎‏‏‏‎‎‏‏‏‎‏‏‏‎‏‎‎‎‎‏‏‏‎‏‏‏‎‎‏‎‎‏‏‎‏‏‏‎‎Connected, battery ‎‏‎‎‏‏‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‏‎‎‏‏‏‏‎‎‎‎‏‏‎‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‎‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‎‎‏‏‏‎‎‏‏‏‎‎‎Connected (no phone), battery ‎‏‎‎‏‏‎<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‏‎<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index fb976e5ccecf..1a4e307c5377 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectado"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Inhabilitada"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Error de configuración IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"No se estableció conexión debido a la mala calidad de la red"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Error de conexión Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problema de autenticación"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"No se puede establecer la conexión"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"No se puede establecer conexión con \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se conectará automáticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No hay acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a la red de uso medido"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conexión automática mediante %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente mediante proveedor de calificación de red"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Conexión a través de %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Conexión a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Disponible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Presiona para registrarte"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Sin Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"No se puede acceder al servidor DNS privado"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sin Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Acceso obligatorio"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"El punto de acceso está completo temporalmente"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Conexión a través de %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Disponible a través de %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Abriendo <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"No se pudo establecer conexión"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Completando registro…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"No se pudo completar el registro. Presiona para volver a intentarlo."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Se completó el registro. Conectando…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Muy lenta"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lenta"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Aceptar"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Media"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muy rápida"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vencida"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Vinculando..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Conectado (sin teléfono) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Conectado (sin archivos multimedia) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Conectado (sin acceso a mensajes) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Conectado (sin teléfono/multimedia) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Conectado a <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería)"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Conectado (sin teléfono) a <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería)"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 609702324e63..fdf99e0acab8 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectado"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Inhabilitado"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Error de configuración de IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"No conectado debido a la baja calidad de la red"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Error de conexión Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Error de autenticación"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"No se puede establecer conexión"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"No se puede establecer conexión con \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se establecerá conexión automáticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No se ha detectado ningún acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a una red de uso medido"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectada automáticamente a través de %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente a través de un proveedor de valoración de redes"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Conectado a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Disponible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toca para registrarte"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Sin Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"No se ha podido acceder al servidor DNS privado"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sin Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Debes iniciar sesión"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Punto de acceso temporalmente lleno"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Conectado a través de %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Disponible a través de %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Abriendo <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"No se ha podido conectar"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Completando registro…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"No se ha podido completar el registro. Toca para volver a intentarlo."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Se ha completado el registro. Conectando…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Muy lenta"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lenta"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Aceptable"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Media"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muy rápida"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Caducada"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Emparejando…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Conectado a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (sin audio de teléfono)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Conectado (sin audio multimedia) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Conectado a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (sin acceso a mensajes)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Conectado a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (sin audio de teléfono ni multimedia)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Conectado a <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería)"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Conectado (sin audio de teléfono) a <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería)"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 2825c0663881..bcd395cc03a0 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Pole ühendatud"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Keelatud"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP seadistamise ebaõnnestumine"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Võrgu kehva kvaliteedi tõttu ei ühendatud"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi-ühenduse viga"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Autentimise probleem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Ei saa ühendada"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Võrguga „<xliff:g id="AP_NAME">%1$s</xliff:g>” ei saa ühendada"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automaatselt ei ühendata"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Juurdepääs Internetile puudub"</string>
<string name="saved_network" msgid="7143698034077223645">"Salvestas: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ühendatud mahupõhise võrguga"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ühendus loodi automaatselt teenusega %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ühendus loodi automaatselt võrgukvaliteedi hinnangute pakkuja kaudu"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Ühendatud üksuse %1$s kaudu"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Ühendatud võrgu <xliff:g id="NAME">%1$s</xliff:g> kaudu"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Saadaval üksuse %1$s kaudu"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Puudutage registreerumiseks"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Interneti pole"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Privaatsele DNS-serverile ei pääse juurde"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Interneti-ühendus puudub"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Nõutav on sisselogimine"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Pääsupunkt on ajutiselt täis"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Ühendatud operaatori %1$s kaudu"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Saadaval operaatori %1$s kaudu"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Teenuse <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> avamine"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Ühendust ei saanud luua"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Registreerimise lõpuleviimine …"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Registreerimist ei saanud lõpule viia. Puudutage, et uuesti proovida."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registreerimine on lõpule viidud. Ühendamine …"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Väga aeglane"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Aeglane"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Hea"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Keskmine"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Kiire"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Väga kiire"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Aegunud"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Sidumine ..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Ühendatud (telefoni pole)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Ühendatud (meediat pole)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Ühendatud (juurdepääs sõnumitele puudub)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Ühendatud (telefoni ega meediat pole)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Ühendatud, aku <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Ühendatud (telefoni pole), aku <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 765e2f2e8647..8e8150cf5a24 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Deskonektatuta"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desgaituta"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Ezin izan da konfiguratu IP helbidea"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Ez dago konektatuta sarearen kalitate eskasagatik"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Ezin izan da konektatu wifi-sarera"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Autentifikazio-arazoa"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Ezin da konektatu"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Ezin da konektatu \"<xliff:g id="AP_NAME">%1$s</xliff:g>\" sarera"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ez da konektatuko automatikoki"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ezin da konektatu Internetera"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Sare neurtu batera konektatuta"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s bidez automatikoki konektatuta"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikoki konektatuta sare-balorazioen hornitzailearen bidez"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s bidez konektatuta"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> bidez konektatuta"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s bidez erabilgarri"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Sakatu erregistratzeko"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Ez dago Interneteko konexiorik"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Ezin da atzitu DNS zerbitzari pribatua"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ez dago Interneteko konexiorik"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Saioa hasi behar da"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Sarbide-puntua beteta dago aldi baterako"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s bidez konektatuta"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s bidez erabilgarri"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> irekitzen"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Ezin izan da konektatu"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Erregistroa osatzen…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Ezin izan da osatu erregistroa. Berriro saiatzeko, ukitu hau."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Erregistratu da. Konektatzen…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Oso motela"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Motela"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Ados"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Tartekoa"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Bizkorra"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Oso bizkorra"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Iraungita"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Parekatzen…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Konektatuta (telefonoaren audiorik gabe)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Konektatuta (gailuaren audiorik gabe)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Konektatuta (mezuetarako sarbiderik gabe)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Konektatuta (telefonoaren edo gailuaren audiorik gabe)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Konektatuta. Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>."</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Konektatuta (telefonoaren audiorik gabe). Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index c0b8a27d3242..d40f692380ab 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"اتصال قطع شد"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"غیرفعال شد"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"‏پیکربندی IP انجام نشد"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"اتصال ناموفق به دلیل شبکه با کیفیت پایین"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"‏اتصال Wi-Fi برقرار نشد"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"مشکل اصالت‌سنجی"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"برقراری اتصال ممکن نیست"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"برقراری اتصال به «<xliff:g id="AP_NAME">%1$s</xliff:g>» ممکن نیست"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"اتصال به‌صورت خودکار انجام نمی‌شود"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"دسترسی به اینترنت ندارد"</string>
<string name="saved_network" msgid="7143698034077223645">"ذخیره‌شده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"اتصال به شبکه محدود برقرار شد"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"‏اتصال خودکار ازطریق %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائه‌دهنده رده‌بندی شبکه"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"‏متصل از طریق %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"متصل شده ازطریق <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"‏در دسترس از طریق %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"برای ثبت‌نام ضربه بزنید"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"عدم اتصال به اینترنت"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"‏سرور DNS خصوصی قابل دسترسی نیست"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"عدم دسترسی به اینترنت"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"ورود به سیستم لازم است"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ظرفیت نقطه دسترسی موقتاً تکمیل شده است"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"‏متصل ازطریق %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"‏در دسترس ازطریق %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"درحال بازکردن <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"متصل نشد"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"درحال تکمیل ثبت‌نام…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"ثبت‌نام تکمیل نشد. برای امتحان مجدد ضربه بزنید."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ثبت‌نام کامل شد. درحال اتصال…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"بسیار آهسته"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"آهسته"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"تأیید"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"متوسط"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"سریع"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"خیلی سریع"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"منقضی‌شده"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"در حال مرتبط‌سازی..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"متصل (بدون تلفن)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"متصل (بدون رسانه)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"متصل (بدون دسترسی پیام)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"متصل (بدون تلفن یا رسانه)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"متصل، باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"متصل (بدون تلفن)، باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 66373b50a6b1..0d6036abd145 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Yhteys katkaistu"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Pois päältä"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP-kokoonpanovirhe"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Ei yhteyttä – verkko huonolaatuinen"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi-yhteysvirhe"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Todennusvirhe"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Yhdistäminen ei onnistu."</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Yhdistäminen verkkoon <xliff:g id="AP_NAME">%1$s</xliff:g> ei onnistu."</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Yhteyttä ei muodosteta automaattisesti"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ei internetyhteyttä"</string>
<string name="saved_network" msgid="7143698034077223645">"Tallentaja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Yhdistetty maksulliseen verkkoon"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaattinen yhteys muodostettu palvelun %1$s kautta"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Yhdistetty automaattisesti verkon arviointipalvelun kautta"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Yhdistetty seuraavan kautta: %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Yhdistetty (<xliff:g id="NAME">%1$s</xliff:g>)"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Käytettävissä seuraavan kautta: %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Rekisteröidy napauttamalla"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Ei internetyhteyttä"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ei internetyhteyttä"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Sisäänkirjautuminen vaaditaan"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Yhteyspiste tilapäisesti täynnä"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Yhdistetty, verkko: %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Käytettävissä, verkko: %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Avataan <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Ei yhteyttä"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Viimeistellään rekisteröitymistä…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Rekisteröityminen epäonnistui. Yritä uudelleen napauttamalla."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Rekisteröityminen valmis. Yhdistetään…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Hyvin hidas"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Hidas"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Kohtuullinen"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Nopea"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Hyvin nopea"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vanhentunut"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Laiteparia muodostetaan..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Yhdistetty (ei puhelimen ääntä) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Yhdistetty (ei median ääntä) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Yhdistetty (ei pääsyä viesteihin) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Yhdistetty (ei puhelinta tai mediaa) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Yhdistetty, akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Yhdistetty (ei puhelimen ääntä), akun varaus <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index f1acddaebb9b..f2762367af33 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Déconnecté"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Désactivés"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Échec de configuration de l\'adresse IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Non connecté en raison de la mauvaise qualité du réseau"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Échec de connexion Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problème d\'authentification"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Connexion impossible"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Impossible de se connecter à « <xliff:g id="AP_NAME">%1$s</xliff:g> »"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Appareil connecté à un réseau mesuré"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiquement connecté par %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement par le fournisseur d\'avis sur le réseau"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté par %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Connecté sur le réseau <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Accessible par %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toucher pour vous connecter"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Aucune connexion Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Impossible d\'accéder au serveur DNS privé"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Aucune connexion Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Connexion requise"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Le point d\'accès est temporairement plein"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Connecté par %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Accessible par %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Ouverture de <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> en cours…"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Impossible de se connecter"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Terminaison de l\'inscription en cours…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Impossible de terminer l\'inscription. Touchez pour réessayer."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Inscription terminée. Connexion en cours…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Très lente"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lente"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Moyenne"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Élevée"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Très rapide"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expiré"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Association…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Connecté (aucun téléphone) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Connecté (aucun média) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Connecté (aucun accès aux messages) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Connecté (aucun téléphone ni média) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Connecté, pile chargée à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Connecté (aucun téléphone), pile chargée à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index d7cd5079d7df..7c4afa7b627f 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Déconnecté"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Désactivé"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Échec de configuration de l\'adresse IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Non connecté en raison de la faible qualité du réseau"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Échec de la connexion Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problème d\'authentification"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Connexion impossible"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Impossible de se connecter au réseau \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Enregistré lors de : <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connecté au réseau facturé à l\'usage"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Connecté automatiquement via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement via un fournisseur d\'évaluation du réseau"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Connecté via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Disponible via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Appuyez ici pour vous connecter"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Aucun accès à Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Impossible d\'accéder au serveur DNS privé"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Aucun accès à Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Connexion requise"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Point d\'accès temporairement plein"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Connecté via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Disponible via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Ouverture de <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>…"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Impossible de se connecter"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Finalisation de l\'inscription…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Impossible de finaliser l\'inscription. Appuyez ici pour réessayer."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Inscription terminée. Connexion…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Très lente"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lente"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Correcte"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Moyenne"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Élevée"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Très rapide"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Arrivé à expiration"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Association…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Connecté (aucun téléphone)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Connecté (aucun contenu mutimédia)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Connecté (sans accès aux messages)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Connecté (aucun tél./contenu multimédia)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Connecté, batterie à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Connecté (aucun téléphone), batterie à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 30ef44efd4a2..95d5e215f16b 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Rede desconectada"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desactivadas"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Erro na configuración de IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Non se estableceu conexión porque a rede é de baixa calidade"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Erro na conexión wifi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problema de autenticación"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Non se pode establecer conexión"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Non se pode establecer conexión coa aplicación <xliff:g id="AP_NAME">%1$s</xliff:g>"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non se conectará automaticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Estableceuse conexión coa rede sen tarifa plana"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Wifi conectada a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Dispoñible a través de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toca para rexistrarte"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Non hai conexión a Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Non se puido acceder ao servidor DNS privado"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Non hai conexión a Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"É obrigatorio iniciar sesión"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"O punto de acceso está temporalmente cheo"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Conectado a través de %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Dispoñible a través de %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Abrindo <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Non se puido establecer conexión"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Completando o rexistro…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Non se puido completar o rexistro. Toca para tentalo de novo."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Completouse o rexistro. Conectando…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Moi lenta"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lenta"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Aceptar"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Media"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Moi rápida"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Caducou"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Vinculando..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Conectado a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (sen teléfono)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Conectado a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (sen audio multimedia)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Conectado a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (sen acceso a mensaxes)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Conectado a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (sen tel./audio multimedia)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Conectado a <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>, batería ao <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Conectado a <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (sen teléfono), batería ao <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 20dbf4f72613..e8acbd5207a1 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"ડિસ્કનેક્ટ કર્યું"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"અક્ષમ કર્યો"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP કન્ફિગરેશન નિષ્ફળ"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"ઓછી ગુણવત્તાવાળા નેટવર્કના લીધે કનેક્ટ થયું નથી"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi કનેક્શન નિષ્ફળ"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"પ્રમાણીકરણ સમસ્યા"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"કનેક્ટ કરી શકાતું નથી"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' સાથે કનેક્ટ કરી શકાતું નથી"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ઑટોમૅટિક રીતે કનેક્ટ કરશે નહીં"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા સચવાયું"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"મીટર્ડ (ડેટા નિયંત્રણ) નેટવર્ક સાથે કનેક્ટેડ છે"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s દ્વારા સ્વત: કનેક્ટ થયેલ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"નેટવર્ક રેટિંગ પ્રદાતા દ્વારા ઑટોમૅટિક રીતે કનેક્ટ થયું"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s દ્વારા કનેક્ટ થયેલ"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કનેક્ટ થયેલ"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s દ્વારા ઉપલબ્ધ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"સાઇન અપ કરવા માટે ટૅપ કરો"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"કોઈ ઇન્ટરનેટ નથી"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ઇન્ટરનેટ ઍક્સેસ નથી"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"સાઇન ઇન આવશ્યક"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ઍક્સેસ પૉઇન્ટ અસ્થાયીરૂપે ભરાયેલ છે"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s દ્વારા કનેક્ટ થયેલ"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s દ્વારા ઉપલબ્ધ"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ખોલી રહ્યાં છીએ"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"કનેક્ટ કરી શકાયું નથી"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"સાઇન અપ પૂર્ણ કરી રહ્યા છીએ…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"સાઇન અપ પૂર્ણ કરી શકાયું નથી. ફરી પ્રયાસ કરવા માટે ટૅપ કરો."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"સાઇન અપ પૂર્ણ. કનેક્ટ કરી રહ્યાં છીએ…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"ખૂબ જ ધીમી"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"ધીમી"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ઓકે"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"મધ્યમ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ઝડપી"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ખૂબ ઝડપી"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"સમય સમાપ્ત થયો"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"જોડી કરી રહ્યું છે…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> સાથે કનેક્ટ થયેલ (કોઈ ફોન નથી)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> સાથે કનેક્ટ થયેલ (કોઈ મીડિયા નથી)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> સાથે કનેક્ટ થયેલ (કોઈ સંદેશ ઍક્સેસ નથી)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> સાથે કનેક્ટ થયેલ (ફોન કે મીડિયા નથી)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> સાથે કનેક્ટ થયેલ, બૅટરી <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> સાથે કનેક્ટ થયેલ (કોઈ ફોન નથી), બૅટરી <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index db536984eeea..1c4e4a05d517 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"डिसकनेक्ट किया गया"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"अक्षम"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉन्‍फ़िगरेशन की विफलता"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"खराब नेटवर्क होने के कारण कनेक्ट नहीं हुआ"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"वाईफ़ाई कनेक्‍शन विफलता"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"पुष्टि नहीं हो सकी"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"कनेक्ट नहीं हो पा रहा है"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' से कनेक्ट नहीं हो पा रहा है"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"अपने आप कनेक्ट नहीं होगा"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट नहीं है"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> के द्वारा सहेजा गया"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"सीमित डेटा वाले नेटवर्क से कनेक्ट किया गया"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s के ज़रिए ऑटोमैटिक रूप से कनेक्ट है"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग कंपनी के ज़रिए अपने आप कनेक्ट है"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s के द्वारा उपलब्ध"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> के ज़रिए कनेक्ट किया गया"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s के द्वारा उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप करने के लिए टैप करें"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"इंटरनेट कनेक्शन नहीं है"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"इंटरनेट कनेक्शन नहीं है"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"साइन इन करना ज़रूरी है"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ऐक्सेस पॉइंट फ़िलहाल भरा हुआ है"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s के ज़रिए कनेक्ट"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s के ज़रिए उपलब्ध"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> खोला जा रहा है"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"कनेक्ट नहीं किया जा सका"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"साइन-अप पूरा हो रहा है…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"साइन-अप पूरा नहीं हो सका. फिर से कोशिश करने के लिए टैप करें."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"साइन-अप पूरा हुआ. कनेक्ट हो रहा है…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"अत्‍यधिक धीमी"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"धीमी"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ठीक है"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"मध्यम"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"तेज़"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"बहुत ज़्यादा तेज़"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"समयसीमा खत्म हो गई"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"जोड़ा जा रहा है…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"जुड़ गया (फ़ोन के ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"जुड़ गया (मीडिया ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"जुड़ गया (मैसेज का ऐक्सेस नहीं)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"जुड़ गया (फ़ोन या मीडिया ऑडियो को छोड़कर)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"जुड़ गया, बैटरी का लेवल <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"जुड़ गया (फ़ोन के ऑडियो को छोड़कर), बैटरी का लेवल <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 89fcb8e7ad69..06166f6f52f0 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Nije povezano"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Onemogućeno"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Konfiguracija IP-a nije uspjela"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Niste povezani jer je mreža loše kvalitete"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Povezivanje s Wi-Fijem nije uspjelo"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problem u autentifikaciji"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Povezivanje nije uspjelo"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Povezivanje s aplikacijom \"<xliff:g id="AP_NAME">%1$s</xliff:g>\" nije uspjelo"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se povezati automatski"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Spremila aplik. <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezano s mrežom s ograničenim prometom"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezan putem %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezan putem ocjenjivača mreže"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Povezano putem %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Povezan putem mreže <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Dostupno putem %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite da biste se registrirali"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Nema interneta"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema interneta"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Obavezna prijava"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Pristupna je točka privremeno puna"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Povezano putem mreže %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Dostupno putem mreže %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Otvaranje aplikacije <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Povezivanje nije uspjelo"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Dovršavanje registracije…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Nije moguće dovršiti registraciju. Dodirnite za ponovni pokušaj."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registracija je dovršena. Povezivanje…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Vrlo sporo"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Sporo"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"U redu"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Srednje"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Brzo"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Vrlo brzo"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Isteklo"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Uparivanje…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Povezano (bez telefona): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Povezano (bez medija): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Povezano (bez pristupa porukama): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Povezano (bez telefona i medija): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Povezano, baterija <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Povezano (bez telefona), baterija <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 21dbfe87cb68..af5a114590b6 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Leválasztva"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Letiltva"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP-konfigurációs hiba"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Nem kapcsolódik a hálózat rossz minősége miatt"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi-kapcsolati hiba"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Azonosítási probléma"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nem lehet csatlakozni"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Nem lehet csatlakozni a(z) „<xliff:g id="AP_NAME">%1$s</xliff:g>” hálózathoz"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nem csatlakozik automatikusan"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nincs internet-hozzáférés"</string>
<string name="saved_network" msgid="7143698034077223645">"Mentette: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Forgalomkorlátos hálózathoz csatlakozva"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatikusan csatlakozott a következőn keresztül: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikusan csatlakozott a hálózatértékelés szolgáltatóján keresztül"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Csatlakozva a következőn keresztül: %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Kapcsolódva a következőn keresztül: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Elérhető a következőn keresztül: %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Koppintson a regisztrációhoz"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Nincs internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nincs internetkapcsolat"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Bejelentkezést igényel"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"A hozzáférési pont átmenetileg megtelt"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Csatlakozva a következőn keresztül: %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Elérhető a következőn keresztül: %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> megnyitása"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Nem sikerült csatlakozni"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Regisztráció befejezése…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Nem sikerült a regisztráció befejezése. Koppintással újrapróbálkozhat."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"A regisztráció befejeződött. Csatlakozás…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Nagyon lassú"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lassú"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Rendben"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Közepes"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Gyors"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Nagyon gyors"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Lejárt"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Párosítás..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Csatlakoztatva (telefonhang nélkül)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Csatlakoztatva (médiahang nélkül)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Csatlakoztatva (nincs üzenet-hozzáférés)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Csatlakoztatva (nincs telefon- és médiahang)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Csatlakoztatva, az akkumulátor töltöttsége: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Csatlakoztatva (telefonhang nélkül); az akkumulátor töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 4affd4fe52c3..61c3e7eddd08 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Կապ չկա"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Անջատված"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP կարգավորման ձախողում"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Չի կապակցվել ցանցի թույլ ազդանշանի պատճառով"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi կապի ձախողում"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Նույնականացման խնդիր"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Կապ չկա"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Հնարավոր չէ միանալ «<xliff:g id="AP_NAME">%1$s</xliff:g>»-ին"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Չի միանա ավտոմատ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ինտերնետ կապ չկա"</string>
<string name="saved_network" msgid="7143698034077223645">"Պահվել է՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Միացած է վճարովի թրաֆիկով ցանցի"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ավտոմատ կերպով կապակցվել է %1$s-ի միջոցով"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ միացել է ցանցերի վարկանիշի մատակարարի միջոցով"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Միացված է %1$s-ի միջոցով"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Միացված է <xliff:g id="NAME">%1$s</xliff:g>-ի միջոցով"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Հասանելի է %1$s-ի միջոցով"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Հպեք՝ գրանցվելու համար"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Կապ չկա"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Մասնավոր DNS սերվերն անհասանելի է"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ինտերնետ կապ չկա"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Անհրաժեշտ է մուտք գործել"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Հասանելիության կետը ժամանակավորապես լիքն է"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Միացված է %1$s-ի միջոցով"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Հասանելի է %1$s-ի միջոցով"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>, բացվում է"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Չհաջողվեց միանալ"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Գրանցումն ավարտվում է…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Չհաջողվեց ավարտել գրանցումը: Հպեք` նորից փորձելու համար:"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Գրանցումն ավարտված է: Միացում…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Շատ դանդաղ"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Դանդաղ"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Լավ"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Միջին"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Արագ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Շատ արագ"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Սպառվել է"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Զուգակցում..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Միացավ <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> սարքին (հեռախոս չկա)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Միացավ <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> սարքին (մեդիա չկա)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Միացավ <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> սարքին (հաղորդագրություններ չկան)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Միացավ <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> սարքին (հեռախոս կամ մեդիա չկա)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Միացված է, մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Միացված է (հեռախոս չկա), մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 69174cfc2b59..d7f58578d622 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Terputus"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Nonaktif"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Kegagalan Konfigurasi IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Tidak terhubung karena jaringan berkualitas rendah"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Kegagalan Sambungan Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Masalah autentikasi"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Tidak dapat terhubung"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Tidak dapat terhubung ke \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan terhubung otomatis"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Tidak ada akses internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Disimpan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Terhubung ke jaringan berbayar"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Terhubung otomatis melalui %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Otomatis terhubung melalui penyedia rating jaringan"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Terhubung melalui %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Terhubung melalui <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Tersedia melalui %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Ketuk untuk mendaftar"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Tidak ada internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Server DNS pribadi tidak dapat diakses"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Tidak ada internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Perlu login"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Titik akses penuh untuk sementara"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Terhubung melalui %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Tersedia melalui %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Membuka <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Tidak dapat terhubung"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Menyelesaikan pendaftaran…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Tidak dapat menyelesaikan pendaftaran. Ketuk untuk mencoba lagi."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Pendaftaran selesai. Menyambungkan…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Sangat Lambat"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lambat"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Oke"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Sedang"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Cepat"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Sangat Cepat"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Sudah tidak berlaku"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Menyambungkan..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Terhubung (tanpa ponsel)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Terhubung (tanpa media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Terhubung (tanpa akses pesan)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Terhubung (tanpa ponsel atau media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Terhubung, baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Terhubung (tanpa ponsel), baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index b541d50028aa..4862904d6d27 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Aftengt"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Óvirkt"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP-stillingarvilla"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Tenging er ekki til staðar því nettengingin er léleg"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi-tengingarvilla"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Vandamál við auðkenningu"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Ekki tókst að tengjast"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Ekki tókst að tengjast við „<xliff:g id="AP_NAME">%1$s</xliff:g>“"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Mun ekki tengjast sjálfkrafa"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Enginn netaðgangur"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> vistaði"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Tengdist neti með mældri notkun"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Sjálfkrafa tengt um %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Sjálfkrafa tengt um netgæðaveitu"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Tengt í gegnum %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Tenging í gegnum <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Í boði í gegnum %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Ýttu til að skrá þig"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Engin nettenging"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Ekki næst í DNS-einkaþjón"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Engin nettenging"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Innskráningar krafist"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Aðgangsstaður tímabundið fullur"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Tengt í gegnum %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Í boði í gegnum %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Opnar <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Ekki tókst að tengjast"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Gengur frá nýskráningu…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Ekki tókst að ljúka við nýskráningu. Ýttu til að reyna aftur."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Nýskráningu lokið. Tengist…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Mjög hægt"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Hægt"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Í lagi"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Miðlungshratt"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Hratt"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Mjög hratt"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Útrunnin"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Parar…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Tengt (enginn sími) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Tengt (ekkert efni) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Tengt (enginn aðgangur að skilaboðum) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Tengt (enginn sími eða efni) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Tengt, staða rafhlöðu <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Tengt (enginn sími), staða rafhlöðu <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 4e948a4f66d4..9e5a2246d0ec 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Non connessa"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Disattivata"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Errore configurazione IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Impossibile connettersi a causa della bassa qualità della rete"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Errore connessione Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problema di autenticazione"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Impossibile stabilire una connessione"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Impossibile connettersi a \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non verrà eseguita la connessione automatica"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nessun accesso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Salvata da <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connessione a rete a consumo effettuata"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Collegato automaticamente tramite %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Collegato automaticamente tramite fornitore di servizi di valutazione rete"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Collegato tramite %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Connesso tramite <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Disponibile tramite %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tocca per registrarti"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Internet assente"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Non è possibile accedere al server DNS privato"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nessuna connessione a Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Accesso richiesto"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Punto di accesso momentaneamente al completo"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Connesso tramite %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Disponibile tramite %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Apertura di <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>…"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Impossibile collegarsi"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Completamento della registrazione…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Impossibile completare la registrazione. Tocca per riprovare."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registrazione completata. Connessione…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Molto lenta"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lenta"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Media"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Veloce"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Molto veloce"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Scaduta"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Accoppiamento…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (telefono escluso)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (contenuti multimediali esclusi)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (nessun accesso ai messaggi)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> connesso (telefono o media esclusi)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> connesso, batteria al <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> connesso (telefono escluso), batteria al <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index f1d48f7bc8ab..c6afad9c0db3 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"מנותקת"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"מושבת"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"‏כשל בתצורת IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"אין חיבור לרשת, כי איכות הרשת נמוכה"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"‏כשל בחיבור Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"בעיית אימות"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"לא ניתן להתחבר"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"לא ניתן להתחבר אל <xliff:g id="AP_NAME">%1$s</xliff:g>"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"לא יתבצע חיבור באופן אוטומטי"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"אין גישה לאינטרנט"</string>
<string name="saved_network" msgid="7143698034077223645">"נשמר על ידי <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"יש חיבור לרשת המבוססת על חיוב לפי שימוש בנתונים"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"‏מחובר אוטומטית דרך %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"מחובר אוטומטית דרך ספק של דירוג רשת"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"‏מחובר דרך %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"מחוברת באמצעות <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"‏זמינה דרך %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"יש להקיש כדי להירשם"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"אין אינטרנט"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"‏לא ניתן לגשת לשרת DNS הפרטי"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"אין אינטרנט"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"נדרשת כניסה"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"נקודת הגישה מלאה באופן זמני"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"‏מחובר לרשת של %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"‏זמינה דרך %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"מתבצעת פתיחה של <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"לא ניתן היה להתחבר"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"מתבצעת השלמה של ההרשמה…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"לא ניתן היה להשלים את ההרשמה. יש להקיש כדי לנסות שוב."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"תהליך ההרשמה הסתיים. בתהליך התחברות…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"איטית מאוד"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"איטית"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"אישור"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"בינונית"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"מהירה"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"מהירה מאוד"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"התוקף פג"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"ההתאמה מתבצעת..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"מחובר (ללא טלפון)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"מחובר (ללא מדיה)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"מחובר (ללא גישה להודעות)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"מחובר (ללא טלפון או מדיה)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"מחובר, שיעור הסוללה <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"מחובר (ללא טלפון), שיעור הסוללה <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 8cfc8886dd2a..43fe1788dbcd 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"未接続"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"無効"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP設定エラー"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"ネットワークの品質が低いため、接続されていません"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi接続エラー"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"認証に問題"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"接続できません"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"「<xliff:g id="AP_NAME">%1$s</xliff:g>」に接続できません"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"自動的に接続されません"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"インターネット接続なし"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>により保存"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"従量制ネットワークに接続しました"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s 経由で自動的に接続しています"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ネットワーク評価プロバイダ経由で自動的に接続しています"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s経由で接続"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> で接続しました"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s経由で使用可能"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"タップして登録してください"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"インターネットに接続されていません"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"プライベート DNS サーバーにアクセスできません"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"インターネット未接続"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"ログインが必要"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"アクセス ポイントが一時的にいっぱいです"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s 経由で接続済み"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s 経由で使用可能"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"「<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>」を開いています"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"接続できませんでした"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"登録を完了しています…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"登録を完了できませんでした。タップしてもう一度お試しください。"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"登録が完了しました。接続しています…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"とても遅い"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"遅い"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"普通"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"速い"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"非常に速い"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"期限切れ"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"ペアとして設定中..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"接続済み(電話なし): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"接続済み(メディアなし): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"接続済み(メッセージ アクセスなし): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"接続済み(電話、メディアなし): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"接続済み、バッテリー残量 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>: <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"接続済み(電話なし)、バッテリー残量 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>: <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 3cc44ce20d83..c498b61a0641 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"კავშირი გაწყვეტილია"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"გამორთულია"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP კონფიგურაციის შეფერხება"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"არ არის დაკავშირებული დაბალი ხარისხის ქსელის გამო"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi კავშირის შეფერხება"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ავთენტიკაციის პრობლემა"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"დაკავშირება ვერ ხერხდება"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"<xliff:g id="AP_NAME">%1$s</xliff:g>-თან დაკავშირება ვერ ხერხდება"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ავტომატურად დაკავშირება ვერ მოხერხდება"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ინტერნეტ-კავშირი არ არის"</string>
<string name="saved_network" msgid="7143698034077223645">"შენახული <xliff:g id="NAME">%1$s</xliff:g>-ის მიერ"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"დაკავშირებულია ფასიან ქსელთან"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"ავტომატურად დაკავშირდა %1$s-ის მეშვეობით"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ავტომატურად დაკავშირდა ქსელის ხარისხის შეფასების პროვაიდერის მეშვეობით"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ით დაკავშირებული"</string>
<string name="connected_via_app" msgid="3532267661404276584">"დაკავშირებულია <xliff:g id="NAME">%1$s</xliff:g>-ით"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"ხელმისაწვდომია %1$s-ით"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"შეეხეთ რეგისტრაციისთვის"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ინტერნეტ-კავშირი არ არის"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ინტერნეტ-კავშირი არ არის"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"აუცილებელია სისტემაში შესვლა"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"წვდომის წერტილი დროებით გადატვირთულია"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s-ით დაკავშირებული"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"ხელმისაწვდომია %1$s-ით"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"მიმდინარეობს <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>-ის გახსნა"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"დაკავშირება ვერ მოხერხდა"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"მიმდინარეობს რეგისტრაციის დასრულება…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"რეგისტრაციის დასრულება ვერ მოხერხდა. შეეხეთ ხელახლა საცდელად."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"რეგისტრაცია დასრულდა. მიმდინარეობს დაკავშირება…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"ძალიან ნელი"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"ნელი"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"კარგი"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"საშუალო"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"სწრაფი"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ძალიან სწრაფი"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"ვადაგასულია"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"დაწყვილება…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"დაკავშირებულია (ტელეფონი არ არის)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"დაკავშირებულია (მედია არ არის)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"დაკავშირებულია (შეტყობინებებზე წვდომა არ არის)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"დაკავშირებულია (ტელეფონი ამ მედია არ არის)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"დაკავშირებულია. ბატარეის დონე: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"დაკავშირებულია (ტელეფონი არ არის). ბატარეის დონე: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 8eb7db740f47..314683bbe8ed 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Ажыратылған"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Өшірілген"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP конфигурациясының қатесі"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Желі байланысының сапасы төмен болғандықтан қосылмады"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi байланысының қатесі"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Растау мәселесі"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Қосылу мүмкін емес"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\"<xliff:g id="AP_NAME">%1$s</xliff:g>\" қолданбасына қосылу мүмкін емес"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматты қосылмайды"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Интернетпен байланыс жоқ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Трафик саналатын желіге қосылды."</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s арқылы автоматты қосылды"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Желі рейтингі провайдері арқылы автоматты түрде қосылған"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s арқылы қосылған"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> арқылы жалғанған"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s арқылы қолжетімді"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Тіркелу үшін түртіңіз."</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Интернетпен байланыс жоқ"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Жеке DNS серверіне кіру мүмкін емес."</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернетпен байланыс жоқ"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Аккаунтқа кіру керек"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Кіру нүктесі уақытша бос емес"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s арқылы қосылды"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s арқылы қолжетімді"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ашылуда"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Жалғанбады"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Тіркелу процесі аяқталуда…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Тіркелу процесі аяқталмады. Әрекетті қайталау үшін түртіңіз."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Тіркелу процесі аяқталды. Жалғануда…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Өте баяу"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Баяу"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Жарайды"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Орташа"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Жылдам"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Өте жылдам"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Мерзімі өтті"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Жұптауда..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> жалғанды (телефонсыз)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> жалғанды (аудиосыз)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> жалғанды (хабарларға кіруге рұқсат жоқ)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> жалғанды (телефонсыз не аудиосыз)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Жалғанды, батарея заряды: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Жалғанды (телефонсыз), батарея заряды: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
@@ -641,7 +631,7 @@
<string name="data_connection_lte" msgid="7675461204366364124">"LTE"</string>
<string name="data_connection_lte_plus" msgid="6643158654804916653">"LTE+"</string>
<string name="data_connection_carrier_wifi" msgid="8932949159370130465">"W+"</string>
- <string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобильдік деректер өшірулі"</string>
+ <string name="cell_data_off_content_description" msgid="2280700839891636498">"Мобильдік интернет өшірулі"</string>
<string name="not_default_data_content_description" msgid="6517068332106592887">"Деректерді пайдалануға реттелмеген."</string>
<string name="accessibility_no_phone" msgid="2687419663127582503">"Телефон жоқ."</string>
<string name="accessibility_phone_one_bar" msgid="5719721147018970063">"Телефон бір баған."</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 7bfa1c44a4ed..00668fed4820 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"បាន​ផ្ដាច់"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"បាន​បិទ"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"ការ​កំណត់​រចនាសម្ព័ន្ធ IP បរាជ័យ"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"មិន​អាច​ភ្ជាប់​បាន​ទេ ដោយសារ​បណ្តាញ​មាន​គុណភាព​សេវា​ខ្សោយ"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"ការ​ភ្ជាប់​ WiFi បរាជ័យ"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"បញ្ហា​ក្នុង​ការ​ផ្ទៀងផ្ទាត់"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"មិនអាច​ភ្ជាប់​បានទេ"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"មិនអាចភ្ជាប់ជាមួយ \'<xliff:g id="AP_NAME">%1$s</xliff:g>\' បានទេ"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"មិនមាន​ការតភ្ជាប់​អ៊ីនធឺណិតទេ"</string>
<string name="saved_network" msgid="7143698034077223645">"បានរក្សាទុកដោយ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"បានភ្ជាប់ទៅ​បណ្ដាញដែលផ្អែកតាមទិន្នន័យដែលប្រើ"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈ %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"បានភ្ជាប់​ដោយស្វ័យប្រវត្តិ​តាម​រយៈក្រុមហ៊ុនផ្តល់​ការ​វាយ​តម្លៃលើ​បណ្តាញ"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"បានភ្ជាប់តាមរយៈ %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"ភ្ជាប់​តាម <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"មានតាមរយៈ %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ចុច​ដើម្បី​ចុះឈ្មោះ"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"គ្មាន​អ៊ីនធឺណិតទេ"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"មិនអាច​ចូលប្រើ​ម៉ាស៊ីនមេ DNS ឯកជន​បានទេ"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"គ្មាន​អ៊ីនធឺណិតទេ"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"តម្រូវ​ឱ្យ​ចូល​គណនី"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ចំណុចចូលប្រើពេញជាបណ្តោះអាសន្ន"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"បានភ្ជាប់តាមរយៈ %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"មានតាមរយៈ %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"កំពុង​បើក <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"មិន​អាចភ្ជាប់​បានទេ"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"កំពុង​បញ្ចប់​ការចុះឈ្មោះ…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"មិនអាច​បញ្ចប់​ការចុះឈ្មោះ​បានទេ។ សូមចុច ដើម្បី​ព្យាយាម​ម្ដង​ទៀត។"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ការចុះ​ឈ្មោះ​បានបញ្ចប់។ កំពុងភ្ជាប់…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"យឺតណាស់"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"យឺត"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"យល់ព្រម"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"មធ្យម"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"លឿន"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"លឿន​ខ្លាំង"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"បានផុតកំណត់"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"កំពុង​ផ្គូផ្គង..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"បាន​ភ្ជាប់ (គ្មាន​ទូរសព្ទ​ទេ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"បាន​ភ្ជាប់ (គ្មាន​មេឌៀ​ទេ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"បាន​ភ្ជាប់ (គ្មាន​ការ​ចូល​ប្រើ​សារ​ទេ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"បាន​ភ្ជាប់ (គ្មាន​ទូរសព្ទ ឬ​មេឌៀ​ទេ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"បានភ្ជាប់ ហើយ​ថ្ម​មាន​កម្រិត <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"បាន​ភ្ជាប់ (គ្មាន​ទូរសព្ទ​ទេ) ហើយ​ថ្ម​មាន​កម្រិត <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index e77f8d49245c..ebf742b9a5dd 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"ಸಂಪರ್ಕ ಕಡಿತಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP ಕಾನ್ಫಿಗರೇಶನ್ ವಿಫಲತೆ"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"ಕಡಿಮೆ ಗುಣಮಟ್ಟದ ನೆಟ್‌ವರ್ಕ್‌ನಿಂದಾಗಿ ಸಂಪರ್ಕ ಸಾಧಿಸಿಲ್ಲ"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi ಸಂಪರ್ಕ ವಿಫಲತೆ"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ಪ್ರಮಾಣೀಕರಣ ಸಮಸ್ಯೆ"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' ಗೆ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವಿಲ್ಲ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ನಿಂದ ಉಳಿಸಲಾಗಿದೆ"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ಮಾಪನ ಮಾಡಲಾದ ನೆಟ್‌ವರ್ಕ್‌ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ನೆಟ್‌ವರ್ಕ್ ರೇಟಿಂಗ್ ಒದಗಿಸುವವರ ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ಆ್ಯಪ್ ಮೂಲಕ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ಸೈನ್ ಅಪ್ ಮಾಡಲು ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"ಖಾಸಗಿ DNS ಸರ್ವರ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"ಸೈನ್ ಇನ್ ಮಾಡುವ ಅಗತ್ಯವಿದೆ"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ಪ್ರವೇಶ ಕೇಂದ್ರ ತಾತ್ಕಾಲಿಕವಾಗಿ ಭರ್ತಿಯಾಗಿದೆ"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ಅನ್ನು ತೆರೆಯಲಾಗುತ್ತಿದೆ"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"ಸೈನ್-ಅಪ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಲಾಗುತ್ತಿದೆ…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"ಸೈನ್-ಅಪ್ ಅನ್ನು ಪೂರ್ಣಗೊಳಿಸಲು ಸಾಧ್ಯವಾಗುತ್ತಿಲ್ಲ. ಮತ್ತೊಮ್ಮೆ ಪ್ರಯತ್ನಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ಸೈನ್-ಅಪ್ ಪೂರ್ಣಗೊಂಡಿದೆ. ಸಂಪರ್ಕಿಸಲಾಗುತ್ತಿದೆ…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"ತುಂಬಾ ನಿಧಾನವಾಗಿದೆ"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"ನಿಧಾನ"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ಸರಿ"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"ಮಧ್ಯಮ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ವೇಗ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ತುಂಬಾ ವೇಗವಾಗಿದೆ"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"ಅವಧಿ ಮುಕ್ತಾಯವಾಗಿದೆ"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"ಜೋಡಿಸಲಾಗುತ್ತಿದೆ..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"ಸಂಪರ್ಕಗೊಂಡಿದೆ (ಫೋನ್ ಇಲ್ಲ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"ಸಂಪರ್ಕಗೊಂಡಿದೆ (ಮಾಧ್ಯಮವಿಲ್ಲ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> ಗೆ (ಸಂದೇಶ ಪ್ರವೇಶವಿಲ್ಲ) ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"ಸಂಪರ್ಕಗೊಂಡಿದೆ (ಫೋನ್ ಅಥವಾ ಮಾಧ್ಯಮವಿಲ್ಲ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"ಸಂಪರ್ಕಗೊಂಡಿದೆ, ಬ್ಯಾಟರಿ ಚಾರ್ಜ್‌ ಮಟ್ಟ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"ಸಂಪರ್ಕಗೊಂಡಿದೆ (ಫೋನ್ ಇಲ್ಲ), ಬ್ಯಾಟರಿ ಚಾರ್ಜ್‌ ಮಟ್ಟ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index fe81cb381904..21c809102b3f 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"연결 끊김"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"사용 중지됨"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP 설정 실패"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"저품질 네트워크로 인해 연결되지 않음"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi 연결 실패"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"인증 문제"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"연결할 수 없음"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'에 연결할 수 없음"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"자동으로 연결되지 않습니다."</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"인터넷에 연결되어 있지 않음"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 저장됨"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"데이터 전송량 제한이 있는 네트워크에 연결됨"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s을(를) 통해 자동으로 연결됨"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"네트워크 평가 제공업체를 통해 자동으로 연결됨"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s을(를) 통해 연결됨"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>을(를) 통해 연결됨"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s을(를) 통해 사용 가능"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"탭하여 가입"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"인터넷 연결 없음"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"인터넷 연결 없음"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"로그인 필요"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"액세스 포인트가 일시적으로 가득 참"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s을(를) 통해 연결됨"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s을(를) 통해 사용 가능"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> 여는 중"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"연결할 수 없음"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"가입 완료 중…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"가입을 완료할 수 없습니다. 다시 시도하려면 탭하세요."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"가입이 완료되었습니다. 연결 중…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"매우 느림"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"느림"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"보통"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"보통"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"빠름"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"매우 빠름"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"만료됨"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"페어링 중..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"연결됨(전화 없음)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"연결됨(미디어 없음)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"연결됨(메시지 액세스 권한 없음)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"연결됨(전화 또는 미디어 없음)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"연결됨, 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"연결됨(전화 없음), 배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index d5dc291b1182..076f317aaa4e 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Ажыратылды"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Өчүрүлгөн"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP конфигурациясы бузулду"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Тармактын сапаты начар болгондуктан, туташкан жок"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi туташуусу бузулду"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Аутентификация маселеси бар"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Туташпай жатат"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\"<xliff:g id="AP_NAME">%1$s</xliff:g>\" тармагына туташпай койду"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматтык түрдө туташпайт"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Интернетке туташпай турат"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Чектелген трафикке туташтырылды"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s аркылуу автоматтык түрдө туташты"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Тармактар рейтингинин булагы аркылуу автоматтык түрдө туташты"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s аркылуу жеткиликтүү"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> аркылуу туташты"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s аркылуу жеткиликтүү"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Катталуу үчүн таптап коюңуз"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Интернет жок"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Жеке DNS сервери жеткиликсиз"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернет жок"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Аккаунтка кирүү талап кылынат"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Байланыш түйүнүнө өтө көп түзмөк туташып турат"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s аркылуу туташты"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s аркылуу иштейт"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ачылууда"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Туташпай койду"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Катталуу аяктоодо…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Катталуу аягына чыккан жок. Кайра аракет кылуу үчүн таптап коюңуз."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Катталуу аягына чыкты. Туташууда…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Өтө жай"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Жай"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Жарайт"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Орто"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Ылдам"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Абдан ылдам"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Эскирип калган"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Туташууда…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Туташып турат (телефониясыз)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Туташып турат (медиасыз)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Туташып турат (SMS/MMS жазышуусуз)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Туташып турат (телефониясыз же медиасыз)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Туташып турат, батареянын деңгээли – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Туташып турат (телефониясыз), батареянын деңгээли – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 578017c6d229..9ebe67a0c8b0 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ປິດການນຳໃຊ້"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"ການ​ຕັ້ງ​ຄ່າ IP ລົ້ມ​ເຫຼວ"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"ບໍ່ໄດ້ເຊື່ອມຕໍ່ເນື່ອງຈາກຄຸນນະພາບເຄືອຂ່າຍຕໍ່າ"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"​ການ​ເຊື່ອມ​ຕໍ່ WiFi ລົ້ມ​ເຫຼວ"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ບັນຫາການພິສູດຢືນຢັນ"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"ບໍ່ສາມາດເຊື່ອມຕໍ່ຫາ \'<xliff:g id="AP_NAME">%1$s</xliff:g>\' ໄດ້"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
<string name="saved_network" msgid="7143698034077223645">"ບັນທຶກ​​​ໂດຍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ເຊື່ອມຕໍ່ຫາເຄືອຂ່າຍທີ່ມີການວັດແທກແລ້ວ"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"ເຊື່ອມຕໍ່ຜ່ານທາງ %1$s ໂດຍອັດຕະໂນມັດ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ເຊື່ອມຕໍ່ກັບອັດຕະໂນມັດແລ້ວຜ່ານຜູ້ໃຫ້ບໍລິການຄະແນນເຄືອຂ່າຍ"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"​ເຊື່ອມຕໍ່​ຜ່ານ %1$s ​ແລ້ວ"</string>
<string name="connected_via_app" msgid="3532267661404276584">"ເຊື່ອມ​ຕໍ່​ຜ່ານ <xliff:g id="NAME">%1$s</xliff:g> ແລ້ວ"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"ມີ​ໃຫ້​ຜ່ານ %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ແຕະເພື່ອສະໝັກ"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ບໍ່ມີອິນເຕີເນັດ"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ບໍ່ມີອິນເຕີເນັດ"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"ຈຳເປັນຕ້ອງເຂົ້າສູ່ລະບົບ"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ຈຸດການເຂົ້າເຖິງເຕັມຊົ່ວຄາວ"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"ເຊື່ອມຕໍ່ຜ່ານ %1$s ແລ້ວ"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"ໃຊ້ໄດ້ຜ່ານ %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"ກຳລັງເປີດ <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"ບໍ່ສາມາດເຊື່ອມຕໍ່ໄດ້"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"ກຳລັງສຳເລັດການສະໝັກ…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"ບໍ່ສາມາດສຳເລັດການສະໝັກໄດ້. ແຕະເພື່ອລອງໃໝ່."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ສະໝັກສຳເລັດແລ້ວ. ກຳລັງເຊື່ອມຕໍ່…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"ຊ້າຫຼາຍ"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"ຊ້າ"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ຕົກລົງ"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"ປານກາງ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ໄວ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ໄວຫຼາຍ"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"ໝົດອາຍຸແລ້ວ"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"ກຳລັງຈັບຄູ່..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"ເຊື່ອມຕໍ່ແລ້ວ (ບໍ່ມີໂທລະສັບ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"ເຊື່ອມຕໍ່ແລ້ວ (ບໍ່ມີມີເດຍ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"ເຊື່ອມຕໍ່ແລ້ວ (ບໍ່ມີສິດເຂົ້າອ່ານຂໍ້ຄວາມ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"ເຊື່ອມຕໍ່ແລ້ວ (ບໍ່ມີໂທລະສັບ ຫຼື ມີເດຍ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"ເຊື່ອມຕໍ່ແລ້ວ, ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"ເຊື່ອມຕໍ່ແລ້ວ (ບໍ່ມີໂທລະສັບ), ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index a2be9015e371..fcea77873ccd 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Neprisijungta"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Neleidžiama"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP konfigūracijos triktis"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Neprisijungta dėl žemos kokybės tinklo"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"„Wi-Fi“ ryšio triktis"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Autentifikavimo problema"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nepavyksta prisijungti"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Nepavyksta prisijungti prie „<xliff:g id="AP_NAME">%1$s</xliff:g>“"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nebus automatiškai prisijungiama"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nėra interneto ryšio"</string>
<string name="saved_network" msgid="7143698034077223645">"Išsaugojo <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Prisijungta prie matuojamo tinklo"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiškai prisijungta naudojant „%1$s“"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiškai prisijungta naudojant tinklo įvertinimo paslaugos teikėjo paslaugomis"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Prisijungta naudojant „%1$s“"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Prisijungta naudojant programą „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Pasiekiama naudojant „%1$s“"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Palieskite, kad prisiregistruotumėte"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Nėra interneto ryšio"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Privataus DNS serverio negalima pasiekti"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nėra interneto ryšio"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Reikia prisijungti"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Prieigos taškas laikinai visiškai užimtas"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Prisijungta naudojant „%1$s“"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Pasiekiama naudojant „%1$s“"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Atidaroma: „<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>“"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Nepavyko prisijungti"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Baigiamas prisiregistravimas…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Nepavyko užbaigti prisiregistravimo. Jei norite bandyti dar kartą, palieskite."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Prisiregistravimas baigtas. Prijungiama…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Labai lėtas"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lėtas"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Gerai"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Vidutinis"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Greitas"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Labai greitas"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Baigėsi galiojimo laikas"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Susiejama..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Prisijungta (<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>), (telefono nėra)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Prisijungta (<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>), (medijos nėra)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Prisij. (<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>), (prieigos prie praneš. nėra)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Prisijungta (<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>), (telef. ar medijos nėra)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Prisijungta (<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>), akumuliatoriaus įkrovos lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Prisijungta (<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>), (telefono nėra), akumuliatoriaus įkrovos lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index d09617b42e77..c280737424f5 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Savienojums pārtraukts"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Atspējots"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP konfigurācijas kļūme"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Savienojums nav izveidots zemas kvalitātes tīkla dēļ"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi savienojuma kļūme"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Autentificēšanas problēma"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nevar izveidot savienojumu"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Nevar izveidot savienojumu ar tīklu <xliff:g id="AP_NAME">%1$s</xliff:g>"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Savienojums netiks izveidots automātiski"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nav piekļuves internetam"</string>
<string name="saved_network" msgid="7143698034077223645">"Saglabāja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Izveidots savienojums ar maksas tīklu"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automātiski savienots, izmantojot %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automātiski izveidots savienojums, izmantojot tīkla vērtējuma sniedzēju"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Savienots, izmantojot %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Savienojums ar <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Pieejams, izmantojot %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Pieskarieties, lai reģistrētos"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Nav interneta"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Nevar piekļūt privātam DNS serverim."</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nav piekļuves internetam"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Nepieciešama pierakstīšanās"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Piekļuves punkts īslaicīgi ir pilns"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Savienojums izveidots, izmantojot %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Pieejams, izmantojot %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Notiek lietotnes <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> atvēršana."</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Nevarēja izveidot savienojumu"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Notiek reģistrācijas pabeigšana…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Nevarēja pabeigt reģistrāciju. Pieskarieties, lai mēģinātu vēlreiz."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Reģistrācija ir pabeigta. Notiek savienojuma izveide…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Ļoti lēns"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lēns"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Labi"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Vidējs"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Ātrs"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Ļoti ātrs"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Beidzies derīguma termiņš"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Notiek pāra izveide..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Savienojums izveidots (nav tālrunis): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Savienojums izveidots (nav multivide): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Savienots (nav piekļuves ziņojumiem): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Savienots (nav tālrunis vai multivide): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Savienojums izveidots (<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>), akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Savienojums izveidots <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (nav tālrunis), akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 7dc9174d863f..f84990051ab6 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Не е поврзано"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Оневозможено"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Конфигурирањето ИП не успеа"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Не е поврзано поради нискиот квалитет на мрежата"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Поврзувањето преку Wi-Fi не успеа"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Проблем со автентикација"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Не може да се поврзе"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Не може да се поврзе на „<xliff:g id="AP_NAME">%1$s</xliff:g>“"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не може да се поврзе автоматски"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Нема пристап до интернет"</string>
<string name="saved_network" msgid="7143698034077223645">"Зачувано од <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Поврзано на мрежа со ограничен интернет"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматски поврзано преку %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматски поврзано преку оценувач на мрежа"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Поврзано преку %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Поврзано преку <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Достапно преку %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Допрете за да се регистрирате"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Нема интернет"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Не може да се пристапи до приватниот DNS-сервер"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Нема интернет"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Потребно е најавување"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Пристапната точка привремено е преоптоварена"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Поврзано преку %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Достапно преку %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Се отвора <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Не може да се поврзе"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Се завршува регистрацијата…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Не може да се заврши регистрацијата. Допрете за да се обидете повторно."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Регистрацијата е завршена. Се поврзува…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Многу бавна"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Бавна"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Во ред"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Средна"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Брза"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Многу брза"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Истечено"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Се спарува..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без телефон)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без аудиовизуелни содржини)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без пристап до пораките)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (без телефон и аудиовизуел.)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>, ниво на батеријата <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Поврзан со <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (без телефон), ниво на батеријата <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index bae251a2874a..cc15e036c8c3 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"വിച്ഛേദിച്ചു"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"പ്രവർത്തനരഹിതമാക്കി"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP കോൺഫിഗറേഷൻ പരാജയം"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"മോശം നെറ്റ്‌വർക്ക് ‌ആയതിനാൽ കണക്‌റ്റായില്ല"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi കണക്ഷൻ പരാജയം"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ആധികാരികമാക്കുന്നതിലെ പ്രശ്‌നം"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"കണക്റ്റുചെയ്യാനാകുന്നില്ല"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' എന്നതിലേക്ക് കണക്‌റ്റുചെയ്യാനാകുന്നില്ല"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"സ്വയമേവ കണക്‌റ്റുചെയ്യില്ല"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ഇന്റർനെറ്റ് ആക്‌സസ് ഇല്ല"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> സംരക്ഷിച്ചത്"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"മീറ്റർ ചെയ്ത നെറ്റ്‌വർക്കിലേക്ക് കണക്റ്റ് ചെയ്തു"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s വഴി സ്വയമേവ ബന്ധിപ്പിച്ചു"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"നെറ്റ്‌വർക്ക് റേറ്റിംഗ് ദാതാവുമായി സ്വയം കണക്‌റ്റുചെയ്‌തു"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s വഴി ബന്ധിപ്പിച്ചു"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> മുഖേന കണക്‌റ്റ് ചെയ്‌തു"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s വഴി ലഭ്യം"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"സൈൻ അപ്പ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ഇന്റർനെറ്റ് ഇല്ല"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"സ്വകാര്യ DNS സെർവർ ആക്‌സസ് ചെയ്യാനാവില്ല"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ഇന്റർനെറ്റ് ഇല്ല"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"സൈൻ ഇൻ ചെയ്യേണ്ടത് ആവശ്യമാണ്"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ആക്‌സസ് പോയിന്റ് താൽക്കാലികമായി നിറഞ്ഞിരിക്കുന്നു"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s വഴി ബന്ധിപ്പിച്ചു"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s വഴി ലഭ്യം"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> തുറക്കുന്നു"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"കണക്റ്റ് ചെയ്യാനായില്ല"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"സൈൻ അപ്പ് പൂർത്തിയാക്കുന്നു…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"സൈൻ അപ്പ് പൂർത്തിയാക്കാനായില്ല. വീണ്ടും ശ്രമിക്കാൻ ടാപ്പ് ചെയ്യുക."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"സൈൻ അപ്പ് പൂർത്തിയായി. കണക്റ്റ് ചെയ്യുന്നു…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"വളരെ കുറഞ്ഞ വേഗത്തിൽ"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"കുറഞ്ഞ വേഗത്തിൽ"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ശരി"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"ഇടത്തരം"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"വേഗത്തിൽ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"അതിവേഗം"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"കാലഹരണപ്പെട്ടത്"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"ജോടിയാക്കുന്നു…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"കണ‌ക്റ്റ് ചെ‌യ്‌തു (ഫോൺ ഇല്ല)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"കണക്റ്റ് ചെയ്‌തു (മീഡിയ ഇല്ല)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"കണ‌ക്റ്റ് ചെയ്‌തു (സന്ദേശ ആക്‌സസ് ഇല്ല)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"കണ‌ക്റ്റ് ചെയ്‌തു (ഫോണോ മീഡിയയോ ഇല്ല)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"കണക്റ്റ് ചെയ്‌തു, ബാറ്ററി <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"കണ‌ക്റ്റ് ചെയ്‌തു (ഫോൺ ഇല്ല), ബാറ്ററി നില <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index 890f2c4374a6..3701a30f6050 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Салсан"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Идэвхгүйжүүлсэн"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP тохируулга амжилтгүй"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Сүлжээний чанар муу байгаа тул холбогдож чадсангүй"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi холболт амжилтгүй"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Баталгаажуулалтын асуудал"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Холбогдож чадсангүй"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'-д холбогдож чадсангүй"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматаар холбогдохгүй"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Интернет хандалт алга"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> хадгалсан"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Хязгаартай сүлжээнд холбогдсон"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s-р автоматаар холбогдсон"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Сүлжээний үнэлгээ үзүүлэгчээр автоматаар холбогдох"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-р холбогдсон"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>-р холбогдсон"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s-р боломжтой"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Бүртгүүлэхийн тулд товшино уу"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Интернэт алга"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Хувийн DNS серверт хандах боломжгүй байна"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернэт алга"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Нэвтрэх шаардлагатай"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Хандах цэг түр хугацаанд дүүрсэн байна"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s-р холбогдсон"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s-р боломжтой"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>-г нээж байна"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Холбогдож чадсангүй"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Бүртгэлийг дуусгаж байна…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Бүртгэлийг дуусгаж чадсангүй. Дахин оролдохын тулд товшино уу."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Бүртгүүлж дууслаа. Холбогдож байна…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Маш удаан"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Удаан"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ЗА"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Дунд"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Хурдан"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Маш хурдан"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Хугацаа дууссан"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Хослуулж байна…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Холбогдсон (утас байхгүй)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Холбогдсон (медиа байхгүй)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Холбогдсон (мессежийн хандалт байхгүй)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Холбогдсон (утас эсвэл медиа байхгүй)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Холбогдсон, батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Холбогдсон (утас байхгүй), батарей <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index e87422dc6324..3fffc1a14f81 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"डिस्कनेक्ट केले"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"अक्षम"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP कॉंफिगरेशन अयशस्वी"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"कमी दर्जाच्या नेटवर्कमुळे कनेक्ट केलेले नाही"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi कनेक्शन अयशस्वी"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"प्रमाणीकरण समस्या"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"कनेक्ट करू शकत नाही"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'शी कनेक्‍ट करू शकत नाही"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वयंचलितपणे कनेक्ट करणार नाही"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट अ‍ॅक्सेस नाही"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे सेव्ह केले"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"मर्यादित नेटवर्कशी कनेक्ट केले आहे"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s द्वारे स्वयंचलितपणे कनेक्ट केले"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग प्रदात्याद्वारे स्वयंचलितपणे कनेक्ट केले"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s द्वारे कनेक्‍ट केले"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे कनेक्ट केले"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s द्वारे उपलब्‍ध"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप करण्यासाठी टॅप करा"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"इंटरनेट नाही"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"खाजगी DNS सर्व्हर ॲक्सेस करू शकत नाही"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"इंटरनेट नाही"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"साइन इन करणे आवश्यक आहे"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"अ‍ॅक्सेस पॉइंट तात्पुरते भरलेले"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s ने कनेक्‍ट केले"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s ने उपलब्‍ध"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> उघडत आहे"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"कनेक्ट करता आले नाही"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"साइन अप पूर्ण होत आहे…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"साइन अप पूर्ण करता आले नाही. पुन्हा प्रयत्न करण्यासाठी टॅप करा."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"साइन अप पूर्ण झाले. कनेक्ट करत आहे…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"खूप हळू"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"हळू"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ठीक आहे"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"मध्‍यम"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"जलद"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"खूप जलद"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"मुदत संपली"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"पेअर करत आहे…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"कनेक्ट केले (फोन नाही)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"कनेक्ट केले (मीडिया नाही)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"कनेक्ट केले (मेसेज अ‍ॅक्सेस नाही)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"कनेक्ट केले (फोन किंवा मीडिया नाही)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"कनेक्ट केले, बॅटरी <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"कनेक्ट केले (फोन नाही), बॅटरी <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index e99de6e64c1d..79553c00afea 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Diputuskan sambungan"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Dinyahdayakan"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Kegagalan Konfigurasi IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Tidak disambungkan kerana rangkaian berkualiti rendah"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Kegagalan Sambungan WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Masalah pengesahan"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Tidak dapat bersambung"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Tidak dapat bersambung ke \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan menyambung secara automatik"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Tiada akses Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Diselamatkan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Disambungkan kepada rangkaian bermeter"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Disambungkan secara automatik melalui %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Disambungkan secara automatik melalui pembekal penilaian rangkaian"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Disambungkan melalui %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Disambungkan melalui <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Tersedia melalui %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Ketik untuk daftar"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Tiada Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Pelayan DNS peribadi tidak boleh diakses"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Tiada Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Log masuk diperlukan"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Titik akses penuh buat sementara waktu"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Disambungkan melalui %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Tersedia melalui %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Membuka <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Tidak dapat menyambung"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Menyelesaikan pendaftaran…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Tidak dapat menyelesaikan pendaftaran. Ketik untuk mencuba lagi."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Pendaftaran selesai. Menyambung…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Sangat Perlahan"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Perlahan"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Sederhana"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Laju"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Sangat Laju"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Tamat tempoh"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Menggandingkan..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Disambungkan (tiada telefon)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Disambungkan (tiada media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Disambungkan (tiada akses mesej)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Disambungkan (tiada telefon atau media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Disambungkan, bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Disambungkan (tiada telefon), bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index fcdd028ae212..fc386269ee0f 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"ချိတ်ဆက်မထားပါ"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ပိတ်ထားသည်"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP ပြုပြင်ခြင်း မအောင်မြင်ပါ"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"ကွန်ရက်ချိတ်ဆက်မှု အားနည်းသည့်အတွက် ချိတ်ဆက်ထားခြင်း မရှိပါ"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi ချိတ်ဆက်မှု မအောင်မြင်ပါ"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"အထောက်အထားစိစစ်မှု ပြဿနာ"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"ချိတ်ဆက်၍ မရပါ"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' နှင့် ချိတ်ဆက်၍ မရပါ"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"အလိုအလျောက်ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"အင်တာနက် ချိတ်ဆက်မှု မရှိပါ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> က သိမ်းဆည်းခဲ့သည်"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"အခမဲ့မဟုတ်သော ကွန်ရက်သို့ ချိတ်ဆက်ထားသည်"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ကွန်ရက်အဆင့်သတ်မှတ်ပေးသူ မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s မှတစ်ဆင့်ရနိုင်သည်"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"အကောင့်ဖွင့်ရန် တို့ပါ"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"အင်တာနက် မရှိပါ"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"အင်တာနက် မရှိပါ"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"လက်မှတ်ထိုးဝင်ရန် လိုအပ်သည်"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ကွန်ရက်ချိတ်ဆက်မှု ယာယီပြည့်နေသည်"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s မှတစ်ဆင့် ရနိုင်သည်"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ကို ဖွင့်နေသည်"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"ချိတ်ဆက်၍ မရပါ"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"အကောင့်ဖွင့်ခြင်း အပြီးသတ်နေသည်…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"အကောင့်ဖွင့်ခြင်း အပြီးသတ်၍ မရပါ။ ပြန်စမ်းကြည့်ရန် တို့ပါ။"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"အကောင့်ဖွင့်ခြင်း ပြီးပါပြီ။ ချိတ်ဆက်နေသည်…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"အလွန်နှေး"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"နှေး"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"အတော်အသင့်"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"မြန်"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"အလွန်မြန်"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"သက်တမ်းကုန်သွားပါပြီ"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"တွဲချိတ်နေသည်…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> ချိတ်ဆက်ပြီးပြီ (ဖုန်းမရှိပါ)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> ချိတ်ဆက်ပြီးပြီ (မီဒီယာ မရှိပါ)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> ချိတ်ဆက်ပြီးပြီ (မက်ဆေ့ဂျ် သုံး၍မရပါ)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> ချိတ်ပြီးပြီ (ဖုန်း (သို့) မီဒီယာ မရှိပါ)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"ချိတ်ဆက်ပြီးပြီ၊ ဘက်ထရီ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"ချိတ်ဆက်ပြီးပြီ (ဖုန်းမရှိပါ)၊ ဘက်ထရီ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 0490623d8563..3d1e98d101d7 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Frakoblet"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Slått av"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP-konfigurasjonsfeil"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Ikke tilkoblet på grunn av nettverk av lav kvalitet"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi-tilkoblingsfeil"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Autentiseringsproblem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Kan ikke koble til"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Kan ikke koble til «<xliff:g id="AP_NAME">%1$s</xliff:g>»"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kobler ikke til automatisk"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internettilgang"</string>
<string name="saved_network" msgid="7143698034077223645">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Koble til et nettverk med datamåling"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilkoblet via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk tilkoblet via leverandør av nettverksvurdering"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Tilkoblet via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Tilkoblet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Tilgjengelig via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Trykk for å registrere deg"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Ingen internettilkobling"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Den private DNS-tjeneren kan ikke nås"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ingen internettilkobling"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Pålogging kreves"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Tilgangspunktet er midlertidig fullt"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Tilkoblet via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Tilgjengelig via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Åpner <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Kunne ikke koble til"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Fullfører registreringen …"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Kunne ikke fullføre registreringen. Trykk for å prøve på nytt."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registreringen er fullført. Kobler til …"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Veldig treg"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Treg"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Ok"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Middels"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rask"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veldig rask"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Utløpt"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Kobler til …"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Koblet til (ingen telefon) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Koblet til (ingen medier) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Koblet til (ingen meldingstilgang) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Koblet til (ingen telefon eller medier) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Koblet til, batteri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Koblet til (ingen telefon), batteri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 87cb1237f759..777c897cccab 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"डिस्कनेक्ट गरिएको छ"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"असक्षम पारियो"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP विन्यास असफल"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"कम गुणस्तरको नेटवर्कका कारण जडान गर्न सकिएन"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"वाईफाई जडान असफल"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"प्रमाणीकरण समस्या"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"जडान गर्न सकिँदैन"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' मा जडान गर्न सकिँदैन"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वतः जडान हुने छैन"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"इन्टरनेटमाथिको पहुँच छैन"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा सेभ गरियो"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"प्रयोगसम्बन्धी सीमा तोकिएको नेटवर्कमा कनेक्ट गरियो"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s मार्फत् स्वतः जडान गरिएको"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क मूल्याङ्कनकर्ता मार्फत स्वत: जडान गरिएको"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s मार्फत जडित"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> मार्फत जडान गरिएको"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s मार्फत उपलब्ध"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप गर्न ट्याप गर्नुहोस्"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"इन्टरनेट छैन"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"इन्टरनेटमाथिको पहुँच छैन"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"साइन इन गर्न आवश्यक छ"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"पहुँचसम्बन्धी स्थान अस्थायी रूपमा भरिएको छ"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s मार्फत जडान गरियो"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s मार्फत उपलब्ध"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> खोल्दै"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"जडान गर्न सकिएन"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"साइन अप गर्ने कार्य सम्पन्न गर्दै…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"साइन अप गर्ने कार्य सम्पन्न गर्न सकिएन। फेरि प्रयास गर्न ट्याप गर्नुहोस्।"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"साइन अप गर्ने कार्य सम्पन्न भयो। जडान गर्दै…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"धेरै ढिलो"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"बिस्तारै"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ठिक छ"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"मध्यम"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"छिटो"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"धेरै छिटो"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"म्याद सकियो"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"कनेक्ट गरिँदै छ..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"जडान गरियो (फोनबाहेेक) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"जडान गरियो (मिडियाबाहेक) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"जडान गरियो (सन्देशमाथि पहुँच छैन) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"जडान गरियो (फोन वा मिडियाबाहेक) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"जडान गरियो, ब्याट्री <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"जडान गरियो (फोनबाहेेक), ब्याट्री <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 66937352a674..d30a21f00e3d 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Verbinding verbroken"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Uitgezet"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP-configuratie mislukt"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Niet verbonden wegens netwerk van lage kwaliteit"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wifi-verbinding mislukt"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Authenticatieprobleem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Kan geen verbinding maken"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Kan geen verbinding maken met \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Er wordt niet automatisch verbinding gemaakt"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string>
<string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Verbonden met netwerk met datalimiet"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Verbonden via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Beschikbaar via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tik om aan te melden"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Geen internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Geen toegang tot privé-DNS-server"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Geen internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Inloggen vereist"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Toegangspunt tijdelijk vol"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Verbonden via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Beschikbaar via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> wordt geopend"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Kan geen verbinding maken"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Aanmelding voltooien…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Kan aanmelding niet voltooien. Tik om het opnieuw te proberen."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Aanmelding voltooid. Verbinden…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Zeer langzaam"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Langzaam"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Redelijk"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Gemiddeld"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Snel"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Zeer snel"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Verlopen"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Koppelen..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Verbonden: <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (geen telefoon)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Verbonden: <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (geen media)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Verbonden: <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (geen berichtentoegang)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Verbonden: <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (geen telefoon of media)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Verbonden: <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>, batterij: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Verbonden: <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (geen telefoon), batterij: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 94afb939f48f..0120768aedad 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"ବିଛିନ୍ନ କରାଯାଇଛି"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ଅକ୍ଷମ ହୋଇଛି"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP କନଫିଗରେଶନ ବିଫଳ ହୋଇଛି"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"ନିମ୍ନ ମାନର ନେଟ୍‌ୱର୍କ କାରଣରୁ ସଂଯୁକ୍ତ ହୋଇନାହିଁ"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"ୱାଇଫାଇ ସଂଯୋଗ ବିଫଳ ହୋଇଛି"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ପ୍ରମାଣୀକରଣରେ ସମସ୍ୟା"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"ସଂଯୋଗ କରିପାରିବ ନାହିଁ"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' ସହିତ ସଂଯୁକ୍ତ ହୋଇପାରୁନାହିଁ"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ସ୍ୱଚାଳିତ ଭାବେ ସଂଯୁକ୍ତ ହେବନାହିଁ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ଇଣ୍ଟରନେଟ୍‌ର କୌଣସି ଆକ୍‌ସେସ୍‌ ନାହିଁ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ୱାରା ସେଭ କରାଯାଇଛି"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ମିଟରଯୁକ୍ତ ନେଟୱାର୍କ ସହ ସଂଯୋଗ କରାଯାଇଛି"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲୀ ସଂଯୁକ୍ତ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ନେଟୱର୍କ ମୂଲ୍ୟାୟନ ପ୍ରଦାତାଙ୍କ ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲ୍ୟ ସଂଯୁକ୍ତ"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ବାରା ସଂଯୋଗ କରାଯାଇଛି"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ସାଇନ୍ ଅପ୍ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ଇଣ୍ଟର୍ନେଟ୍ ସଂଯୋଗ ନାହିଁ"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"ବ୍ୟକ୍ତିଗତ DNS ସର୍ଭର୍ ଆକ୍ସେସ୍ କରିହେବ ନାହିଁ"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"କୌଣସି ଇଣ୍ଟରନେଟ୍‌ ନାହିଁ"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"ସାଇନ୍-ଇନ୍ ଆବଶ୍ୟକ"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ଆକ୍ସେସ୍ ପଏଣ୍ଟ ସାମୟିକ ଭାବେ ପୂର୍ଣ୍ଣ"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ଖୋଲୁଛି"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"ସଂଯୋଗ କରିହେଲା ନାହିଁ"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"ସାଇନ୍ ଅପ୍ ଶେଷ ହେଉଛି…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"ସାଇନ୍ ଅପ୍ ଶେଷ ହୋଇପାରିଲା ନାହିଁ। ପୁଣି ଥରେ ଚେଷ୍ଟା କରିବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ସାଇନ୍ ଅପ୍ ଶେଷ ହୋଇଛି। ସଂଯୋଗ କରୁଛି…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"ବହୁତ ମନ୍ଥର"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"କମ୍‌ ବେଗ"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ଠିକ୍‌ ଅଛି"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"ମଧ୍ୟମ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ଦ୍ରୁତ"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ଅତି ଦ୍ରୁତ"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"ମିଆଦ ଶେଷ ହୋଇଯାଇଛି"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"ପେୟାର୍‌ କରୁଛି…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"ସଂଯୁକ୍ତ ହେଲା (ଫୋନ୍ ନୁହେଁ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"ସଂଯୁକ୍ତ ହେଲା (ମିଡିଆ ନୁହେଁ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"ସଂଯୁକ୍ତ ହେଲା (ମେସେଜ୍ ଆକ୍ସେସ୍ ନାହିଁ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"ସଂଯୁକ୍ତ ହେଲା (ଫୋନ୍ କିମ୍ବା ମେଡିଆ ନୁହେଁ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"ସଂଯୁକ୍ତ ହେଲା, ବ୍ୟାଟେରୀ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"ସଂଯୁକ୍ତ ନାହିଁ (ଫୋନ୍ ନୁହେଁ), ବ୍ୟାଟେରୀ<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 7bab52f7d27e..14817679639f 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"ਡਿਸਕਨੈਕਟ ਹੋਇਆ"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ਅਯੋਗ ਬਣਾਇਆ"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP ਕੌਂਫਿਗਰੇਸ਼ਨ ਅਸਫਲਤਾ"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"ਘੱਟ ਗੁਣਵੱਤਾ ਵਾਲੇ ਨੈੱਟਵਰਕ ਕਾਰਨ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਗਿਆ"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi ਕਨੈਕਸ਼ਨ ਅਸਫਲਤਾ"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ਪ੍ਰਮਾਣੀਕਰਨ ਸਮੱਸਿਆ"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' ਨਾਲ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ਵੱਲੋਂ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"ਮੀਟਰਬੱਧ ਨੈੱਟਵਰਕ ਨਾਲ ਕਨੈਕਟ ਹੈ"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ਰਾਹੀਂ ਆਪਣੇ-ਆਪ ਕਨੈਕਟ ਹੋਇਆ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ਨੈੱਟਵਰਕ ਰੇਟਿੰਗ ਪ੍ਰਦਾਨਕ ਰਾਹੀਂ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਹੋਇਆ"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ਸਾਈਨ-ਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"ਸਾਈਨ-ਇਨ ਲੋੜੀਂਦਾ ਹੈ"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ਐਕਸੈੱਸ ਪੁਆਇੰਟ ਅਸਥਾਈ ਤੌਰ \'ਤੇ ਸੰਪੂਰਨ ਰੁਝੇਂਵੇਂ ਵਿੱਚ ਹੈ"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ਖੋਲ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"ਸਾਈਨ-ਅੱਪ ਮੁਕੰਮਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"ਸਾਈਨ-ਅੱਪ ਮੁਕੰਮਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ਸਾਈਨ-ਅੱਪ ਮੁਕੰਮਲ ਹੋਇਆ ਕਨੈਕਟ ਹੋ ਰਿਹਾ ਹੈ…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"ਬਹੁਤ ਹੌਲੀ"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"ਹੌਲੀ"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ਠੀਕ ਹੈ"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"ਔਸਤ"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"ਤੇਜ਼"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ਬਹੁਤ ਤੇਜ਼"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"ਮਿਆਦ ਮੁੱਕ ਗਈ"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"ਜੋੜਾਬੱਧ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"ਕਨੈਕਟ ਕੀਤਾ ਹੋਇਆ (ਕੋਈ ਫ਼ੋਨ ਨਹੀਂ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"ਕਨੈਕਟ ਕੀਤਾ ਹੋਇਆ (ਕੋਈ ਮੀਡੀਆ ਨਹੀਂ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"ਕਨੈਕਟ ਕੀਤਾ ਹੋਇਆ (ਸੁਨੇਹੇ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"ਕਨੈਕਟ ਕੀਤਾ ਹੋਇਆ (ਕੋਈ ਫ਼ੋਨ ਜਾਂ ਮੀਡੀਆ ਨਹੀਂ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"ਕਨੈਕਟ ਕੀਤਾ ਹੋਇਆ, ਬੈਟਰੀ ਦਾ ਪੱਧਰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"ਕਨੈਕਟ ਕੀਤਾ ਹੋਇਆ (ਕੋਈ ਫ਼ੋਨ ਨਹੀਂ), ਬੈਟਰੀ ਦਾ ਪੱਧਰ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 1384d5bcb090..83f8e49e5d44 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Rozłączono"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Wyłączona"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Błąd konfiguracji IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Brak połączenia z powodu słabego sygnału sieci"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Błąd połączenia Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problem z uwierzytelnianiem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nie można się połączyć"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Nie można się połączyć z siecią „<xliff:g id="AP_NAME">%1$s</xliff:g>”"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nie można połączyć automatycznie"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Brak dostępu do internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Zapisane przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Połączono z siecią z pomiarem użycia danych"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatycznie połączono przez: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatycznie połączono przez dostawcę ocen jakości sieci"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Połączono przez %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Połączenie przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Dostępne przez %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Kliknij, by się zarejestrować"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Brak internetu"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Brak dostępu do prywatnego serwera DNS"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Brak internetu"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Musisz się zalogować"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Punkt dostępu jest tymczasowo zajęty"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Połączono przez: %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Dostępna przez: %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Otwieram: <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Nie udało się połączyć"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Kończę rejestrować…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Nie udało się dokończyć rejestracji. Kliknij, by spróbować ponownie."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Rejestracja zakończona. Łączę…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Bardzo wolna"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Wolna"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Średnia"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Szybka"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Bardzo szybka"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Ważność wygasła"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Paruję…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Połączono (bez telefonu) – <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Połączono (bez multimediów) – <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Połączono (bez dostępu do wiadomości) – <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Połączono (bez telefonu i multimediów) – <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Połączono, bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> – <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Połączono (bez telefonu), bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> – <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 36675aef1699..d9a7c6941a44 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectada"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Falha de configuração de IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Não conectado devido à baixa qualidade da rede"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Falha de conexão Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problema de autenticação"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Não é possível estabelecer conexão"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Não é possível conectar-se a \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Não se conectará automaticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Sem acesso à Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Salva por <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a uma rede limitada"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectado automaticamente via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automaticamente via provedor de avaliação de rede"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Conectado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Disponível via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Sem Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Não é possível acessar o servidor DNS privado"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"É necessário fazer login"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Ponto de acesso temporariamente cheio"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Conectado via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Disponível via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Abrindo <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Não foi possível conectar"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Concluindo inscrição…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Não foi possível concluir a inscrição. Toque para tentar novamente."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Inscrição concluída. Conectando…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Muito lenta"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lenta"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Ok"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Média"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muito rápida"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirada"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Pareando…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Conectado (sem telefone) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Conectado (sem mídia) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Conectado (sem acesso a mensagens) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Conectado (sem telefone ou mídia) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Conectado, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> de bateria"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Conectado (sem telefone), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> de bateria"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index f5a9625bedcb..c24589c249b1 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Desligada"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Falha de configuração de IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Não ligado devido à baixa qualidade da rede"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Falha de ligação Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problema de autenticação"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Não é possível ligar"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Não é possível ligar a \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Não é efetuada uma ligação automaticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Sem acesso à Internet."</string>
<string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ligação estabelecida a uma rede de acesso limitado."</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ligado automaticamente através de %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ligado automaticamente através do fornecedor de classificação de rede"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Ligado através de %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Ligado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Disponível através de %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Sem Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Não é possível aceder ao servidor DNS."</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"É necessário iniciar sessão"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Ponto de acesso temporariamente cheio"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Ligado através de %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Disponível através de %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"A abrir <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Não foi possível estabelecer ligação"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"A concluir a inscrição…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Não foi possível concluir a inscrição. Toque para tentar novamente."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Inscrição concluída. A ligar…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Muito lenta"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lenta"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Média"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muito rápida"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirada"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"A sincronizar..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Ligado (sem telemóvel)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Ligado (sem multimédia)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Ligado (sem acesso a mensagens)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Ligado (sem telemóvel nem multimédia)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Ligado, bateria a <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Ligado (sem telemóvel), bateria a <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 36675aef1699..d9a7c6941a44 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Desconectada"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Desativado"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Falha de configuração de IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Não conectado devido à baixa qualidade da rede"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Falha de conexão Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problema de autenticação"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Não é possível estabelecer conexão"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Não é possível conectar-se a \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Não se conectará automaticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Sem acesso à Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Salva por <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a uma rede limitada"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectado automaticamente via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automaticamente via provedor de avaliação de rede"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Conectado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Disponível via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Sem Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Não é possível acessar o servidor DNS privado"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"É necessário fazer login"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Ponto de acesso temporariamente cheio"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Conectado via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Disponível via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Abrindo <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Não foi possível conectar"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Concluindo inscrição…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Não foi possível concluir a inscrição. Toque para tentar novamente."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Inscrição concluída. Conectando…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Muito lenta"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lenta"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Ok"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Média"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rápida"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Muito rápida"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirada"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Pareando…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Conectado (sem telefone) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Conectado (sem mídia) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Conectado (sem acesso a mensagens) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Conectado (sem telefone ou mídia) a <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Conectado, <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> de bateria"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Conectado (sem telefone), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> de bateria"</string>
diff --git a/packages/SettingsLib/res/values-ro/arrays.xml b/packages/SettingsLib/res/values-ro/arrays.xml
index 987b9c36caac..faa15c253fb4 100644
--- a/packages/SettingsLib/res/values-ro/arrays.xml
+++ b/packages/SettingsLib/res/values-ro/arrays.xml
@@ -113,7 +113,7 @@
<item msgid="8887519571067543785">"96,0 kHz"</item>
</string-array>
<string-array name="bluetooth_a2dp_codec_sample_rate_summaries">
- <item msgid="2284090879080331090">"Folosiți selectarea sistemului (prestabilit)"</item>
+ <item msgid="2284090879080331090">"Folosește selectarea sistemului (prestabilit)"</item>
<item msgid="1872276250541651186">"44,1 kHz"</item>
<item msgid="8736780630001704004">"48,0 kHz"</item>
<item msgid="7698585706868856888">"88,2 kHz"</item>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 8b6259c3e6f0..04c58aecb2b0 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Deconectat"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Dezactivată"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Eroare de configurație IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Nu există conexiune din cauza rețelei de calitate slabă"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Eroare de conexiune Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problemă la autentificare"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nu se poate conecta"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Nu se poate conecta la „<xliff:g id="AP_NAME">%1$s</xliff:g>”"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nu se va conecta automat"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nu există acces la internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Salvată de <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"S-a conectat la o rețea contorizată"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectată automat prin %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectată automat prin furnizor de evaluări ale rețelei"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Conectată prin %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Conectat prin <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Disponibilă prin %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Atinge pentru a te înscrie"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Fără conexiune la internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Serverul DNS privat nu poate fi accesat"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Fără conexiune la internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Trebuie să te conectezi"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Punctul de acces este temporar plin"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Conectată prin %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Disponibilă prin %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Se deschide <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Nu s-a putut conecta"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Se finalizează înscrierea…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Nu s-a putut finaliza înscrierea. Atinge pentru a încerca din nou."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Înscrierea a fost finalizată. Se conectează…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Foarte lentă"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Lentă"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Bine"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Medie"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rapidă"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Foarte rapidă"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Expirat"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Se asociază…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Conectat (fără telefon) la <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Conectat (fără conținut media) la <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Conectat (fără acces la mesaje) la <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Conectat (fără telefon sau media) la <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Conectat, baterie <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Conectat (fără telefon), baterie <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
@@ -143,8 +133,8 @@
<string name="bluetooth_a2dp_profile_summary_use_for" msgid="7324694226276491807">"Folosește pentru profilul pentru conținut media audio"</string>
<string name="bluetooth_headset_profile_summary_use_for" msgid="808970643123744170">"Folosește pentru componenta audio a telefonului"</string>
<string name="bluetooth_opp_profile_summary_use_for" msgid="461981154387015457">"Folosește pentru transferul de fișiere"</string>
- <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Utilizați pentru introducere date"</string>
- <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Folosiți pentru aparatele auditive"</string>
+ <string name="bluetooth_hid_profile_summary_use_for" msgid="4289460627406490952">"Folosește pentru introducere date"</string>
+ <string name="bluetooth_hearing_aid_profile_summary_use_for" msgid="7689393730163320483">"Folosește pentru aparatele auditive"</string>
<string name="bluetooth_le_audio_profile_summary_use_for" msgid="2778318636027348572">"Folosește pentru LE_AUDIO"</string>
<string name="bluetooth_pairing_accept" msgid="2054232610815498004">"Asociază"</string>
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"CONECTEAZĂ"</string>
@@ -206,7 +196,7 @@
<string name="tts_status_not_supported" msgid="2702997696245523743">"<xliff:g id="LOCALE">%1$s</xliff:g> nu este acceptată"</string>
<string name="tts_status_checking" msgid="8026559918948285013">"Se verifică…"</string>
<string name="tts_engine_settings_title" msgid="7849477533103566291">"Setări pentru <xliff:g id="TTS_ENGINE_NAME">%s</xliff:g>"</string>
- <string name="tts_engine_settings_button" msgid="477155276199968948">"Lansați setările motorului"</string>
+ <string name="tts_engine_settings_button" msgid="477155276199968948">"Lansează setările motorului"</string>
<string name="tts_engine_preference_section_title" msgid="3861562305498624904">"Motor preferat"</string>
<string name="tts_general_section_title" msgid="8919671529502364567">"Preferințe generale"</string>
<string name="tts_reset_speech_pitch_title" msgid="7149398585468413246">"Resetează tonalitatea vorbirii"</string>
@@ -289,11 +279,11 @@
<string name="bluetooth_select_avrcp_version_string" msgid="1710571610177659127">"Versiunea AVRCP pentru Bluetooth"</string>
<string name="bluetooth_select_avrcp_version_dialog_title" msgid="7846922290083709633">"Selectează versiunea AVRCP pentru Bluetooth"</string>
<string name="bluetooth_select_map_version_string" msgid="526308145174175327">"Versiunea MAP pentru Bluetooth"</string>
- <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selectați versiunea MAP pentru Bluetooth"</string>
+ <string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"Selectează versiunea MAP pentru Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"Codec audio Bluetooth"</string>
<string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"Declanșează codecul audio Bluetooth\nSelecție"</string>
<string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"Rată de eșantionare audio Bluetooth"</string>
- <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"Declanșați codecul audio Bluetooth\nSelecție: rată de eșantionare"</string>
+ <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"Declanșează codecul audio Bluetooth\nSelecție: rată de eșantionare"</string>
<string name="bluetooth_select_a2dp_codec_type_help_info" msgid="8647200416514412338">"O opțiune inactivă înseamnă incompatibilitate cu telefonul sau setul căști-microfon"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"Biți audio Bluetooth per eșantion"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"Declanșează codecul audio Bluetooth\nSelecție: biți per eșantion"</string>
@@ -309,7 +299,7 @@
<string name="private_dns_mode_provider" msgid="3619040641762557028">"Nume de gazdă al furnizorului de DNS privat"</string>
<string name="private_dns_mode_provider_hostname_hint" msgid="6564868953748514595">"Introdu numele de gazdă al furnizorului de DNS"</string>
<string name="private_dns_mode_provider_failure" msgid="8356259467861515108">"Nu s-a putut conecta"</string>
- <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afișați opțiunile pentru certificarea Ecran wireless"</string>
+ <string name="wifi_display_certification_summary" msgid="8111151348106907513">"Afișează opțiunile pentru certificarea Ecran wireless"</string>
<string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"Mărește nivelul de înregistrare prin Wi‑Fi, afișează după 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_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>
@@ -326,16 +316,16 @@
<string name="allow_mock_location" msgid="2102650981552527884">"Permite locațiile fictive"</string>
<string name="allow_mock_location_summary" msgid="179780881081354579">"Permite locațiile fictive"</string>
<string name="debug_view_attributes" msgid="3539609843984208216">"Activează inspectarea atributelor de vizualizare"</string>
- <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Păstrați întotdeauna conexiunea de date mobile activată, chiar și atunci când funcția Wi‑Fi este activată (pentru comutarea rapidă între rețele)."</string>
+ <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Păstrează întotdeauna conexiunea de date mobile activată, chiar și atunci când funcția Wi‑Fi este activată (pentru comutarea rapidă între rețele)."</string>
<string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Folosește accelerarea hardware pentru tethering, dacă este disponibilă"</string>
- <string name="adb_warning_title" msgid="7708653449506485728">"Permiteți remedierea erorilor prin USB?"</string>
- <string name="adb_warning_message" msgid="8145270656419669221">"Remedierea erorilor prin USB are exclusiv scopuri de dezvoltare. Utilizați-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string>
+ <string name="adb_warning_title" msgid="7708653449506485728">"Permiți remedierea erorilor prin USB?"</string>
+ <string name="adb_warning_message" msgid="8145270656419669221">"Remedierea erorilor prin USB are exclusiv scopuri de dezvoltare. Folosește-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string>
<string name="adbwifi_warning_title" msgid="727104571653031865">"Permiți remedierea erorilor wireless?"</string>
<string name="adbwifi_warning_message" msgid="8005936574322702388">"Remedierea erorilor wireless are exclusiv scopuri de dezvoltare. Folosește-o pentru a copia date de pe computer pe dispozitiv, pentru a instala aplicații pe dispozitiv fără notificare și pentru a citi datele din jurnale."</string>
<string name="adb_keys_warning_message" msgid="2968555274488101220">"Revoci accesul la remedierea erorilor prin USB de pe toate computerele pe care le-ai autorizat anterior?"</string>
<string name="dev_settings_warning_title" msgid="8251234890169074553">"Permiți setările pentru dezvoltare?"</string>
<string name="dev_settings_warning_message" msgid="37741686486073668">"Aceste setări sunt destinate exclusiv utilizării pentru dezvoltare. Din cauza lor, este posibil ca dispozitivul tău și aplicațiile de pe acesta să nu mai funcționeze sau să funcționeze necorespunzător."</string>
- <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Verificați aplicațiile prin USB"</string>
+ <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Verifică aplicațiile prin USB"</string>
<string name="verify_apps_over_usb_summary" msgid="1317933737581167839">"Verifică aplicațiile instalate utilizând ADB/ADT, pentru a detecta un comportament dăunător."</string>
<string name="bluetooth_show_devices_without_names_summary" msgid="780964354377854507">"Vor fi afișate dispozitivele Bluetooth fără nume (numai adresele MAC)"</string>
<string name="bluetooth_disable_absolute_volume_summary" msgid="2006309932135547681">"Dezactivează funcția Bluetooth de volum absolut în cazul problemelor de volum apărute la dispozitivele la distanță, cum ar fi volumul mult prea ridicat sau lipsa de control asupra acestuia."</string>
@@ -393,7 +383,7 @@
<string name="window_animation_scale_title" msgid="5236381298376812508">"Scară animație fereastră"</string>
<string name="transition_animation_scale_title" msgid="1278477690695439337">"Scară tranziție animații"</string>
<string name="animator_duration_scale_title" msgid="7082913931326085176">"Scară durată Animator"</string>
- <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simulați afișaje secundare"</string>
+ <string name="overlay_display_devices_title" msgid="5411894622334469607">"Simulează afișaje secundare"</string>
<string name="debug_applications_category" msgid="5394089406638954196">"Aplicații"</string>
<string name="immediately_destroy_activities" msgid="1826287490705167403">"Nu păstra activitățile"</string>
<string name="immediately_destroy_activities_summary" msgid="6289590341144557614">"Elimină activitățile imediat ce utilizatorul le închide"</string>
@@ -404,10 +394,10 @@
<string name="show_notification_channel_warnings_summary" msgid="68031143745094339">"Afișează avertisment pe ecran când o aplicație postează o notificare fără canal valid"</string>
<string name="force_allow_on_external" msgid="9187902444231637880">"Forțează accesul aplicațiilor la stocarea externă"</string>
<string name="force_allow_on_external_summary" msgid="8525425782530728238">"Permite scrierea oricărei aplicații eligibile în stocarea externă, indiferent de valorile manifestului"</string>
- <string name="force_resizable_activities" msgid="7143612144399959606">"Forțați redimensionarea activităților"</string>
- <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permiteți redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string>
+ <string name="force_resizable_activities" msgid="7143612144399959606">"Forțează redimensionarea activităților"</string>
+ <string name="force_resizable_activities_summary" msgid="2490382056981583062">"Permite redimensionarea tuturor activităților pentru modul cu ferestre multiple, indiferent de valorile manifestului."</string>
<string name="enable_freeform_support" msgid="7599125687603914253">"Activează ferestrele cu formă liberă"</string>
- <string name="enable_freeform_support_summary" msgid="1822862728719276331">"Activați compatibilitatea pentru ferestrele experimentale cu formă liberă."</string>
+ <string name="enable_freeform_support_summary" msgid="1822862728719276331">"Activează compatibilitatea pentru ferestrele experimentale cu formă liberă."</string>
<string name="desktop_mode" msgid="2389067840550544462">"Modul desktop"</string>
<string name="local_backup_password_title" msgid="4631017948933578709">"Parolă backup computer"</string>
<string name="local_backup_password_summary_none" msgid="7646898032616361714">"În prezent, backupurile complete pe computer nu sunt protejate"</string>
@@ -449,7 +439,7 @@
<string name="daltonizer_mode_protanomaly" msgid="7805583306666608440">"Protanomalie (roșu-verde)"</string>
<string name="daltonizer_mode_tritanomaly" msgid="7135266249220732267">"Tritanomalie (albastru-galben)"</string>
<string name="accessibility_display_daltonizer_preference_title" msgid="1810693571332381974">"Corecția culorii"</string>
- <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Corecția culorii poate fi utilă dacă doriți:&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;să vedeți mai precis culorile;&lt;/li&gt; &lt;li&gt;&amp;nbsp;să eliminați culorile pentru a vă concentra mai bine.&lt;/li&gt; &lt;/ol&gt;"</string>
+ <string name="accessibility_display_daltonizer_preference_subtitle" msgid="1522101114585266455">"Corecția culorii poate fi utilă dacă vrei:&lt;br/&gt; &lt;ol&gt; &lt;li&gt;&amp;nbsp;să vezi mai precis culorile;&lt;/li&gt; &lt;li&gt;&amp;nbsp;să elimini culorile pentru a te concentra mai bine.&lt;/li&gt; &lt;/ol&gt;"</string>
<string name="daltonizer_type_overridden" msgid="4509604753672535721">"Valoare înlocuită de <xliff:g id="TITLE">%1$s</xliff:g>"</string>
<string name="power_remaining_settings_home_page" msgid="4885165789445462557">"<xliff:g id="PERCENTAGE">%1$s</xliff:g> - <xliff:g id="TIME_STRING">%2$s</xliff:g>"</string>
<string name="power_remaining_duration_only" msgid="8264199158671531431">"Timp aproximativ rămas: <xliff:g id="TIME_REMAINING">%1$s</xliff:g>"</string>
@@ -573,10 +563,10 @@
<string name="user_add_user_title" msgid="5457079143694924885">"Adaugi un utilizator nou?"</string>
<string name="user_add_user_message_long" msgid="1527434966294733380">"Poți să permiți accesul la acest dispozitiv altor persoane creând utilizatori suplimentari. Fiecare utilizator are propriul spațiu, pe care îl poate personaliza cu aplicații, imagini de fundal etc. De asemenea, utilizatorii pot ajusta setările dispozitivului, cum ar fi setările pentru Wi-Fi, care îi afectează pe toți ceilalți utilizatori.\n\nDupă ce adaugi un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOricare dintre utilizatori poate actualiza aplicațiile pentru toți ceilalți utilizatori. Este posibil ca setările de accesibilitate și serviciile să nu se transfere la noul utilizator."</string>
<string name="user_add_user_message_short" msgid="3295959985795716166">"Când adaugi un utilizator nou, acesta trebuie să-și configureze spațiul.\n\nOrice utilizator poate actualiza aplicațiile pentru toți ceilalți utilizatori."</string>
- <string name="user_setup_dialog_title" msgid="8037342066381939995">"Configurați utilizatorul acum?"</string>
+ <string name="user_setup_dialog_title" msgid="8037342066381939995">"Configurezi utilizatorul acum?"</string>
<string name="user_setup_dialog_message" msgid="269931619868102841">"Asigură-te că utilizatorul are posibilitatea de a prelua dispozitivul și de a-și configura spațiul"</string>
<string name="user_setup_profile_dialog_message" msgid="4788197052296962620">"Configurezi profilul acum?"</string>
- <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Configurați acum"</string>
+ <string name="user_setup_button_setup_now" msgid="1708269547187760639">"Configurează acum"</string>
<string name="user_setup_button_setup_later" msgid="8712980133555493516">"Nu acum"</string>
<string name="user_add_user_type_title" msgid="551279664052914497">"Adaugă"</string>
<string name="user_new_user_name" msgid="60979820612818840">"Utilizator nou"</string>
@@ -593,7 +583,7 @@
<string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
<string name="user_add_user" msgid="7876449291500212468">"Adaugă un utilizator"</string>
<string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
- <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
+ <string name="guest_exit_guest" msgid="5908239569510734136">"Șterge invitatul"</string>
<string name="guest_reset_guest" msgid="6110013010356013758">"Resetezi sesiunea pentru invitați"</string>
<string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"Resetezi invitatul?"</string>
<string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"Excludeți invitatul?"</string>
@@ -604,7 +594,7 @@
<string name="guest_reset_and_restart_dialog_message" msgid="2764425635305200790">"Astfel, va începe o nouă sesiune pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea actuală"</string>
<string name="guest_exit_dialog_title" msgid="1846494656849381804">"Ieși din modul pentru invitați?"</string>
<string name="guest_exit_dialog_message" msgid="1743218864242719783">"Se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală"</string>
- <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ieșiți"</string>
+ <string name="guest_exit_dialog_button" msgid="1736401897067442044">"Ieși"</string>
<string name="guest_exit_dialog_title_non_ephemeral" msgid="7675327443743162986">"Salvezi activitatea invitatului?"</string>
<string name="guest_exit_dialog_message_non_ephemeral" msgid="223385323235719442">"Salvează activitatea din sesiunea actuală sau șterge aplicațiile și datele"</string>
<string name="guest_exit_clear_data_button" msgid="3425812652180679014">"Șterge"</string>
@@ -613,11 +603,11 @@
<string name="guest_reset_button" msgid="2515069346223503479">"Resetează sesiunea pentru invitați"</string>
<string name="guest_exit_quick_settings_button" msgid="1912362095913765471">"Ieși din modul pentru invitați"</string>
<string name="guest_notification_ephemeral" msgid="7263252466950923871">"Toate activitățile vor fi șterse la ieșire"</string>
- <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Puteți să salvați sau să ștergeți activitatea la ieșire"</string>
+ <string name="guest_notification_non_ephemeral" msgid="6843799963012259330">"Poți să salvezi sau să ștergi activitatea la ieșire"</string>
<string name="guest_notification_non_ephemeral_non_first_login" msgid="8009307983766934876">"Resetează pentru a șterge acum activitatea din sesiune sau salvează ori șterge activitatea la ieșire"</string>
<string name="user_image_take_photo" msgid="467512954561638530">"Fă o fotografie"</string>
<string name="user_image_choose_photo" msgid="1363820919146782908">"Alege o imagine"</string>
- <string name="user_image_photo_selector" msgid="433658323306627093">"Selectați fotografia"</string>
+ <string name="user_image_photo_selector" msgid="433658323306627093">"Selectează fotografia"</string>
<string name="failed_attempts_now_wiping_device" msgid="4016329172216428897">"Prea multe încercări incorecte. Datele de pe acest dispozitiv vor fi șterse."</string>
<string name="failed_attempts_now_wiping_user" msgid="469060411789668050">"Prea multe încercări incorecte. Acest utilizator va fi șters."</string>
<string name="failed_attempts_now_wiping_profile" msgid="7626589520888963129">"Prea multe încercări incorecte. Acest profil de serviciu și datele sale vor fi șterse."</string>
@@ -662,7 +652,7 @@
<string name="keyboard_layout_dialog_title" msgid="3927180147005616290">"Alege aspectul tastaturii"</string>
<string name="keyboard_layout_default_label" msgid="1997292217218546957">"Prestabilit"</string>
<string name="turn_screen_on_title" msgid="3266937298097573424">"Activează ecranul"</string>
- <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permiteți activarea ecranului"</string>
+ <string name="allow_turn_screen_on" msgid="6194845766392742639">"Permite activarea ecranului"</string>
<string name="allow_turn_screen_on_description" msgid="43834403291575164">"Permite unei aplicații să activeze ecranul. Dacă acorzi permisiunea, aplicația poate să activeze oricând ecranul, fără intenția ta explicită."</string>
<string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"Oprești difuzarea <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Dacă difuzezi <xliff:g id="SWITCHAPP">%1$s</xliff:g> sau schimbi rezultatul, difuzarea actuală se va opri"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 12a5fcc6c040..704d84861a7a 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Не подключено"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Отключено"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Ошибка IP-конфигурации"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Подключение невозможно из-за низкого качества сети"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Ошибка подключения Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Ошибка аутентификации"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Ошибка подключения"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Не удалось подключиться к сети \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Подключение не будет выполняться автоматически"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Без доступа к Интернету"</string>
<string name="saved_network" msgid="7143698034077223645">"Сохранено: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Подключено к сети с ограниченным трафиком"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматически подключено к %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматически подключено через автора рейтинга сетей"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Подключено к %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Подключено через приложение \"<xliff:g id="NAME">%1$s</xliff:g>\"."</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Доступно через %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Нажмите, чтобы зарегистрироваться"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Нет подключения к Интернету"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Доступа к частному DNS-серверу нет."</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Нет подключения к Интернету"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Требуется выполнить вход."</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"К точке доступа подключено слишком много устройств"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Подключено к %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Доступно через %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Открытие <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>…"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Не удалось подключиться."</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Завершение регистрации…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Не удалось завершить регистрацию. Нажмите, чтобы повторить попытку."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Регистрация завершена. Подключение…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Очень медленная"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Медленная"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ОК"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Средняя"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Быстрая"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Очень быстрая"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Срок действия истек"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Устанавливается соединение..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Подключено (кроме звонков)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Подключено (кроме аудио)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Подключено (кроме сообщений)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Подключено (кроме звонков и аудио)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Подключено, уровень заряда батареи: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Подключено (кроме звонков), уровень заряда батареи: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 8abc51a27468..d67060ebb14e 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"විසන්ධි විය"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"අබලයි"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP වින්‍යාස කිරීම අසාර්ථකයි"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"අඩු ගුණත්වයේ ජාලය හේතුවෙන් සම්බන්ධ නොවීය"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi සම්බන්ධතාව අසාර්ථකයි"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"සත්‍යාපනයේ ගැටලුවකි"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"සම්බන්ධ විය නොහැකිය"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' වෙත සම්බන්ධ විය නොහැකිය"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ස්වයංක්‍රිය නැවත සම්බන්ධ නොවනු ඇත"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"අන්තර්ජාල ප්‍රවේශය නැත"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> විසින් සුරකින ලදී"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"මනුගත ජාලයට සම්බන්ධ කර ඇත"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s හරහා ස්වයංක්‍රියව සම්බන්ධ විය"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ජාල ශ්‍රේණිගත සපයන්නා හරහා ස්වයංක්‍රියව සම්බන්ධ විය"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s හරහා සම්බන්ධ විය"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> හරහා සම්බන්ධයි"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s හරහා ලබා ගැනීමට හැකිය"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"ලියාපදිංචි වීමට තට්ටු කරන්න"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"අන්තර්ජාලය නැත"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"පුද්ගලික DNS සේවාදායකයට ප්‍රවේශ වීමට නොහැකිය"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"අන්තර්ජාලය නැත"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"පිරීම අවශ්‍යයි"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"ප්‍රවේශ ලක්ෂ්‍ය තාවකාලිකව පිරී ඇත"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s හරහා සම්බන්ධ විය"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s හරහා ලබා ගැනීමට හැකිය"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> විවෘත කරමින්"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"සබැඳීමට නොහැකි විය"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"ලියාපදිංචිය සම්පූර්ණ කරමින්…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"ලියාපදිංචිය සම්පූර්ණ කළ නොහැකි විය. නැවත උත්සාහ කිරීමට තට්ටු කරන්න."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"ලියාපදිංචිය සම්පූර්ණයි. සබැඳෙමින්…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"ඉතා මන්දගාමී"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"මන්දගාමී"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"හරි"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"මධ්‍යම"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"වේගවත්"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"ඉතා වේගවත්"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"කල් ඉකුත් විය"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"යුගල කරමින්…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"සම්බන්ධිතයි (දුරකථනය නැත) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"සම්බන්ධිතයි (මාධ්‍ය නැත)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"සම්බන්ධිතයි (පණිවිඩ ප්‍රවේශය නැත)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"සම්බන්ධිතයි (දුරකථනය හෝ මාධ්‍ය නැත) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"සම්බන්ධිතයි, බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"සම්බන්ධිතයි (දුරකථනය නැත), බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 3a8e07814661..198289329941 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Odpojené"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Vypnuté"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Zlyhanie konfigurácie adresy IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Nepripojené z dôvodu siete nízkej kvality"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Zlyhanie pripojenia Wi‑Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problém s overením"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nedá sa pripojiť"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"K sieti <xliff:g id="AP_NAME">%1$s</xliff:g> sa nedá pripojiť"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nedôjde k automatickému pripojeniu"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Žiadny prístup k internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Uložila aplikácia <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Pripojené k meranej sieti"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky pripojené prostredníctvom %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky pripojené prostredníctvom poskytovateľa hodnotenia siete"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Pripojené prostredníctvom %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Pripojené prostredníctvom siete <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"K dispozícii prostredníctvom %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Prihláste sa klepnutím"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Bez internetu"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Žiadny internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Vyžaduje sa prihlásenie"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Prístupový bod je dočasne plný"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Pripojené prostredníctvom operátora %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"K dispozícii prostredníctvom operátora %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Otvára sa <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Nepodarilo sa pripojiť"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Dokončuje sa registrácia…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Registráciu sa nepodarilo dokončiť. Klepnutím to skúste znova."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registrácia je dokončená. Pripája sa…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Veľmi pomalá"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Pomalá"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Stredná"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Rýchla"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Veľmi rýchla"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Vypršalo"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Páruje sa..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Pripojené k zariadeniu <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (bez telefónu)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Pripojené k zariadeniu <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (bez médií)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Pripojené k <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (bez prístupu k správam)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Pripojené k <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> (bez telefónu a médií)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Pripojené k zariadeniu <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>, úroveň batérie <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Pripojené k zariadeniu <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> (bez telefónu), úroveň batérie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 9f1aecb36722..262ecfdca78d 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Ni povezave"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Onemogočeno"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Konfiguracija IP-ja ni uspela"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Ni povezano zaradi slabe kakovosti omrežja"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Povezava prek Wi-Fi-ja ni uspela"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Težava s preverjanjem pristnosti"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Povezava ni mogoča"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Povezava z aplikacijo »<xliff:g id="AP_NAME">%1$s</xliff:g>« ni mogoča"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Samodejna vnovična vzpostavitev povezave se ne bo izvedla"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ni dostopa do interneta"</string>
<string name="saved_network" msgid="7143698034077223645">"Shranil(-a): <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Povezano z omrežjem z omejenim prenosom podatkov"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Samodejno vzpostavljena povezava prek: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Samodejno vzpostavljena povezava prek ponudnika ocenjevanja omrežij"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Vzpostavljena povezava prek: %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Povezava vzpostavljena prek omrežja <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Na voljo prek: %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Dotaknite se, če se želite registrirati"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Ni internetne povezave"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Brez internetne povezave"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Zahtevana je prijava"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Dostopna točka je trenutno zasedena"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Vzpostavljena povezava prek: %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Na voljo prek: %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Odpiranje ponudnika <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Povezave ni bilo mogoče vzpostaviti"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Dokončevanje registracije …"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Registracije ni bilo mogoče dokončati. Če želite poskusiti znova, se dotaknite."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registracija je končana. Povezovanje …"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Zelo počasna"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Počasna"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"V redu"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Srednje hitra"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Hitra"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Zelo hitra"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Poteklo"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Seznanjanje ..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Povezano (brez telefona) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Povezano (brez predstavnosti) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Povezano (brez dostopa do sporočil) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Povezano (brez telefona/predstavnosti) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Povezano, raven napolnjenosti baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Povezano (brez telefona), raven napolnjenosti baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 515f9631e982..d0f1f7c0133b 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Shkëputur"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Të çaktivizuara"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Dështim në konfigurimin e IP-së"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Nuk është lidhur për shkak të rrjetit me cilësi të dobët"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Dështim i lidhjes WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problem me vërtetimin"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Nuk mund të lidhet"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Nuk mund të lidhet me \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nuk do të lidhet automatikisht"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nuk ka qasje në internet"</string>
<string name="saved_network" msgid="7143698034077223645">"E ruajtur nga <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Lidhur me një rrjet me matje"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Lidhur automatikisht përmes %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Lidhur automatikisht nëpërmjet ofruesit të vlerësimit të rrjetit"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"E lidhur përmes %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Lidhur përmes <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"E mundshme përmes %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Trokit për t\'u regjistruar"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Nuk ka internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Serveri privat DNS nuk mund të qaset"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Nuk ka internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Kërkohet identifikimi"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Pika e qasjes është përkohësisht plot"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"E lidhur përmes %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"E disponueshme përmes %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Po hapet <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Nuk mund të lidhej"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Po përfundon regjistrimin…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Regjistrimi nuk mund të përfundonte. Trokit për të provuar përsëri."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Regjistrimi përfundoi. Po lidhet…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Shumë e ulët"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"E ngadaltë"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Në rregull"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Mesatare"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"E shpejtë"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Shumë e shpejtë"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Skaduar"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Po çiftohet..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"E lidhur (pa telefon)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"E lidhur (pa media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"E lidhur (nuk ka qasje te mesazhet)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"E lidhur (pa telefon ose media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"E lidhur, bateria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"E lidhur (pa telefon), bateria <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 61496ae2ff25..b102d3ad5ecc 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Веза је прекинута"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Онемогућено"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP конфигурација је отказала"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Није повезано због лошег квалитета мреже"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi веза је отказала"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Проблем са потврдом идентитета"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Повезивање није успело"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Повезивање са „<xliff:g id="AP_NAME">%1$s</xliff:g>“ није успело"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Аутоматско повезивање није успело"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Нема приступа интернету"</string>
<string name="saved_network" msgid="7143698034077223645">"Сачувао/ла је <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Повезани сте на мрежу са ограничењем"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Аутоматски повезано преко %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аутоматски повезано преко добављача оцене мреже"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Веза је успостављена преко приступне тачке %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Повезано преко: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Доступна је преко приступне тачке %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Додирните да бисте се регистровали"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Нема интернета"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Приступ приватном DNS серверу није успео"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Нема интернета"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Треба да се пријавите"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Приступна тачка је привремено заузета"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Повезано преко %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Доступно преко %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Отвара се <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Повезивање није успело"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Регистрација се довршава…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Довршавање регистрације није успело. Додирните да бисте пробали поново."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Регистрација је довршена. Повезује се…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Веома спора"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Спора"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Потврди"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Средња"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Брза"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Веома брза"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Истекло"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Упаривање..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Повезано (без телефона): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Повезано (без медија): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Повезано је (без приступа порукама): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Повезано (без телефона или медија): <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Повезано, ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Повезано (без телефона), ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 943ba5f61ccf..8c12372ff7d1 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Frånkopplad"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Inaktiverad"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP-konfigurationsfel"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Ingen anslutning på grund av låg kvalitet på nätverket"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wifi-anslutningsfel"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Autentiseringsproblem"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Det gick inte att ansluta"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Det gick inte att ansluta till <xliff:g id="AP_NAME">%1$s</xliff:g>"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Det går inte att ansluta automatiskt"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetåtkomst"</string>
<string name="saved_network" msgid="7143698034077223645">"Sparades av <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ansluten till nätverk med datapriser"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiskt ansluten via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiskt ansluten via leverantör av nätverksbetyg"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Anslutet via %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Anslutet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Tillgängligt via %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Tryck för att logga in"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Inget internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Det går inte att komma åt den privata DNS-servern."</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Inget internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Inloggning krävs"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Åtkomstpunkten har inga platser över för tillfället"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Anslutet via %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Tillgängligt via %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Öppnar <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Det gick inte att ansluta"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Registreringen slutförs …"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Det gick inte att slutföra registreringen. Tryck för att försöka igen."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registrering slutförd. Ansluter …"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Mycket långsam"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Långsam"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Okej"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Medelsnabb"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Snabb"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Mycket snabb"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Har upphört att gälla"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Parkoppling…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Ansluten (ingen mobil)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Ansluten (inga medier)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Ansluten (ingen meddelandeåtkomst)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Ansluten (ingen mobil och inga medier) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Ansluten, batterinivå <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Ansluten (ingen mobil), batterinivå <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index b7124a6306ff..72a3751cee08 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Hujaunganishwa"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Imezimwa"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Haikuweza Kusanidi IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Haijaunganishwa kwa sababu intaneti si thabiti"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Haikuweza Kuunganisha kwenye WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Tatizo la uthibitishaji"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Imeshindwa kuunganisha"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Imeshindwa kuunganisha kwenye \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Haiwezi kuunganisha kiotomatiki"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Hakuna muunganisho wa intaneti"</string>
<string name="saved_network" msgid="7143698034077223645">"Ilihifadhiwa na <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Imeunganishwa kwenye mtandao unaopima data"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Imeunganishwa kiotomatiki kupitia %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Imeunganishwa kiotomatiki kupitia mtoa huduma wa ukadiriaji wa mtandao"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Imeunganishwa kupitia %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Imeunganishwa kupitia <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Inapatikana kupitia %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Gusa ili ujisajili"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Hakuna intaneti"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Hakuna intaneti"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Unahitaji kuingia katika akaunti"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Lango la mtandao lina shughuli nyingi kwa sasa"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Imeunganishwa kupitia %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Inapatikana kupitia %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Inafungua <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Imeshindwa kuunganisha"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Inakamilisha usajili…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Imeshindwa kukamilisha usajili. Gusa ili ujaribu tena."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Imekamilisha kusajili. Inaunganisha…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Polepole Sana"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Polepole"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Sawa"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Wastani"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Haraka"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Haraka Sana"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Muda umeisha"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Inaoanisha..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Imeunganishwa (hamna simu)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Imeunganishwa (hamna kifaa cha sauti)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Imeunganishwa (haifikii ujumbe)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Imeunganishwa (hamna simu au kifaa cha sauti)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Imeunganishwa, kiasi cha chaji ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Imeunganishwa (hamna simu), kiasi cha chaji ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 5b3aae3304c0..8714958b1993 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"தொடர்பு துண்டிக்கப்பட்டது"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"முடக்கப்பட்டது"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP உள்ளமைவில் தோல்வி"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"தரம் குறைவான நெட்வொர்க்கின் காரணமாக, இணைக்கப்படவில்லை"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"வைஃபை இணைப்பில் தோல்வி"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"அங்கீகரிப்புச் சிக்கல்"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"இணைக்க முடியவில்லை"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' உடன் இணைக்க முடியவில்லை"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"தானாக இணைக்கப்படாது"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"இண்டர்நெட் அணுகல் இல்லை"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> சேமித்தது"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"கட்டண நெட்வொர்க்குடன் இணைக்கப்பட்டுள்ளது"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s மூலம் தானாக இணைக்கப்பட்டது"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"நெட்வொர்க் மதிப்பீடு வழங்குநரால் தானாக இணைக்கப்பட்டது"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s வழியாக இணைக்கப்பட்டது"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> மூலம் இணைக்கப்பட்டது"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s வழியாகக் கிடைக்கிறது"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"பதிவு செய்யத் தட்டவும்"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"இணைய இணைப்பு இல்லை"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"இணைய இணைப்பு இல்லை"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"உள்நுழைய வேண்டும்"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"தற்காலிகமாக அணுகல் புள்ளி நிரம்பியுள்ளது"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s வழியாக இணைக்கப்பட்டது"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s வழியாகக் கிடைக்கிறது"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> திறக்கப்படுகிறது"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"இணைக்க முடியவில்லை"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"பதிவு செய்வது நிறைவடைகிறது…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"பதிவு செய்வதை நிறைவுசெய்ய இயலவில்லை மீண்டும் முயற்சிக்கத் தட்டவும்."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"பதிவு செய்வது நிறைவடைந்தது. இணைக்கிறது…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"மிகவும் வேகம் குறைவானது"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"வேகம் குறைவு"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"சரி"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"நடுத்தரம்"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"வேகம்"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"மிகவும் வேகமானது"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"காலாவதியாகிவிட்டது"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"இணைக்கிறது..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> இணைக்கப்பட்டது (மொபைல் இல்லை)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> இணைக்கப்பட்டது (மீடியா இல்லை)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> இணைக்கப்பட்டது (செய்தி அணுகல் இல்லை)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> இணைக்கப்பட்டது (மொபைல்/மீடியா இல்லை)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"இணைக்கப்பட்டது, பேட்டரி <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"இணைக்கப்பட்டது (மொபைல் இல்லை), பேட்டரி <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index df9b0ef4ad4d..e35bd764650d 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"డిస్‌కనెక్ట్ అయ్యింది"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"డిజేబుల్ చేయబడింది"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP కాన్ఫిగరేషన్ వైఫల్యం"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"తక్కువ క్వాలిటీ నెట్‌వర్క్ కారణంగా కనెక్ట్ చేయబడలేదు"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi కనెక్షన్ వైఫల్యం"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ప్రామాణీకరణ సమస్య"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"కనెక్ట్ చేయడం సాధ్యపడదు"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'కు కనెక్ట్ చేయడం సాధ్యపడదు"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ఆటోమేటిక్‌గా కనెక్ట్ కాదు"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"డేటా నియంత్రణ నెట్‌వర్క్‌కు కనెక్ట్ చేయబడింది"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ద్వారా ఆటోమేటిక్‌గా కనెక్ట్ చేయబడింది"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"నెట్‌వర్క్ రేటింగ్ ప్రదాత ద్వారా ఆటోమేటిక్‌గా కనెక్ట్ చేయబడింది"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా కనెక్ట్ చేయబడింది"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ద్వారా అందుబాటులో ఉంది"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"సైన్ అప్ చేయడానికి నొక్కండి"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ఇంటర్నెట్ లేదు"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"ప్రైవేట్ DNS సర్వర్‌ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ఇంటర్నెట్ లేదు"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"సైన్ ఇన్ చేయాలి"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"యాక్సెస్ పాయింట్ తాత్కాలికంగా నిండుకుంది"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s ద్వారా అందుబాటులో ఉంది"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> తెరవబడుతోంది"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"కనెక్ట్ చేయడం సాధ్యపడలేదు"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"సైన్ అప్ పూర్తివుతోంది…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"సైన్ అప్‌ను పూర్తి చేయడం సాధ్య పడలేదు. మళ్లీ ప్రయత్నించడానికి నొక్కండి."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"సైన్ అప్ పూర్తయింది. కనెక్ట్ చేయబడుతోంది…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"చాలా నెమ్మది"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"నెమ్మది"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"సరే"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"మధ్యస్థం"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"వేగవంతం"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"చాలా వేగవంతం"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"గడువు ముగిసింది"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"పెయిరింగ్..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"కనెక్ట్ చేయబడింది (ఫోన్ కాదు)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"కనెక్ట్ చేయబడింది (మీడియా కాదు)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"కనెక్ట్ చేయబడింది (సందేశ యాక్సెస్ లేదు)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"కనెక్ట్ చేయబడింది (ఫోన్ లేదా మీడియా కాదు)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"కనెక్ట్ చేయబడింది, బ్యాటరీ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"కనెక్ట్ చేయబడింది (ఫోన్ కాదు), బ్యాటరీ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index a8744fa46356..f386e5da132a 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"ยกเลิกการเชื่อมต่อแล้ว"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"ปิดอยู่"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"การกำหนดค่า IP ล้มเหลว"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"ไม่ได้เชื่อมต่อเนื่องจากเครือข่ายคุณภาพต่ำ"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"การเชื่อมต่อ Wi-Fi ล้มเหลว"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"ปัญหาในการตรวจสอบสิทธิ์"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"ไม่สามารถเชื่อมต่อ"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"ไม่สามารถเชื่อมต่อกับ \"<xliff:g id="AP_NAME">%1$s</xliff:g>\""</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"จะไม่เชื่อมต่อโดยอัตโนมัติ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"เข้าถึงอินเทอร์เน็ตไม่ได้"</string>
<string name="saved_network" msgid="7143698034077223645">"บันทึกโดย<xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"เชื่อมต่อกับเครือข่ายแบบจำกัดปริมาณแล้ว"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"เชื่อมต่ออัตโนมัติผ่าน %1$s แล้ว"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"เชื่อมต่ออัตโนมัติผ่านผู้ให้บริการการจัดอันดับเครือข่าย"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"เชื่อมต่อผ่าน %1$s แล้ว"</string>
<string name="connected_via_app" msgid="3532267661404276584">"เชื่อมต่อแล้วผ่าน <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"พร้อมใช้งานผ่านทาง %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"แตะเพื่อลงชื่อสมัครใช้"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"ไม่มีอินเทอร์เน็ต"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"ไม่มีอินเทอร์เน็ต"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"ต้องลงชื่อเข้าใช้"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"จุดเข้าใช้งานเต็มชั่วคราว"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"เชื่อมต่อผ่าน %1$s แล้ว"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"พร้อมใช้งานผ่านทาง %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"กำลังเปิด <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"เชื่อมต่อไม่ได้"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"กำลังลงชื่อสมัครใช้ให้เสร็จสิ้น…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"ลงชื่อสมัครใช้ให้เสร็จสิ้นไม่ได้ แตะเพื่อลองอีกครั้ง"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"การลงชื่อสมัครใช้เสร็จสมบูรณ์ กำลังเชื่อมต่อ…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"ช้ามาก"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"ช้า"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ตกลง"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"ปานกลาง"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"เร็ว"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"เร็วมาก"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"หมดอายุแล้ว"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"กำลังจับคู่อุปกรณ์..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"เชื่อมต่อแล้ว (ไม่รวมโทรศัพท์)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"เชื่อมต่อแล้ว (ไม่รวมสื่อ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"เชื่อมต่อแล้ว (ไม่มีการเข้าถึงข้อความ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"เชื่อมต่อแล้ว (ไม่รวมโทรศัพท์หรือสื่อ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"เชื่อมต่อแล้ว แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"เชื่อมต่อแล้ว (ไม่รวมโทรศัพท์) แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index bdcdbaca83da..db2738276bb9 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Nadiskonekta"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Naka-disable"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Pagkabigo ng Configuration ng IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Hindi nakakonekta dahil mababa ang kalidad ng network"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Pagkabigo ng Koneksyon sa WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Problema sa pag-authenticate"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Hindi makakonekta"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Hindi makakonekta sa \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Hindi awtomatikong kokonekta"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Walang access sa internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Na-save ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Nakakonekta sa nakametrong network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Awtomatikong nakakonekta sa pamamagitan ng %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Awtomatikong nakakonekta sa pamamagitan ng provider ng rating ng network"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Nakakonekta sa pamamagitan ng %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Nakakonekta sa pamamagitan ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Available sa pamamagitan ng %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"I-tap para mag-sign up"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Walang internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Hindi ma-access ang pribadong DNS server"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Walang internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Kinakailangang mag-sign in"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Pansamantalang puno ang access point"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Nakakonekta sa pamamagitan ng %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Available sa pamamagitan ng %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Binubuksan ang <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Hindi makakonekta"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Kinukumpleto ang pag-sign up…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Hindi makumpleto ang pag-sign up. I-tap para subukang muli."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Kumpleto na ang pag-sign up. Kumokonekta…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Napakabagal"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Mabagal"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Katamtaman"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Mabilis"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Napakabilis"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Nag-expire na"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Pinapares…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Nakakonekta (walang telepono)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Nakakonekta (walang media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Nakakonekta (walang access sa mensahe)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Nakakonekta (walang telepono o media)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Nakakonekta, baterya <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Nakakonekta (walang telepono), baterya <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index de88c2c04bc9..8f4bae49cea5 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Bağlı değil"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Devre dışı"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP Yapılandırması Hatası"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Ağ kalitesi düşük olduğundan bağlanamadı"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Kablosuz Bağlantı Hatası"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Kimlik doğrulama sorunu"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Bağlanılamıyor"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\"<xliff:g id="AP_NAME">%1$s</xliff:g>\" ağına bağlanılamıyor"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Otomatik olarak bağlanma"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"İnternet erişimi yok"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tarafından kaydedildi"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Sayaçlı ağa bağlı"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzerinden otomatik olarak bağlı"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ağ derecelendirme sağlayıcı aracılığıyla otomatik olarak bağlandı"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s üzerinden bağlı"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ile bağlandı"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s üzerinden kullanılabilir"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Kaydolmak için dokunun"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"İnternet yok"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Gizli DNS sunucusuna erişilemiyor"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"İnternet yok"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Oturum açılması gerekiyor"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Erişim noktası geçici olarak dolu"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s üzerinden bağlı"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s üzerinden kullanılabilir"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> açılıyor"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Bağlanılamadı"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Kayıt işlemi tamamlanıyor…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Kayıt işlemi tamamlanamadı. Tekrar denemek için dokunun."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Kayıt tamamlandı. Bağlanıyor…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Çok Yavaş"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Yavaş"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Tamam"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Orta"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Hızlı"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Çok Hızlı"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Süresi sona erdi"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Eşleniyor…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> cihazına bağlandı (telefon yok)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> cihazına bağlandı (medya yok)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> cihazına bağlandı (mesaj erişimi yok)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> cihazına bağlandı (telefon veya medya yok)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> cihazına bağlandı, pil <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> cihazına bağlandı (telefon yok), pil <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index a438741b2067..a0c68c140f91 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Від’єднано"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Вимкнено"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Помилка конфігурації IP-адреси"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Не під’єднано через низьку якість мережі"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Помилка з’єднання Wi-Fi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Проблема з автентифікацією"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Не вдається під’єднатись"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Не вдається під’єднатися до мережі <xliff:g id="AP_NAME">%1$s</xliff:g>"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не під’єднуватиметься автоматично"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Немає доступу до Інтернету"</string>
<string name="saved_network" msgid="7143698034077223645">"Збережено додатком <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Установлено з\'єднання з мережею з тарифікацією трафіку"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично під’єднано через %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично під’єднано через постачальника оцінки якості мережі"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Під’єднано через %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Підключено через додаток <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Доступ через %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Торкніться, щоб увійти"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Немає Інтернету"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Немає доступу до приватного DNS-сервера"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Немає Інтернету"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Потрібно ввійти в обліковий запис"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Точка доступу тимчасово переповнена"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Під’єднано через мережу %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Доступ через мережу %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> відкривається"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Не вдалося підключитись"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Завершення реєстрації…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Не вдалося завершити реєстрацію. Торкніться, щоб повторити спробу."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Реєстрацію завершено. Підключення…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Дуже повільна"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Повільна"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ОК"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Середня"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Швидка"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Дуже швидка"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Термін дії минув"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Підключення…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> під’єднано (без телефона)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> під’єдно (без медіа)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> під’єднано (без доступу до повідомлень)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> під’єднано (без телефона й медіа)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> під’єднано, заряд акумулятора – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> під’єднано (без телефона), заряд акумулятора – <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 2e409ba9388a..d0a120429bb6 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"غیر منسلک"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"غیر فعال"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"‏IP کنفیگریشن کی ناکامی"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"کم معیاری نیٹ ورک کی وجہ سے منسلک نہیں ہے"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"‏WiFi کنکشن کی ناکامی"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"توثیق کا مسئلہ"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"منسلک نہیں ہو سکتا ہے"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' سے منسلک نہیں ہو سکتا ہے"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"خودکار طور پر منسلک نہیں ہو گا"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"انٹرنیٹ تک کوئی رسائی نہیں"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> کی جانب سے محفوظ کردہ"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"میٹرڈ نیٹ ورک سے منسلک ہے"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"‏‎%1$s کے ذریعے از خود منسلک کردہ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"نیٹ ورک درجہ بندی کے فراہم کنندہ کے ذریعے از خود منسلک"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"‏منسلک بذریعہ ‎%1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> کے ذریعے منسلک"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"‏دستیاب بذریعہ ‎%1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"سائن اپ کے لیے تھپتھپائیں"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"انٹرنیٹ نہیں ہے"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"‏نجی DNS سرور تک رسائی حاصل نہیں کی جا سکی"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"انٹرنیٹ نہیں ہے"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"سائن ان درکار ہے"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"رسائی پوائنٹ عارضی طور پر فُل ہے"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"‏منسلک بذریعہ ‎%1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"‏دستیاب بذریعہ ‎%1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> کھل رہا ہے"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"منسلک نہیں کیا جا سکا"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"سائن اپ مکمل ہو رہا ہے…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"سائن اپ مکمل نہیں ہو سکا۔ دوبارہ کوشش کرنے کے لیے تھپتھپائیں۔"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"سائن اپ مکمل ہو گیا۔ منسلک ہو رہا ہے…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"بہت سست"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"سست"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"ٹھیک ہے"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"متوسط"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"تیز"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"بہت تیز"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"میعاد ختم ہو گئی"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"جوڑا بنایا جا رہا ہے…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"منسلک ہے (فون کے علاوہ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"منسلک ہے (میڈیا کے علاوہ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"منسلک ہے (پیغام تک رسائی نہیں ہے)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"منسلک ہے (فون یا میڈیا کے علاوہ)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"منسلک ہو گیا، بٹری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"منسلک ہے (فون کے علاوہ)، بیٹری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index eeec1010bc42..1738e011287a 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Ulanmagan"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Yoqilmagan"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP manzilini sozlab bo‘lmadi"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Sifatsiz tarmoq sababli ulanib bo‘lmadi"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Wi-Fi ulanishini o‘rnatib bo‘lmadi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Tekshiruvda muammo"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Tarmoqqa ulanilmadi"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"“<xliff:g id="AP_NAME">%1$s</xliff:g>” nomli tarmoqqa ulanilmadi"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik ravishda ulanilmaydi"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Internet aloqasi yo‘q"</string>
<string name="saved_network" msgid="7143698034077223645">"Saqlangan: <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Trafik hisoblanadigan tarmoqqa ulandi"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s orqali avtomatik ulandi"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tarmoqlar reytingi muallifi orqali avtomatik ulandi"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s orqali ulangan"</string>
<string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> orqali ulandi"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s orqali ishlaydi"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Yozilish uchun bosing"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Internetga ulanmagansiz"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Xususiy DNS server ishlamayapti"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Internet yo‘q"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Hisob bilan kirish zarur"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Internet kirish nuqtasi vaqtinchalik to‘lgan"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"%1$s orqali ulangan"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"%1$s orqali ishlaydi"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g> ochilmoqda"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Ulanmadi"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Registratsiya tamomlanmoqda…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Registratsiya tamomlanmadi. Qayta urinish uchun tegining."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Registratsiya qilindi. Ulanmoqda…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Juda sekin"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Sekin"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"OK"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"O‘rtacha"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Tez"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Juda tez"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Muddati tugagan"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Juftlanmoqda…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> ulandi (telefondan tashqari)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> ulandi (mediadan tashqari)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> ulandi (xabarlarga ruxsatsiz)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g> ulandi (telefon yoki mediadan tashqari)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> ulandi, batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"<xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g> ulandi (telefondan tashqari), batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index fc11dec9eed4..f7f4c0e3abb3 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Đã ngắt kết nối"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Đã tắt"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Lỗi cấu hình IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Không được kết nối do mạng chất lượng kém"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Lỗi kết nối WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Sự cố xác thực"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Không thể kết nối"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Không thể kết nối với \'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sẽ không tự động kết nối"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Không có quyền truy cập Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Do <xliff:g id="NAME">%1$s</xliff:g> lưu"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Đã kết nối với mạng có đo lượng dữ liệu"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Tự động được kết nối qua %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tự động được kết nối qua nhà cung cấp dịch vụ xếp hạng mạng"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Được kết nối qua %1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Đã kết nối qua <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Có sẵn qua %1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Nhấn để đăng ký"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Không có Internet"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Không thể truy cập máy chủ DNS riêng tư"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Không có Internet"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Yêu cầu đăng nhập"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Điểm truy cập tạm thời đã đạt đến giới hạn số lượng thiết bị truy cập."</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Được kết nối qua %1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Có sẵn qua %1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Đang mở <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Không thể kết nối"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Đang hoàn tất đăng ký…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Không thể hoàn tất đăng ký. Nhấn để thử lại."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Đã hoàn tất đăng ký. Đang kết nối…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Rất chậm"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Chậm"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"Khá tốt"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Trung bình"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Nhanh"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Rất nhanh"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Đã hết hạn"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Đang ghép nối…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Đã kết nối (không có điện thoại) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Đã kết nối (không có phương tiện) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Đã kết nối (không có quyền truy cập tin nhắn) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Đã kết nối (không có điện thoại hoặc phương tiện) <xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Đã kết nối, mức pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Đã kết nối (không có điện thoại), mức pin <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index a4fa1d9c7a36..f536bb9e3511 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"已断开连接"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP 配置失败"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"网络质量较差,因此未连接"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WLAN 连接失败"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"身份验证出现问题"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"无法连接"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"无法连接到“<xliff:g id="AP_NAME">%1$s</xliff:g>”"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"无法自动连接"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"无法访问互联网"</string>
<string name="saved_network" msgid="7143698034077223645">"由“<xliff:g id="NAME">%1$s</xliff:g>”保存"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已连接到按流量计费的网络"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"已通过%1$s自动连接"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已自动连接(通过网络评分服务提供方)"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"已通过%1$s连接"</string>
<string name="connected_via_app" msgid="3532267661404276584">"已通过<xliff:g id="NAME">%1$s</xliff:g>连接到网络"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"可通过%1$s连接"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"点按即可注册"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"无法访问互联网"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"无法访问私人 DNS 服务器"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"无法访问互联网"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"必须登录"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"接入点暂时满载"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"已通过%1$s连接"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"可通过%1$s连接"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"正在打开<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"无法连接"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"正在完成注册…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"无法完成注册。点按即可重试。"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"注册完毕。正在连接…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"很慢"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"慢"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"良好"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"适中"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"快"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"很快"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"已失效"</string>
@@ -98,14 +89,13 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"正在配对..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"已连接(无手机信号)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"已连接(无媒体信号)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"已连接(无消息访问权限)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"已连接(无手机或媒体信号)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"已连接,电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"已连接(无手机信号),电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp_battery_level" msgid="6499078454894324287">"已连接(无媒体信号),电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp_battery_level" msgid="8477440576953067242">"已连接(无手机或媒体信号),电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> <xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_active_battery_level" msgid="3450745316700494425">"使用中,电池电量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
- <string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"已启用,左:目前电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>;右:目前电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
+ <string name="bluetooth_active_battery_level_untethered" msgid="2706188607604205362">"使用中,左:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> 电量,右:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> 电量"</string>
<string name="bluetooth_battery_level" msgid="2893696778200201555">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> 的电量"</string>
<string name="bluetooth_battery_level_untethered" msgid="4002282355111504349">"左:目前电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>;右:目前电量为 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
<string name="bluetooth_active_no_battery_level" msgid="4155462233006205630">"使用中"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 369fe522b7c8..357302b96c90 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"已中斷連線"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP 設定失敗"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"網絡品質欠佳,因此無法連線"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi 連線失敗"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"驗證問題"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"無法連線"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"無法連線至「<xliff:g id="AP_NAME">%1$s</xliff:g>」"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"不會自動連線"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"無法連接互聯網"</string>
<string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已連結至按用量收費的網絡"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網絡評分供應商自動連線"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string>
<string name="connected_via_app" msgid="3532267661404276584">"已透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"可透過 %1$s 連線"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"輕按即可登入"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"沒有互聯網連線"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"無法存取私人 DNS 伺服器"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"沒有互聯網連線"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"必須登入"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"存取點暫時已滿"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"已透過 %1$s 連線"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"可透過 %1$s 連線"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"正在開啟 <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"無法連接"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"正在完成申請…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"無法完成申請。輕按即可重試。"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"已完成申請。連接中…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"非常慢"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"慢"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"良好"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"適中"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"快"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"非常快"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"已過期"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"正在配對..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"已連接「<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>」(無手機音訊)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"已連接「<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>」(無媒體音訊)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"已連接「<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>」(無訊息存取權)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"已連接「<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>」(無手機或媒體音訊)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"已連接,電量為 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"已連接 (無手機音訊),電量為 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 8219811d572e..4ae28304a17c 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"已中斷連線"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"已停用"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"IP 設定失敗"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"網路品質不佳,因此未連線"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"WiFi 連線失敗"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"驗證問題"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"無法連線"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"無法連線至「<xliff:g id="AP_NAME">%1$s</xliff:g>」"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"無法自動連線"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"沒有可用的網際網路連線"</string>
<string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"已連線到計量付費網路"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網路評分供應商自動連線"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string>
<string name="connected_via_app" msgid="3532267661404276584">"透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"可透過 %1$s 使用"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"輕觸即可註冊"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"沒有網際網路連線"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"無法存取私人 DNS 伺服器"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"沒有網際網路連線"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"必須登入"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"存取點暫時滿載"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"已透過 %1$s 連線"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"可透過 %1$s 使用"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"正在開啟 <xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"無法連線"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"正在完成註冊程序…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"無法完成註冊程序。輕觸即可重試。"</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"註冊完成。連線中…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"非常慢"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"慢"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"確定"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"適中"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"快"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"非常快"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"已失效"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"配對中…"</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"已連線到「<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>」(無手機音訊)"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"已連線到「<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>」(無媒體音訊)"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"已連線到「<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>」(無訊息存取權)"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"已連線到「<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>」(無手機或媒體音訊)"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"已連線,電量為 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"已連線 (無手機音訊),電量為 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 6ca4cc5fe789..c113dc8a799f 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -53,8 +53,6 @@
<string name="wifi_disconnected" msgid="7054450256284661757">"Inqamukile"</string>
<string name="wifi_disabled_generic" msgid="2651916945380294607">"Akusebenzi"</string>
<string name="wifi_disabled_network_failure" msgid="2660396183242399585">"Ukwehluleka kokulungiswa kwe-IP"</string>
- <string name="wifi_disabled_by_recommendation_provider" msgid="1302938248432705534">"Ayixhunyiwe ngenxa yenethiwekhi yekhwalithi ephansi"</string>
- <string name="wifi_disabled_wifi_failure" msgid="8819554899148331100">"Ukwehlulekla koxhumo le-WiFi"</string>
<string name="wifi_disabled_password_failure" msgid="6892387079613226738">"Inkinga yokufakazela ubuqiniso"</string>
<string name="wifi_cant_connect" msgid="5718417542623056783">"Ayikwazi ukuxhuma"</string>
<string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"Ayikwazi ukuxhumeka ku-\'<xliff:g id="AP_NAME">%1$s</xliff:g>\'"</string>
@@ -63,12 +61,9 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ngeke ize ixhumeke ngokuzenzakalela"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Akukho ukufinyelela kwe-inthanethi"</string>
<string name="saved_network" msgid="7143698034077223645">"Kulondolozwe ngu-<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Kuxhunywe kunethiwekhi eyenziwe imitha"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ixhumeke ngokuzenzakalela nge-%1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Kuxhunywe ngokuzenzakalelayo ngomhlinzeki wesilinganiso wenethiwekhi"</string>
- <string name="connected_via_passpoint" msgid="7735442932429075684">"Kuxhumeke nge-%1$s"</string>
<string name="connected_via_app" msgid="3532267661404276584">"Ixhumeke nge-<xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="available_via_passpoint" msgid="1716000261192603682">"Iyatholakala nge-%1$s"</string>
<string name="tap_to_sign_up" msgid="5356397741063740395">"Thepha ukuze ubhalisele"</string>
<string name="wifi_connected_no_internet" msgid="5087420713443350646">"Ayikho i-inthanethi"</string>
<string name="private_dns_broken" msgid="1984159464346556931">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
@@ -76,17 +71,13 @@
<string name="wifi_status_no_internet" msgid="3799933875988829048">"Ayikho i-inthanethi"</string>
<string name="wifi_status_sign_in_required" msgid="2236267500459526855">"Ukungena ngemvume kuyadingeka"</string>
<string name="wifi_ap_unable_to_handle_new_sta" msgid="5885145407184194503">"Iphoyinti lokufinyelela ligcwele okwesikhashana"</string>
- <string name="connected_via_carrier" msgid="1968057009076191514">"Kuxhumeke nge-%1$s"</string>
- <string name="available_via_carrier" msgid="465598683092718294">"Iyatholakala nge-%1$s"</string>
<string name="osu_opening_provider" msgid="4318105381295178285">"Ivula i-<xliff:g id="PASSPOINTPROVIDER">%1$s</xliff:g>"</string>
<string name="osu_connect_failed" msgid="9107873364807159193">"Ayikwazanga ukuxhumeka"</string>
<string name="osu_completing_sign_up" msgid="8412636665040390901">"Iqedela ukubhalisa…"</string>
<string name="osu_sign_up_failed" msgid="5605453599586001793">"Ayikwazanga ukuqedelela ukubhalisa. Thepha ukuze uzame futhi."</string>
<string name="osu_sign_up_complete" msgid="7640183358878916847">"Ukubhalisa kuqediwe. Iyaxhuma…"</string>
- <string name="speed_label_very_slow" msgid="8526005255731597666">"Phansi kakhulu"</string>
<string name="speed_label_slow" msgid="6069917670665664161">"Phansi"</string>
<string name="speed_label_okay" msgid="1253594383880810424">"KULUNGILE"</string>
- <string name="speed_label_medium" msgid="9078405312828606976">"Okumaphakathi"</string>
<string name="speed_label_fast" msgid="2677719134596044051">"Sheshayo"</string>
<string name="speed_label_very_fast" msgid="8215718029533182439">"Kushesha kakhulu"</string>
<string name="wifi_passpoint_expired" msgid="6540867261754427561">"Iphelelwe isikhathi"</string>
@@ -98,7 +89,6 @@
<string name="bluetooth_pairing" msgid="4269046942588193600">"Iyabhangqa..."</string>
<string name="bluetooth_connected_no_headset" msgid="2224101138659967604">"Ixhunyiwe (ayikho ifoni)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_a2dp" msgid="8566874395813947092">"Ixhunyiwe (ayikho imidiya)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
- <string name="bluetooth_connected_no_map" msgid="3381860077002724689">"Ixhunyiwe (akukho ukufinyelela kumlayezo)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_no_a2dp" msgid="2893204819854215433">"Ixhunyiwe (ayikho ifoni noma imidiya)<xliff:g id="ACTIVE_DEVICE">%1$s</xliff:g>"</string>
<string name="bluetooth_connected_battery_level" msgid="5410325759372259950">"Ixhunyiwe, ibhethri ngu-<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
<string name="bluetooth_connected_no_headset_battery_level" msgid="2661863370509206428">"Ixhunyiwe (ayikho ifoni), ibhethri ngu-<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g><xliff:g id="ACTIVE_DEVICE">%2$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 663e8e4b14d8..179573b4b9c7 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -158,18 +158,6 @@
<item>Opus</item>
</string-array>
- <!-- Values for Bluetooth Audio Codec selection preference. -->
- <string-array name="bluetooth_a2dp_codec_values" translatable="false" >
- <item>1000000</item>
- <item>0</item>
- <item>1</item>
- <item>2</item>
- <item>3</item>
- <item>4</item>
- <item>5</item>
- <item>6</item>
- </string-array>
-
<!-- Summaries for Bluetooth Audio Codec selection preference. [CHAR LIMIT=50]-->
<string-array name="bluetooth_a2dp_codec_summaries" >
<item>Use System Selection (Default)</item>
@@ -191,15 +179,6 @@
<item>96.0 kHz</item>
</string-array>
- <!-- Values for Bluetooth Audio Codec Sample Rate selection preference. -->
- <string-array name="bluetooth_a2dp_codec_sample_rate_values" translatable="false" >
- <item>0</item>
- <item>1</item>
- <item>2</item>
- <item>4</item>
- <item>8</item>
- </string-array>
-
<!-- Summaries for Bluetooth Audio Codec Sample Rate selection preference. [CHAR LIMIT=50]-->
<string-array name="bluetooth_a2dp_codec_sample_rate_summaries" >
<item>Use System Selection (Default)</item>
@@ -217,14 +196,6 @@
<item>32 bits/sample</item>
</string-array>
- <!-- Values for Bluetooth Audio Codec Bits Per Sample selection preference. -->
- <string-array name="bluetooth_a2dp_codec_bits_per_sample_values" translatable="false" >
- <item>0</item>
- <item>1</item>
- <item>2</item>
- <item>4</item>
- </string-array>
-
<!-- Summaries for Bluetooth Audio Codec Bits Per Sample selection preference. [CHAR LIMIT=50]-->
<string-array name="bluetooth_a2dp_codec_bits_per_sample_summaries" >
<item>Use System Selection (Default)</item>
@@ -240,13 +211,6 @@
<item>Stereo</item>
</string-array>
- <!-- Values for Bluetooth Audio Codec Channel Mode selection preference. -->
- <string-array name="bluetooth_a2dp_codec_channel_mode_values" translatable="false" >
- <item>0</item>
- <item>1</item>
- <item>2</item>
- </string-array>
-
<!-- Summaries for Bluetooth Audio Codec Channel Mode selection preference. [CHAR LIMIT=50]-->
<string-array name="bluetooth_a2dp_codec_channel_mode_summaries" >
<item>Use System Selection (Default)</item>
@@ -262,14 +226,6 @@
<item>Best Effort (Adaptive Bit Rate)</item>
</string-array>
- <!-- Values for Bluetooth Audio Codec LDAC Playback Quaility selection preference. -->
- <string-array name="bluetooth_a2dp_codec_ldac_playback_quality_values" translatable="false" >
- <item>1000</item>
- <item>1001</item>
- <item>1002</item>
- <item>1003</item>
- </string-array>
-
<!-- Summaries for Bluetooth Audio Codec LDAC Playback Quality selection preference. [CHAR LIMIT=70]-->
<string-array name="bluetooth_a2dp_codec_ldac_playback_quality_summaries" >
<item>Optimized for Audio Quality</item>
diff --git a/packages/SettingsLib/res/values/colors.xml b/packages/SettingsLib/res/values/colors.xml
index d2d747119bba..6b7e918ee5db 100644
--- a/packages/SettingsLib/res/values/colors.xml
+++ b/packages/SettingsLib/res/values/colors.xml
@@ -17,9 +17,6 @@
<resources>
<color name="disabled_text_color">#66000000</color> <!-- 38% black -->
- <color name="usage_graph_dots">@*android:color/tertiary_device_default_settings</color>
- <color name="list_divider_color">#64000000</color>
-
<color name="bt_color_icon_1">#b4a50e0e</color> <!-- 72% Material Red 900 -->
<color name="bt_color_icon_2">#b40d652d</color> <!-- 72% Material Green 900 -->
<color name="bt_color_icon_3">#b4e37400</color> <!-- 72% Material Yellow 900 -->
diff --git a/packages/SettingsLib/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 226b119ebb76..3ef3d3642262 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -19,16 +19,11 @@
<!-- The y translation to apply at the start in appear animations. -->
<dimen name="appear_y_translation_start">32dp</dimen>
- <!-- The translation for disappearing security views after having solved them. -->
- <dimen name="disappear_y_translation">-32dp</dimen>
-
<!-- Height of a user icon view -->
<dimen name="user_icon_view_height">24dp</dimen>
<!-- User spinner -->
- <dimen name="user_spinner_height">72dp</dimen>
<dimen name="user_spinner_padding">4dp</dimen>
<dimen name="user_spinner_padding_sides">20dp</dimen>
- <dimen name="user_spinner_item_height">56dp</dimen>
<!-- Lock icon for preferences locked by admin -->
<dimen name="restricted_icon_padding">4dp</dimen>
@@ -36,10 +31,8 @@
<dimen name="wifi_preference_badge_padding">8dip</dimen>
<!-- Usage graph dimens -->
- <dimen name="usage_graph_area_height">122dp</dimen>
<dimen name="usage_graph_margin_top_bottom">9dp</dimen>
<dimen name="usage_graph_labels_width">56dp</dimen>
- <dimen name="usage_graph_labels_padding">16dp</dimen>
<dimen name="usage_graph_divider_size">1dp</dimen>
@@ -64,22 +57,13 @@
<!-- Ratio between width and height -->
<fraction name="bt_battery_ratio_fraction">35%</fraction>
- <!-- Ratio of height between battery icon and bluetooth icon -->
- <fraction name="bt_battery_scale_fraction">75%</fraction>
-
<!-- Fraction value to smooth the edges of the battery icon. The path will be inset by this
fraction of a pixel.-->
<fraction name="battery_subpixel_smoothing_left">0%</fraction>
<fraction name="battery_subpixel_smoothing_right">0%</fraction>
- <!-- Zen mode panel: condition item button padding -->
- <dimen name="zen_mode_condition_detail_button_padding">8dp</dimen>
- <!-- Zen mode panel: spacing between condition items -->
- <dimen name="zen_mode_condition_detail_item_spacing">12dp</dimen>
<!-- Zen mode panel: spacing between two-line condition upper and lower lines -->
<dimen name="zen_mode_condition_detail_item_interline_spacing">4dp</dimen>
- <!-- Zen mode panel: bottom padding, a bit less than qs_panel_padding -->
- <dimen name="zen_mode_condition_detail_bottom_padding">4dp</dimen>
<!-- SignalDrawable -->
<dimen name="signal_icon_size">15dp</dimen>
@@ -93,9 +77,6 @@
<!-- Size of advanced icon -->
<dimen name="advanced_icon_size">18dp</dimen>
- <!-- Minimum width for the popup for updating a user's photo. -->
- <dimen name="update_user_photo_popup_min_width">300dp</dimen>
-
<dimen name="add_a_photo_circled_padding">6dp</dimen>
<dimen name="user_photo_size_in_user_info_dialog">112dp</dimen>
<dimen name="add_a_photo_icon_size_in_user_info_dialog">32dp</dimen>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 06d7bb49c809..2e0155b86e7f 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -91,10 +91,6 @@
<string name="wifi_disabled_generic">Disabled</string>
<!-- Status for networked disabled from a DNS or DHCP failure -->
<string name="wifi_disabled_network_failure">IP Configuration Failure</string>
- <!-- Status for networks disabled by the network recommendation provider -->
- <string name="wifi_disabled_by_recommendation_provider">Not connected due to low quality network</string>
- <!-- Status for networked disabled from a wifi association failure -->
- <string name="wifi_disabled_wifi_failure">WiFi Connection Failure</string>
<!-- Status for networks disabled from authentication failure (wrong password
or certificate). -->
<string name="wifi_disabled_password_failure">Authentication problem</string>
@@ -114,20 +110,14 @@
<string name="wifi_no_internet">No internet access</string>
<!-- Summary for saved networks -->
<string name="saved_network">Saved by <xliff:g id="name">%1$s</xliff:g></string>
- <!-- Summary for connect to metered access point [CHAR LIMIT=NONE] -->
- <string name="connected_to_metered_access_point">Connected to metered network</string>
<!-- Status message of Wi-Fi when it is automatically connected by a network recommendation provider. [CHAR LIMIT=NONE] -->
<string name="connected_via_network_scorer">Automatically connected via %1$s</string>
<!-- Status message of Wi-Fi when it is automatically connected by a default network recommendation provider. [CHAR LIMIT=NONE] -->
<string name="connected_via_network_scorer_default">Automatically connected via network rating provider</string>
- <!-- Status message of Wi-Fi when it is connected by Passpoint configuration. [CHAR LIMIT=NONE] -->
- <string name="connected_via_passpoint">Connected via %1$s</string>
<!-- Status message of Wi-Fi when it is connected by a app (via suggestion or network request). [CHAR LIMIT=NONE] -->
<string name="connected_via_app">Connected via <xliff:g id="name" example="Wifi App">%1$s</xliff:g></string>
- <!-- Status message of Wi-Fi when network has matching passpoint credentials. [CHAR LIMIT=NONE] -->
- <string name="available_via_passpoint">Available via %1$s</string>
<!-- Status message of OSU Provider network when not connected. [CHAR LIMIT=NONE] -->
<string name="tap_to_sign_up">Tap to sign up</string>
<!-- Package name for Settings app-->
@@ -153,11 +143,6 @@
<!-- Summary for networks failing to connect due to association rejection status 17, AP full -->
<string name="wifi_ap_unable_to_handle_new_sta">Access point temporarily full</string>
- <!-- Status message of Wi-Fi when it is connected to a Carrier Network. [CHAR LIMIT=NONE] -->
- <string name="connected_via_carrier">Connected via %1$s</string>
- <!-- Status message of Wi-Fi when an available network is a carrier network. [CHAR LIMIT=NONE] -->
- <string name="available_via_carrier">Available via %1$s</string>
-
<!-- Status message of OSU Provider upon initiating provisioning flow [CHAR LIMIT=NONE] -->
<string name="osu_opening_provider">Opening <xliff:g id="passpointProvider" example="Passpoint Provider">%1$s</xliff:g></string>
<!-- Status message of OSU Provider when connection fails [CHAR LIMIT=NONE] -->
@@ -169,14 +154,10 @@
<!-- Status message of OSU Provider on completing provisioning. [CHAR LIMIT=NONE] -->
<string name="osu_sign_up_complete">Sign-up complete. Connecting\u2026</string>
- <!-- Speed label for very slow network speed -->
- <string name="speed_label_very_slow">Very Slow</string>
<!-- Speed label for slow network speed -->
<string name="speed_label_slow">Slow</string>
<!-- Speed label for okay network speed -->
<string name="speed_label_okay">OK</string>
- <!-- Speed label for medium network speed -->
- <string name="speed_label_medium">Medium</string>
<!-- Speed label for fast network speed -->
<string name="speed_label_fast">Fast</string>
<!-- Speed label for very fast network speed -->
@@ -203,8 +184,6 @@
<string name="bluetooth_connected_no_headset">Connected (no phone)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for media audio. [CHAR LIMIT=40] -->
<string name="bluetooth_connected_no_a2dp">Connected (no media)<xliff:g id="active_device">%1$s</xliff:g></string>
- <!-- Bluetooth settings. Message when connected to a device, except for map. [CHAR LIMIT=40] -->
- <string name="bluetooth_connected_no_map">Connected (no message access)<xliff:g id="active_device">%1$s</xliff:g></string>
<!-- Bluetooth settings. Message when connected to a device, except for phone/media audio. [CHAR LIMIT=40] -->
<string name="bluetooth_connected_no_headset_no_a2dp">Connected (no phone or media)<xliff:g id="active_device">%1$s</xliff:g></string>
diff --git a/packages/SettingsLib/search/Android.bp b/packages/SettingsLib/search/Android.bp
index 45746d9848df..cfff519705f2 100644
--- a/packages/SettingsLib/search/Android.bp
+++ b/packages/SettingsLib/search/Android.bp
@@ -19,11 +19,11 @@ java_plugin {
name: "SettingsLib-annotation-processor",
processor_class: "com.android.settingslib.search.IndexableProcessor",
static_libs: [
- "javapoet-prebuilt-jar",
+ "javapoet",
],
srcs: [
"processor-src/**/*.java",
- "src/com/android/settingslib/search/SearchIndexable.java"
+ "src/com/android/settingslib/search/SearchIndexable.java",
],
java_resource_dirs: ["resources"],
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
index 59735f413f9d..85e8aad38808 100644
--- a/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/accessibility/AccessibilityUtils.java
@@ -183,7 +183,7 @@ public class AccessibilityUtils {
final String currentShortcutServiceId = Settings.Secure.getStringForUser(
context.getContentResolver(), Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
userId);
- if (currentShortcutServiceId != null) {
+ if (!TextUtils.isEmpty(currentShortcutServiceId)) {
return currentShortcutServiceId;
}
return context.getString(R.string.config_defaultAccessibilityService);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
index 3152e65d5a36..fa056e2b77bd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothCallback.java
@@ -16,6 +16,22 @@
package com.android.settingslib.bluetooth;
+import static android.bluetooth.BluetoothAdapter.STATE_CONNECTED;
+import static android.bluetooth.BluetoothAdapter.STATE_CONNECTING;
+import static android.bluetooth.BluetoothAdapter.STATE_DISCONNECTED;
+import static android.bluetooth.BluetoothAdapter.STATE_DISCONNECTING;
+import static android.bluetooth.BluetoothAdapter.STATE_OFF;
+import static android.bluetooth.BluetoothAdapter.STATE_ON;
+import static android.bluetooth.BluetoothAdapter.STATE_TURNING_OFF;
+import static android.bluetooth.BluetoothAdapter.STATE_TURNING_ON;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+
+import androidx.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
* BluetoothCallback provides a callback interface for the settings
@@ -33,7 +49,7 @@ public interface BluetoothCallback {
* {@link android.bluetooth.BluetoothAdapter#STATE_ON},
* {@link android.bluetooth.BluetoothAdapter#STATE_TURNING_OFF}.
*/
- default void onBluetoothStateChanged(int bluetoothState) {}
+ default void onBluetoothStateChanged(@AdapterState int bluetoothState) {}
/**
* It will be called when the local Bluetooth adapter has started
@@ -54,14 +70,14 @@ public interface BluetoothCallback {
*
* @param cachedDevice the Bluetooth device.
*/
- default void onDeviceAdded(CachedBluetoothDevice cachedDevice) {}
+ default void onDeviceAdded(@NonNull CachedBluetoothDevice cachedDevice) {}
/**
* It will be called when requiring to remove a remote device from CachedBluetoothDevice list
*
* @param cachedDevice the Bluetooth device.
*/
- default void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {}
+ default void onDeviceDeleted(@NonNull CachedBluetoothDevice cachedDevice) {}
/**
* It will be called when bond state of a remote device is changed.
@@ -73,7 +89,8 @@ public interface BluetoothCallback {
* {@link android.bluetooth.BluetoothDevice#BOND_BONDING},
* {@link android.bluetooth.BluetoothDevice#BOND_BONDED}.
*/
- default void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {}
+ default void onDeviceBondStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int bondState) {}
/**
* It will be called in following situations:
@@ -89,7 +106,9 @@ public interface BluetoothCallback {
* {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTED},
* {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTING}.
*/
- default void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {}
+ default void onConnectionStateChanged(
+ @Nullable CachedBluetoothDevice cachedDevice,
+ @ConnectionState int state) {}
/**
* It will be called when device been set as active for {@code bluetoothProfile}
@@ -101,7 +120,8 @@ public interface BluetoothCallback {
* @param activeDevice the active Bluetooth device.
* @param bluetoothProfile the profile of active Bluetooth device.
*/
- default void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
+ default void onActiveDeviceChanged(
+ @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {}
/**
* It will be called in following situations:
@@ -124,8 +144,10 @@ public interface BluetoothCallback {
* {@link android.bluetooth.BluetoothProfile#STATE_DISCONNECTING}.
* @param bluetoothProfile the BluetoothProfile id.
*/
- default void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
- int state, int bluetoothProfile) {
+ default void onProfileConnectionStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice,
+ @ConnectionState int state,
+ int bluetoothProfile) {
}
/**
@@ -138,6 +160,24 @@ public interface BluetoothCallback {
* {@link android.bluetooth.BluetoothAdapter#STATE_DISCONNECTED},
* {@link android.bluetooth.BluetoothAdapter#STATE_CONNECTED}
*/
- default void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
- }
+ default void onAclConnectionStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int state) {}
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_DISCONNECTED,
+ STATE_CONNECTING,
+ STATE_CONNECTED,
+ STATE_DISCONNECTING,
+ })
+ @interface ConnectionState {}
+
+ @IntDef(prefix = { "STATE_" }, value = {
+ STATE_OFF,
+ STATE_TURNING_ON,
+ STATE_ON,
+ STATE_TURNING_OFF,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface AdapterState {}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 51812f043908..a9f4e9c74103 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -32,6 +32,7 @@ import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
@@ -193,19 +194,19 @@ public class BluetoothEventManager {
return deviceAdded;
}
- void dispatchDeviceAdded(CachedBluetoothDevice cachedDevice) {
+ void dispatchDeviceAdded(@NonNull CachedBluetoothDevice cachedDevice) {
for (BluetoothCallback callback : mCallbacks) {
callback.onDeviceAdded(cachedDevice);
}
}
- void dispatchDeviceRemoved(CachedBluetoothDevice cachedDevice) {
+ void dispatchDeviceRemoved(@NonNull CachedBluetoothDevice cachedDevice) {
for (BluetoothCallback callback : mCallbacks) {
callback.onDeviceDeleted(cachedDevice);
}
}
- void dispatchProfileConnectionStateChanged(CachedBluetoothDevice device, int state,
+ void dispatchProfileConnectionStateChanged(@NonNull CachedBluetoothDevice device, int state,
int bluetoothProfile) {
for (BluetoothCallback callback : mCallbacks) {
callback.onProfileConnectionStateChanged(device, state, bluetoothProfile);
@@ -228,7 +229,8 @@ public class BluetoothEventManager {
}
@VisibleForTesting
- void dispatchActiveDeviceChanged(CachedBluetoothDevice activeDevice,
+ void dispatchActiveDeviceChanged(
+ @Nullable CachedBluetoothDevice activeDevice,
int bluetoothProfile) {
for (CachedBluetoothDevice cachedDevice : mDeviceManager.getCachedDevicesCopy()) {
boolean isActive = Objects.equals(cachedDevice, activeDevice);
@@ -239,7 +241,7 @@ public class BluetoothEventManager {
}
}
- private void dispatchAclStateChanged(CachedBluetoothDevice activeDevice, int state) {
+ private void dispatchAclStateChanged(@NonNull CachedBluetoothDevice activeDevice, int state) {
for (BluetoothCallback callback : mCallbacks) {
callback.onAclConnectionStateChanged(activeDevice, state);
}
@@ -456,6 +458,7 @@ public class BluetoothEventManager {
Log.w(TAG, "ActiveDeviceChangedHandler: action is null");
return;
}
+ @Nullable
CachedBluetoothDevice activeDevice = mDeviceManager.findDevice(device);
int bluetoothProfile = 0;
if (Objects.equals(action, BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED)) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index fea7475fc087..5c796af84fef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -237,14 +237,10 @@ public class BluetoothUtils {
* @return true if it supports advanced metadata, false otherwise.
*/
public static boolean isAdvancedDetailsHeader(@NonNull BluetoothDevice bluetoothDevice) {
- if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, BT_ADVANCED_HEADER_ENABLED,
- true)) {
- Log.d(TAG, "isAdvancedDetailsHeader: advancedEnabled is false");
+ if (!isAdvancedHeaderEnabled()) {
return false;
}
- // The metadata is for Android R
- if (getBooleanMetaData(bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) {
- Log.d(TAG, "isAdvancedDetailsHeader: untetheredHeadset is true");
+ if (isUntetheredHeadset(bluetoothDevice)) {
return true;
}
// The metadata is for Android S
@@ -260,6 +256,47 @@ public class BluetoothUtils {
}
/**
+ * Check if the Bluetooth device is supports advanced metadata and an untethered headset
+ *
+ * @param bluetoothDevice the BluetoothDevice to get metadata
+ * @return true if it supports advanced metadata and an untethered headset, false otherwise.
+ */
+ public static boolean isAdvancedUntetheredDevice(@NonNull BluetoothDevice bluetoothDevice) {
+ if (!isAdvancedHeaderEnabled()) {
+ return false;
+ }
+ if (isUntetheredHeadset(bluetoothDevice)) {
+ return true;
+ }
+ // The metadata is for Android S
+ String deviceType = getStringMetaData(bluetoothDevice,
+ BluetoothDevice.METADATA_DEVICE_TYPE);
+ if (TextUtils.equals(deviceType, BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET)) {
+ Log.d(TAG, "isAdvancedUntetheredDevice: is untethered device ");
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean isAdvancedHeaderEnabled() {
+ if (!DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SETTINGS_UI, BT_ADVANCED_HEADER_ENABLED,
+ true)) {
+ Log.d(TAG, "isAdvancedDetailsHeader: advancedEnabled is false");
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean isUntetheredHeadset(@NonNull BluetoothDevice bluetoothDevice) {
+ // The metadata is for Android R
+ if (getBooleanMetaData(bluetoothDevice, BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) {
+ Log.d(TAG, "isAdvancedDetailsHeader: untetheredHeadset is true");
+ return true;
+ }
+ return false;
+ }
+
+ /**
* Create an Icon pointing to a drawable.
*/
public static IconCompat createIconWithDrawable(Drawable drawable) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 7927c5d460a4..eb53ea1d44f7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -758,16 +758,23 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
public boolean isBusy() {
- synchronized (mProfileLock) {
- for (LocalBluetoothProfile profile : mProfiles) {
- int status = getProfileConnectionState(profile);
- if (status == BluetoothProfile.STATE_CONNECTING
- || status == BluetoothProfile.STATE_DISCONNECTING) {
- return true;
- }
+ for (CachedBluetoothDevice memberDevice : getMemberDevice()) {
+ if (isBusyState(memberDevice)) {
+ return true;
+ }
+ }
+ return isBusyState(this);
+ }
+
+ private boolean isBusyState(CachedBluetoothDevice device){
+ for (LocalBluetoothProfile profile : device.getProfiles()) {
+ int status = device.getProfileConnectionState(profile);
+ if (status == BluetoothProfile.STATE_CONNECTING
+ || status == BluetoothProfile.STATE_DISCONNECTING) {
+ return true;
}
- return getBondState() == BluetoothDevice.BOND_BONDING;
}
+ return device.getBondState() == BluetoothDevice.BOND_BONDING;
}
private boolean updateProfiles() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
index 123c01b6e12f..79fb56602328 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothLeBroadcast.java
@@ -210,13 +210,15 @@ public class LocalBluetoothLeBroadcast implements LocalBluetoothProfile {
LocalBluetoothLeBroadcast(Context context) {
mExecutor = Executors.newSingleThreadExecutor();
- BluetoothAdapter.getDefaultAdapter().
- getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST);
mBuilder = new BluetoothLeAudioContentMetadata.Builder();
mContentResolver = context.getContentResolver();
Handler handler = new Handler(Looper.getMainLooper());
mSettingsObserver = new BroadcastSettingsObserver(handler);
updateBroadcastInfoFromContentProvider();
+
+ // Before registering callback, the constructor should finish creating the all of variables.
+ BluetoothAdapter.getDefaultAdapter()
+ .getProfileProxy(context, mServiceListener, BluetoothProfile.LE_AUDIO_BROADCAST);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
index d84e57a38ee4..3e33da5a2ba7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/EventLogWriter.java
@@ -49,6 +49,10 @@ public class EventLogWriter implements LogWriter {
}
@Override
+ public void clicked(int sourceCategory, String key) {
+ }
+
+ @Override
public void action(Context context, int category, Pair<Integer, Object>... taggedData) {
final LogMaker logMaker = new LogMaker(category)
.setType(MetricsProto.MetricsEvent.TYPE_ACTION);
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
index d4ef3d7b24a2..cceca13ec3e5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/LogWriter.java
@@ -34,6 +34,11 @@ public interface LogWriter {
void hidden(Context context, int category, int visibleTime);
/**
+ * Logs a click event when user click item.
+ */
+ void clicked(int category, String key);
+
+ /**
* Logs an user action.
*/
void action(Context context, int category, Pair<Integer, Object>... taggedData);
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
index 7390b6aaaaed..915421a8f7a0 100644
--- a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/MetricsFeatureProvider.java
@@ -96,6 +96,18 @@ public class MetricsFeatureProvider {
}
/**
+ * Logs an event when user click item.
+ *
+ * @param category the target page id
+ * @param key the key id that user clicked
+ */
+ public void clicked(int category, String key) {
+ for (LogWriter writer : mLoggerWriters) {
+ writer.clicked(category, key);
+ }
+ }
+
+ /**
* Logs a simple action without page id or attribution
*
* @param category the target page
@@ -138,7 +150,7 @@ public class MetricsFeatureProvider {
}
public int getMetricsCategory(Object object) {
- if (object == null || !(object instanceof Instrumentable)) {
+ if (!(object instanceof Instrumentable)) {
return MetricsEvent.VIEW_UNKNOWN;
}
return ((Instrumentable) object).getMetricsCategory();
@@ -198,11 +210,7 @@ public class MetricsFeatureProvider {
// Not loggable
return false;
}
- action(sourceMetricsCategory,
- MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
- SettingsEnums.PAGE_UNKNOWN,
- logKey,
- 0);
+ clicked(sourceMetricsCategory, logKey);
return true;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index 1be9d76cf3eb..39034047d6eb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -59,14 +59,14 @@ public class BluetoothMediaDevice extends MediaDevice {
@Override
public Drawable getIcon() {
- return BluetoothUtils.isAdvancedDetailsHeader(mCachedDevice.getDevice())
+ return BluetoothUtils.isAdvancedUntetheredDevice(mCachedDevice.getDevice())
? mContext.getDrawable(R.drawable.ic_earbuds_advanced)
: BluetoothUtils.getBtClassDrawableWithDescription(mContext, mCachedDevice).first;
}
@Override
public Drawable getIconWithoutBackground() {
- return BluetoothUtils.isAdvancedDetailsHeader(mCachedDevice.getDevice())
+ return BluetoothUtils.isAdvancedUntetheredDevice(mCachedDevice.getDevice())
? mContext.getDrawable(R.drawable.ic_earbuds_advanced)
: BluetoothUtils.getBtClassDrawableWithDescription(mContext, mCachedDevice).first;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 766c036d521c..7353cc0393c5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -407,7 +407,7 @@ public class InfoMediaManager extends MediaManager {
|| sessionInfo.getVolumeHandling() != MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
}
- private void refreshDevices() {
+ private synchronized void refreshDevices() {
mMediaDevices.clear();
mCurrentConnectedDevice = null;
if (TextUtils.isEmpty(mPackageName)) {
@@ -437,7 +437,7 @@ public class InfoMediaManager extends MediaManager {
return infos;
}
- private void buildAvailableRoutes() {
+ private synchronized void buildAvailableRoutes() {
for (MediaRoute2Info route : getAvailableRoutes(mPackageName)) {
if (DEBUG) {
Log.d(TAG, "buildAvailableRoutes() route : " + route.getName() + ", volume : "
@@ -447,7 +447,7 @@ public class InfoMediaManager extends MediaManager {
}
}
- private List<MediaRoute2Info> getAvailableRoutes(String packageName) {
+ private synchronized List<MediaRoute2Info> getAvailableRoutes(String packageName) {
final List<MediaRoute2Info> infos = new ArrayList<>();
RoutingSessionInfo routingSessionInfo = getRoutingSessionInfo(packageName);
if (routingSessionInfo != null) {
@@ -571,7 +571,7 @@ public class InfoMediaManager extends MediaManager {
@Override
public void onSessionUpdated(RoutingSessionInfo sessionInfo) {
- dispatchDataChanged();
+ refreshDevices();
}
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/DataServiceUtils.java b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/DataServiceUtils.java
new file mode 100644
index 000000000000..0d6e91183a34
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/mobile/dataservice/DataServiceUtils.java
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mobile.dataservice;
+
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.UiccCardInfo;
+import android.telephony.UiccPortInfo;
+import android.telephony.UiccSlotInfo;
+import android.telephony.UiccSlotMapping;
+
+public class DataServiceUtils {
+
+ /**
+ * Represents columns of the MobileNetworkInfoData table, define these columns from
+ * {@see MobileNetworkUtils} or relevant common APIs.
+ */
+ public static final class MobileNetworkInfoData {
+
+ /** The name of the MobileNetworkInfoData table. */
+ public static final String TABLE_NAME = "MobileNetworkInfo";
+
+ /**
+ * The name of the ID column, set the {@link SubscriptionInfo#getSubscriptionId()}
+ * as the primary key.
+ */
+ public static final String COLUMN_ID = "subId";
+
+ /**
+ * The name of the WFC provision column,
+ * {@see MobileNetworkUtils#isWfcProvisionedOnDevice(int)}.
+ */
+ public static final String COLUMN_IS_WFC_PROVISIONED_ON_DEVICE = "isWfcProvisionedOnDevice";
+
+ /**
+ * The name of the contact discovery enabled state column,
+ * {@see MobileNetworkUtils#isContactDiscoveryEnabled(Context, int)}.
+ */
+ public static final String COLUMN_IS_CONTACT_DISCOVERY_ENABLED =
+ "isContactDiscoveryEnabled";
+
+ /**
+ * The name of the contact discovery visible state column,
+ * {@see MobileNetworkUtils#isContactDiscoveryEnabled(Context, int)}.
+ */
+ public static final String COLUMN_IS_CONTACT_DISCOVERY_VISIBLE =
+ "isContactDiscoveryVisible";
+
+ /**
+ * The name of the mobile network data state column,
+ * {@see MobileNetworkUtils#isMobileDataEnabled(Context)}.
+ */
+ public static final String COLUMN_IS_MOBILE_DATA_ENABLED = "isMobileDataEnabled";
+
+ /**
+ * The name of the CDMA option state column,
+ * {@see MobileNetworkUtils#isCdmaOptions(Context, int)}.
+ */
+ public static final String COLUMN_IS_CDMA_OPTIONS = "isCdmaOptions";
+
+ /**
+ * The name of the GSM option state column,
+ * {@see MobileNetworkUtils#isGsmOptions(Context, int)}.
+ */
+ public static final String COLUMN_IS_GSM_OPTIONS = "isGsmOptions";
+
+ /**
+ * The name of the world mode state column,
+ * {@see MobileNetworkUtils#isWorldMode(Context, int)}.
+ */
+ public static final String COLUMN_IS_WORLD_MODE = "isWorldMode";
+
+ /**
+ * The name of the display network select options state column,
+ * {@see MobileNetworkUtils#shouldDisplayNetworkSelectOptions(Context, int)}.
+ */
+ public static final String COLUMN_SHOULD_DISPLAY_NETWORK_SELECT_OPTIONS =
+ "shouldDisplayNetworkSelectOptions";
+
+ /**
+ * The name of the TDSCDMA supported state column,
+ * {@see MobileNetworkUtils#isTdscdmaSupported(Context, int)}.
+ */
+ public static final String COLUMN_IS_TDSCDMA_SUPPORTED = "isTdscdmaSupported";
+
+ /**
+ * The name of the active network is cellular state column,
+ * {@see MobileNetworkUtils#activeNetworkIsCellular(Context)}.
+ */
+ public static final String COLUMN_ACTIVE_NETWORK_IS_CELLULAR = "activeNetworkIsCellular";
+
+ /**
+ * The name of the show toggle for physicalSim state column,
+ * {@see SubscriptionUtil#showToggleForPhysicalSim(SubscriptionManager)}.
+ */
+ public static final String COLUMN_SHOW_TOGGLE_FOR_PHYSICAL_SIM = "showToggleForPhysicalSim";
+
+ }
+
+ /**
+ * Represents columns of the UiccInfoData table, define these columns from
+ * {@link android.telephony.UiccSlotInfo}, {@link android.telephony.UiccCardInfo},
+ * {@link UiccSlotMapping} and {@link android.telephony.UiccPortInfo}.If columns of these 4
+ * classes are changed, we should also update the table except PII data.
+ */
+ public static final class UiccInfoData {
+
+ /** The name of the UiccInfoData table. */
+ public static final String TABLE_NAME = "uiccInfo";
+
+ /**
+ * The name of the ID column, set the {@link SubscriptionInfo#getSubscriptionId()}
+ * as the primary key.
+ */
+ public static final String COLUMN_ID = "sudId";
+
+ /**
+ * The name of the physical slot index column, see
+ * {@link UiccSlotMapping#getPhysicalSlotIndex()}.
+ */
+ public static final String COLUMN_PHYSICAL_SLOT_INDEX = "physicalSlotIndex";
+
+ /**
+ * The name of the logical slot index column, see
+ * {@link UiccSlotMapping#getLogicalSlotIndex()}.
+ */
+ public static final String COLUMN_LOGICAL_SLOT_INDEX = "logicalSlotIndex";
+
+ /**
+ * The name of the card ID column, see {@link UiccCardInfo#getCardId()}.
+ */
+ public static final String COLUMN_CARD_ID = "cardId";
+
+ /**
+ * The name of the eUICC state column, see {@link UiccCardInfo#isEuicc()}.
+ */
+ public static final String COLUMN_IS_EUICC = "isEuicc";
+
+ /**
+ * The name of the multiple enabled profiles supported state column, see
+ * {@link UiccCardInfo#isMultipleEnabledProfilesSupported()}.
+ */
+ public static final String COLUMN_IS_MULTIPLE_ENABLED_PROFILES_SUPPORTED =
+ "isMultipleEnabledProfilesSupported";
+
+ /**
+ * The name of the card state column, see {@link UiccSlotInfo#getCardStateInfo()}.
+ */
+ public static final String COLUMN_CARD_STATE = "cardState";
+
+ /**
+ * The name of the extended APDU supported state column, see
+ * {@link UiccSlotInfo#getIsExtendedApduSupported()}.
+ */
+ public static final String COLUMN_IS_EXTENDED_APDU_SUPPORTED = "isExtendedApduSupported";
+
+ /**
+ * The name of the removable state column, see {@link UiccSlotInfo#isRemovable()}.
+ */
+ public static final String COLUMN_IS_REMOVABLE = "isRemovable";
+
+ /**
+ * The name of the active state column, see {@link UiccPortInfo#isActive()}.
+ */
+ public static final String COLUMN_IS_ACTIVE = "isActive";
+
+ /**
+ * The name of the port index column, see {@link UiccPortInfo#getPortIndex()}.
+ */
+ public static final String COLUMN_PORT_INDEX = "portIndex";
+ }
+
+ /**
+ * Represents columns of the SubscriptionInfoData table, define these columns from
+ * {@link SubscriptionInfo}, {@see SubscriptionUtil} and
+ * {@link SubscriptionManager} or relevant common APIs. If columns of the
+ * {@link SubscriptionInfo} are changed, we should also update the table except PII data.
+ */
+ public static final class SubscriptionInfoData {
+
+ /** The name of the SubscriptionInfoData table. */
+ public static final String TABLE_NAME = "subscriptionInfo";
+
+ /**
+ * The name of the ID column, set the {@link SubscriptionInfo#getSubscriptionId()}
+ * as the primary key.
+ */
+ public static final String COLUMN_ID = "sudId";
+
+ /**
+ * The name of the sim slot index column, see
+ * {@link SubscriptionInfo#getSimSlotIndex()}.
+ */
+ public static final String COLUMN_SIM_SLOT_INDEX = "simSlotIndex";
+
+ /**
+ * The name of the carrier ID column, see {@link SubscriptionInfo#getCarrierId()}.
+ */
+ public static final String COLUMN_CARRIER_ID = "carrierId";
+
+ /**
+ * The name of the display name column, see {@link SubscriptionInfo#getDisplayName()}.
+ */
+ public static final String COLUMN_DISPLAY_NAME = "displayName";
+
+ /**
+ * The name of the carrier name column, see {@link SubscriptionInfo#getCarrierName()}.
+ */
+ public static final String COLUMN_CARRIER_NAME = "carrierName";
+
+ /**
+ * The name of the data roaming state column, see
+ * {@link SubscriptionInfo#getDataRoaming()}.
+ */
+ public static final String COLUMN_DATA_ROAMING = "dataRoaming";
+
+ /**
+ * The name of the mcc column, see {@link SubscriptionInfo#getMccString()}.
+ */
+ public static final String COLUMN_MCC = "mcc";
+
+ /**
+ * The name of the mnc column, see {@link SubscriptionInfo#getMncString()}.
+ */
+ public static final String COLUMN_MNC = "mnc";
+
+ /**
+ * The name of the country ISO column, see {@link SubscriptionInfo#getCountryIso()}.
+ */
+ public static final String COLUMN_COUNTRY_ISO = "countryIso";
+
+ /**
+ * The name of the embedded state column, see {@link SubscriptionInfo#isEmbedded()}.
+ */
+ public static final String COLUMN_IS_EMBEDDED = "isEmbedded";
+
+ /**
+ * The name of the card ID column, see {@link SubscriptionInfo#getCardId()}.
+ */
+ public static final String COLUMN_CARD_ID = "cardId";
+
+ /**
+ * The name of the port index column, see {@link SubscriptionInfo#getPortIndex()}.
+ */
+ public static final String COLUMN_PORT_INDEX = "portIndex";
+
+ /**
+ * The name of the opportunistic state column, see
+ * {@link SubscriptionInfo#isOpportunistic()}.
+ */
+ public static final String COLUMN_IS_OPPORTUNISTIC = "isOpportunistic";
+
+ /**
+ * The name of the groupUUID column, see {@link SubscriptionInfo#getGroupUuid()}.
+ */
+ public static final String COLUMN_GROUP_UUID = "groupUUID";
+
+ /**
+ * The name of the subscription type column, see
+ * {@link SubscriptionInfo#getSubscriptionType()}}.
+ */
+ public static final String COLUMN_SUBSCRIPTION_TYPE = "subscriptionType";
+
+ /**
+ * The name of the uniqueName column,
+ * {@see SubscriptionUtil#getUniqueSubscriptionDisplayName(SubscriptionInfo, Context)}.
+ */
+ public static final String COLUMN_UNIQUE_NAME = "uniqueName";
+
+ /**
+ * The name of the subscription visible state column,
+ * {@see SubscriptionUtil#isSubscriptionVisible(SubscriptionManager, Context,
+ * SubscriptionInfo)}.
+ */
+ public static final String COLUMN_IS_SUBSCRIPTION_VISIBLE = "isSubscriptionVisible";
+
+ /**
+ * The name of the formatted phone number column,
+ * {@see SubscriptionUtil#getFormattedPhoneNumber(Context, SubscriptionInfo)}.
+ */
+ public static final String COLUMN_FORMATTED_PHONE_NUMBER = "getFormattedPhoneNumber";
+
+ /**
+ * The name of the first removable subscription state column,
+ * {@see SubscriptionUtil#getFirstRemovableSubscription(Context)}.
+ */
+ public static final String COLUMN_IS_FIRST_REMOVABLE_SUBSCRIPTION =
+ "isFirstRemovableSubscription";
+
+ /**
+ * The name of the default SIM config column,
+ * {@see SubscriptionUtil#getDefaultSimConfig(Context, int)}.
+ */
+ public static final String COLUMN_DEFAULT_SIM_CONFIG = "defaultSimConfig";
+
+ /**
+ * The name of the default subscription selection column,
+ * {@see SubscriptionUtil#getSubscriptionOrDefault(Context, int)}.
+ */
+ public static final String COLUMN_IS_DEFAULT_SUBSCRIPTION_SELECTION =
+ "isDefaultSubscriptionSelection";
+
+ /**
+ * The name of the valid subscription column,
+ * {@link SubscriptionManager#isValidSubscriptionId(int)}.
+ */
+ public static final String COLUMN_IS_VALID_SUBSCRIPTION = "isValidSubscription";
+
+ /**
+ * The name of the usable subscription column,
+ * {@link SubscriptionManager#isUsableSubscriptionId(int)}.
+ */
+ public static final String COLUMN_IS_USABLE_SUBSCRIPTION = "isUsableSubscription";
+
+ /**
+ * The name of the active subscription column,
+ * {@link SubscriptionManager#isActiveSubscriptionId(int)}.
+ */
+ public static final String COLUMN_IS_ACTIVE_SUBSCRIPTION_ID = "isActiveSubscription";
+
+ /**
+ * The name of the available subscription column,
+ * {@see SubscriptionUtil#getAvailableSubscription(Context, ProxySubscriptionManager, int)}.
+ */
+ public static final String COLUMN_IS_AVAILABLE_SUBSCRIPTION = "isAvailableSubscription";
+
+ /**
+ * The name of the default voice subscription state column, see
+ * {@link SubscriptionManager#getDefaultVoiceSubscriptionId()}.
+ */
+ public static final String COLUMN_IS_DEFAULT_VOICE_SUBSCRIPTION =
+ "isDefaultVoiceSubscription";
+
+ /**
+ * The name of the default sms subscription state column, see
+ * {@link SubscriptionManager#getDefaultSmsSubscriptionId()}.
+ */
+ public static final String COLUMN_IS_DEFAULT_SMS_SUBSCRIPTION = "isDefaultSmsSubscription";
+
+ /**
+ * The name of the default data subscription state column, see
+ * {@link SubscriptionManager#getDefaultDataSubscriptionId()}.
+ */
+ public static final String COLUMN_IS_DEFAULT_DATA_SUBSCRIPTION =
+ "isDefaultDataSubscription";
+
+ /**
+ * The name of the default subscription state column, see
+ * {@link SubscriptionManager#getDefaultSubscriptionId()}.
+ */
+ public static final String COLUMN_IS_DEFAULT_SUBSCRIPTION = "isDefaultSubscription";
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
index 1c0ea1a1f4b0..ca14573c95e1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java
@@ -205,4 +205,45 @@ public class BluetoothUtilsTest {
public void isAdvancedDetailsHeader_noMetadata_returnFalse() {
assertThat(BluetoothUtils.isAdvancedDetailsHeader(mBluetoothDevice)).isEqualTo(false);
}
+
+ @Test
+ public void isAdvancedUntetheredDevice_untetheredHeadset_returnTrue() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn(
+ BOOL_METADATA.getBytes());
+
+ assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(true);
+ }
+
+ @Test
+ public void isAdvancedUntetheredDevice_deviceTypeUntetheredHeadset_returnTrue() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
+
+ assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(true);
+ }
+
+ @Test
+ public void isAdvancedUntetheredDevice_deviceTypeWatch_returnFalse() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_WATCH.getBytes());
+
+ assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false);
+ }
+
+ @Test
+ public void isAdvancedUntetheredDevice_deviceTypeDefault_returnFalse() {
+ when(mBluetoothDevice.getMetadata(
+ BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn(
+ BluetoothDevice.DEVICE_TYPE_DEFAULT.getBytes());
+
+ assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false);
+ }
+
+ @Test
+ public void isAdvancedUntetheredDevice_noMetadata_returnFalse() {
+ assertThat(BluetoothUtils.isAdvancedUntetheredDevice(mBluetoothDevice)).isEqualTo(false);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 79e99387b2fa..315ab0aac878 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1069,4 +1069,80 @@ public class CachedBluetoothDeviceTest {
assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
}
+
+ @Test
+ public void isBusy_mainDeviceIsConnecting_returnsBusy() {
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTING);
+
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mCachedDevice.isBusy()).isTrue();
+ }
+
+ @Test
+ public void isBusy_mainDeviceIsBonding_returnsBusy() {
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mCachedDevice.isBusy()).isTrue();
+ }
+
+ @Test
+ public void isBusy_memberDeviceIsConnecting_returnsBusy() {
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTING);
+
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mCachedDevice.isBusy()).isTrue();
+ }
+
+ @Test
+ public void isBusy_memberDeviceIsBonding_returnsBusy() {
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDING);
+
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mCachedDevice.isBusy()).isTrue();
+ }
+
+ @Test
+ public void isBusy_allDevicesAreNotBusy_returnsNotBusy() {
+ mCachedDevice.addMemberDevice(mSubCachedDevice);
+ updateProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ updateSubDeviceProfileStatus(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+ when(mSubDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+
+ assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
+ assertThat(mCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mSubCachedDevice.getProfiles().contains(mA2dpProfile)).isTrue();
+ assertThat(mCachedDevice.isBusy()).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
index 0e2a3cbbbd75..3352d86b2dcc 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/MetricsFeatureProviderTest.java
@@ -80,12 +80,7 @@ public class MetricsFeatureProviderTest {
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
- verify(mLogWriter).action(
- MetricsEvent.SETTINGS_GESTURES,
- MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
- SettingsEnums.PAGE_UNKNOWN,
- key,
- 0);
+ verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, key);
}
@Test
@@ -98,12 +93,7 @@ public class MetricsFeatureProviderTest {
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
- verify(mLogWriter).action(
- MetricsEvent.SETTINGS_GESTURES,
- MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
- SettingsEnums.PAGE_UNKNOWN,
- Intent.ACTION_ASSIST,
- 0);
+ verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, Intent.ACTION_ASSIST);
}
@Test
@@ -116,12 +106,7 @@ public class MetricsFeatureProviderTest {
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
- verify(mLogWriter).action(
- MetricsEvent.SETTINGS_GESTURES,
- MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
- SettingsEnums.PAGE_UNKNOWN,
- fragment,
- 0);
+ verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, fragment);
}
@Test
@@ -140,12 +125,7 @@ public class MetricsFeatureProviderTest {
final boolean loggable = mProvider.logStartedIntent(intent, MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
- verify(mLogWriter).action(
- MetricsEvent.SETTINGS_GESTURES,
- MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
- SettingsEnums.PAGE_UNKNOWN,
- Intent.ACTION_ASSIST,
- 0);
+ verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, Intent.ACTION_ASSIST);
}
@Test
@@ -155,12 +135,7 @@ public class MetricsFeatureProviderTest {
final boolean loggable = mProvider.logStartedIntent(intent, MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
- verify(mLogWriter).action(
- MetricsEvent.SETTINGS_GESTURES,
- MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
- SettingsEnums.PAGE_UNKNOWN,
- "pkg/cls",
- 0);
+ verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, "pkg/cls");
}
@Test
@@ -171,12 +146,7 @@ public class MetricsFeatureProviderTest {
MetricsEvent.SETTINGS_GESTURES, false);
assertThat(loggable).isTrue();
- verify(mLogWriter).action(
- MetricsEvent.SETTINGS_GESTURES,
- MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
- SettingsEnums.PAGE_UNKNOWN,
- "pkg/cls/personal",
- 0);
+ verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, "pkg/cls/personal");
}
@Test
@@ -187,12 +157,7 @@ public class MetricsFeatureProviderTest {
MetricsEvent.SETTINGS_GESTURES, true);
assertThat(loggable).isTrue();
- verify(mLogWriter).action(
- MetricsEvent.SETTINGS_GESTURES,
- MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
- SettingsEnums.PAGE_UNKNOWN,
- "pkg/cls/work",
- 0);
+ verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, "pkg/cls/work");
}
@Test
@@ -226,12 +191,7 @@ public class MetricsFeatureProviderTest {
MetricsEvent.SETTINGS_GESTURES);
assertThat(loggable).isTrue();
- verify(mLogWriter).action(
- MetricsEvent.SETTINGS_GESTURES,
- MetricsEvent.ACTION_SETTINGS_TILE_CLICK,
- SettingsEnums.PAGE_UNKNOWN,
- key,
- 0);
+ verify(mLogWriter).clicked(MetricsEvent.SETTINGS_GESTURES, key);
}
@Test
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 f4af6e852580..33fb91d2252c 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
@@ -666,12 +666,22 @@ public class InfoMediaManagerTest {
}
@Test
- public void onSessionUpdated_shouldDispatchDataChanged() {
+ public void onSessionUpdated_shouldDispatchDeviceListAdded() {
+ final MediaRoute2Info info = mock(MediaRoute2Info.class);
+ when(info.getId()).thenReturn(TEST_ID);
+ when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(info.isSystemRoute()).thenReturn(true);
+
+ final List<MediaRoute2Info> routes = new ArrayList<>();
+ routes.add(info);
+ mShadowRouter2Manager.setAllRoutes(routes);
+
+ mInfoMediaManager.mPackageName = "";
mInfoMediaManager.registerCallback(mCallback);
mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(mock(RoutingSessionInfo.class));
- verify(mCallback).onDeviceAttributesChanged();
+ verify(mCallback).onDeviceListAdded(any());
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java
index fd181ff07222..e2b242c97e0e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/AppHeaderPreferenceTest.java
@@ -65,7 +65,6 @@ public class AppHeaderPreferenceTest {
@Test
public void setIsInstantApp_shouldUpdateInstallType() {
-
mPreference.onBindViewHolder(mHolder);
mPreference.setIsInstantApp(true);
@@ -77,8 +76,8 @@ public class AppHeaderPreferenceTest {
public void setSecondSummary_shouldUpdateSecondSummary() {
final String defaultTestText = "Test second summary";
- mPreference.onBindViewHolder(mHolder);
mPreference.setSecondSummary(defaultTestText);
+ mPreference.onBindViewHolder(mHolder);
assertThat(((TextView) mRootView.findViewById(R.id.second_summary)).getText().toString())
.isEqualTo(defaultTestText);
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 10ac5d9aed16..f50168250c96 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -72,6 +72,7 @@ public class SystemSettings {
Settings.System.SIP_CALL_OPTIONS,
Settings.System.SIP_RECEIVE_CALLS,
Settings.System.POINTER_SPEED,
+ Settings.System.VIBRATE_ON,
Settings.System.VIBRATE_WHEN_RINGING,
Settings.System.RINGTONE,
Settings.System.LOCK_TO_APP_ENABLED,
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index e7c7ac0f8d80..b1979c905510 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -83,7 +83,6 @@ public class SettingsBackupTest {
Settings.System.SYSTEM_LOCALES, // bug?
Settings.System.USER_ROTATION, // backup candidate?
Settings.System.VIBRATE_IN_SILENT, // deprecated?
- Settings.System.VIBRATE_ON, // candidate for backup?
Settings.System.VOLUME_ACCESSIBILITY, // used internally, changing value will
// not change volume
Settings.System.VOLUME_ALARM, // deprecated since API 2?
@@ -769,6 +768,7 @@ public class SettingsBackupTest {
Settings.Secure.SMS_DEFAULT_APPLICATION,
Settings.Secure.SPELL_CHECKER_ENABLED, // Intentionally removed in Q
Settings.Secure.TRUST_AGENTS_INITIALIZED,
+ Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED,
Settings.Secure.TV_APP_USES_NON_SYSTEM_INPUTS,
Settings.Secure.TV_INPUT_CUSTOM_LABELS,
Settings.Secure.TV_INPUT_HIDDEN_INPUTS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b31e36c417ab..6c17036b3f5f 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -180,6 +180,7 @@
<uses-permission android:name="android.permission.MANAGE_ROLLBACKS" />
<uses-permission android:name="android.permission.TEST_MANAGE_ROLLBACKS" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
+ <uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT" />
<uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
diff --git a/packages/Shell/res/values-ro/strings.xml b/packages/Shell/res/values-ro/strings.xml
index 91b0b1e4cd59..56e9ee0d1350 100644
--- a/packages/Shell/res/values-ro/strings.xml
+++ b/packages/Shell/res/values-ro/strings.xml
@@ -21,14 +21,14 @@
<string name="bugreport_in_progress_title" msgid="4311705936714972757">"Raportul de eroare <xliff:g id="ID">#%d</xliff:g> se generează"</string>
<string name="bugreport_finished_title" msgid="4429132808670114081">"Raportul de eroare <xliff:g id="ID">#%d</xliff:g> a fost creat"</string>
<string name="bugreport_updating_title" msgid="4423539949559634214">"Se adaugă detaliile la raportul de eroare"</string>
- <string name="bugreport_updating_wait" msgid="3322151947853929470">"Așteptați…"</string>
+ <string name="bugreport_updating_wait" msgid="3322151947853929470">"Te rugăm să aștepți…"</string>
<string name="bugreport_finished_text" product="watch" msgid="1223616207145252689">"Raportul de eroare va apărea curând pe telefon"</string>
- <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selectați pentru a trimite raportul de eroare"</string>
- <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Atingeți pentru a trimite raportul de eroare"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selectați pentru a trimite raportul de eroare fără captură de ecran sau așteptați finalizarea acesteia"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Atingeți ca să trimiteți raportul de eroare fără captură de ecran sau așteptați finalizarea acesteia"</string>
- <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Atingeți ca să trimiteți raportul de eroare fără captură de ecran sau așteptați finalizarea acesteia"</string>
- <string name="bugreport_confirm" msgid="5917407234515812495">"Rapoartele despre erori conțin date din diferite fișiere de jurnal ale sistemului. Acestea pot include date pe care le puteți considera sensibile (cum ar fi utilizarea aplicației și date despre locație). Permiteți accesul la rapoartele despre erori numai aplicațiilor și persoanelor în care aveți încredere."</string>
+ <string name="bugreport_finished_text" product="tv" msgid="5758325479058638893">"Selectează pentru a trimite raportul de eroare"</string>
+ <string name="bugreport_finished_text" product="default" msgid="8353769438382138847">"Atinge pentru a trimite raportul de eroare"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="tv" msgid="2343263822812016950">"Selectează pentru a trimite raportul fără captură de ecran sau așteaptă finalizarea acesteia"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="watch" msgid="1474435374470177193">"Atinge ca să trimiți raportul de eroare fără captură de ecran sau așteaptă finalizarea acesteia"</string>
+ <string name="bugreport_finished_pending_screenshot_text" product="default" msgid="1474435374470177193">"Atinge ca să trimiți raportul de eroare fără captură de ecran sau așteaptă finalizarea acesteia"</string>
+ <string name="bugreport_confirm" msgid="5917407234515812495">"Rapoartele despre erori conțin date din diferite fișiere de jurnal ale sistemului. Acestea pot include date pe care le poți considera sensibile (cum ar fi utilizarea aplicației și date despre locație). Permite accesul la rapoartele despre erori numai aplicațiilor și persoanelor în care ai încredere."</string>
<string name="bugreport_confirm_dont_repeat" msgid="6179945398364357318">"Nu mai afișa"</string>
<string name="bugreport_storage_title" msgid="5332488144740527109">"Rapoarte de erori"</string>
<string name="bugreport_unreadable_text" msgid="586517851044535486">"Fișierul cu raportul de eroare nu a putut fi citit"</string>
@@ -42,6 +42,6 @@
<string name="bugreport_info_name" msgid="4414036021935139527">"Numele fișierului"</string>
<string name="bugreport_info_title" msgid="2306030793918239804">"Titlul erorii"</string>
<string name="bugreport_info_description" msgid="5072835127481627722">"Rezumat privind eroarea"</string>
- <string name="save" msgid="4781509040564835759">"Salvați"</string>
- <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Trimiteți raportul de eroare"</string>
+ <string name="save" msgid="4781509040564835759">"Salvează"</string>
+ <string name="bugreport_intent_chooser_title" msgid="7605709494790894076">"Trimite raportul de eroare"</string>
</resources>
diff --git a/packages/Shell/src/com/android/shell/Screenshooter.java b/packages/Shell/src/com/android/shell/Screenshooter.java
index 85f25528f07e..d55eda0c7062 100644
--- a/packages/Shell/src/com/android/shell/Screenshooter.java
+++ b/packages/Shell/src/com/android/shell/Screenshooter.java
@@ -20,6 +20,7 @@ import android.graphics.Bitmap;
import android.os.IBinder;
import android.util.Log;
import android.view.SurfaceControl;
+import android.window.ScreenCapture;
/**
* Helper class used to take screenshots.
@@ -40,11 +41,11 @@ final class Screenshooter {
Log.d(TAG, "Taking fullscreen screenshot");
// Take the screenshot
final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
- final SurfaceControl.DisplayCaptureArgs captureArgs =
- new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+ final ScreenCapture.DisplayCaptureArgs captureArgs =
+ new ScreenCapture.DisplayCaptureArgs.Builder(displayToken)
.build();
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureDisplay(captureArgs);
+ final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+ ScreenCapture.captureDisplay(captureArgs);
final Bitmap screenShot = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
if (screenShot == null) {
Log.e(TAG, "Failed to take fullscreen screenshot");
diff --git a/packages/SimAppDialog/res/values-ro/strings.xml b/packages/SimAppDialog/res/values-ro/strings.xml
index 21663d125f7f..5d876eac82d8 100644
--- a/packages/SimAppDialog/res/values-ro/strings.xml
+++ b/packages/SimAppDialog/res/values-ro/strings.xml
@@ -18,9 +18,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="8898068901680117589">"Sim App Dialog"</string>
- <string name="install_carrier_app_title" msgid="334729104862562585">"Activați serviciul mobil"</string>
+ <string name="install_carrier_app_title" msgid="334729104862562585">"Activează serviciul mobil"</string>
<string name="install_carrier_app_description" msgid="4014303558674923797">"Pentru ca noul card SIM să funcționeze corect, va trebui să instalați aplicația <xliff:g id="ID_1">%1$s</xliff:g>"</string>
<string name="install_carrier_app_description_default" msgid="7356830245205847840">"Pentru ca noul card SIM să funcționeze corect, va trebui să instalați aplicația operatorului"</string>
<string name="install_carrier_app_defer_action" msgid="2558576736886876209">"Nu acum"</string>
- <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Descărcați aplicația"</string>
+ <string name="install_carrier_app_download_action" msgid="7859229305958538064">"Descarcă aplicația"</string>
</resources>
diff --git a/packages/SoundPicker/res/values-ro/strings.xml b/packages/SoundPicker/res/values-ro/strings.xml
index 6190f7f8eac3..58b5aeb4dca8 100644
--- a/packages/SoundPicker/res/values-ro/strings.xml
+++ b/packages/SoundPicker/res/values-ro/strings.xml
@@ -19,10 +19,10 @@
<string name="ringtone_default" msgid="798836092118824500">"Ton de apel prestabilit"</string>
<string name="notification_sound_default" msgid="8133121186242636840">"Sunet de notificare prestabilit"</string>
<string name="alarm_sound_default" msgid="4787646764557462649">"Sunet de alarmă prestabilit"</string>
- <string name="add_ringtone_text" msgid="6642389991738337529">"Adăugați un ton de sonerie"</string>
- <string name="add_alarm_text" msgid="3545497316166999225">"Adăugați o alarmă"</string>
- <string name="add_notification_text" msgid="4431129543300614788">"Adăugați o notificare"</string>
- <string name="delete_ringtone_text" msgid="201443984070732499">"Ștergeți"</string>
+ <string name="add_ringtone_text" msgid="6642389991738337529">"Adaugă un ton de sonerie"</string>
+ <string name="add_alarm_text" msgid="3545497316166999225">"Adaugă o alarmă"</string>
+ <string name="add_notification_text" msgid="4431129543300614788">"Adaugă o notificare"</string>
+ <string name="delete_ringtone_text" msgid="201443984070732499">"Șterge"</string>
<string name="unable_to_add_ringtone" msgid="4583511263449467326">"Nu se poate adăuga tonul de sonerie personalizat"</string>
<string name="unable_to_delete_ringtone" msgid="6792301380142859496">"Nu se poate șterge tonul de sonerie personalizat"</string>
<string name="app_label" msgid="3091611356093417332">"Sunete"</string>
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b7fb4727e981..f3614d327561 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -124,6 +124,7 @@ android_library {
"dagger2",
"jsr330",
"lottie",
+ "LowLightDreamLib",
],
manifest: "AndroidManifest.xml",
@@ -228,6 +229,7 @@ android_library {
"dagger2",
"jsr330",
"WindowManager-Shell",
+ "LowLightDreamLib",
],
libs: [
"android.test.runner",
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index 234ef241f538..aaee42fe05f0 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -1,25 +1,6 @@
{
// Looking for unit test presubmit configuration?
// This currently lives in ATP config apct/system_ui/unit_test
- "presubmit-large": [
- {
- "name": "PlatformScenarioTests",
- "options": [
- {
- "include-filter": "android.platform.test.scenario.sysui"
- },
- {
- "include-annotation": "android.platform.test.scenario.annotation.Scenario"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.Postsubmit"
- }
- ]
- }
- ],
"presubmit-sysui": [
{
"name": "PlatformScenarioTests",
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 7d4dcf88542b..ebabdf571dfd 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -352,8 +352,11 @@ class ActivityLaunchAnimator(
* The animation was cancelled. Note that [onLaunchAnimationEnd] will still be called after
* this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
* before the cancellation.
+ *
+ * If this launch animation affected the occlusion state of the keyguard, WM will provide
+ * us with [newKeyguardOccludedState] so that we can set the occluded state appropriately.
*/
- fun onLaunchAnimationCancelled() {}
+ fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean? = null) {}
}
@VisibleForTesting
@@ -667,7 +670,7 @@ class ActivityLaunchAnimator(
removeTimeout()
context.mainExecutor.execute {
animation?.cancel()
- controller.onLaunchAnimationCancelled()
+ controller.onLaunchAnimationCancelled(newKeyguardOccludedState = isKeyguardOccluded)
}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index eac5d275092a..9656b8a99d41 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -238,7 +238,7 @@ constructor(
}
}
- override fun onLaunchAnimationCancelled() {
+ override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
controller.onLaunchAnimationCancelled()
enableDialogDismiss()
dialog.dismiss()
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt
new file mode 100644
index 000000000000..19624e605be8
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.compose.layout.pager
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.rememberSplineBasedDecay
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.Velocity
+import androidx.compose.ui.unit.dp
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.filter
+
+/** Library-wide switch to turn on debug logging. */
+internal const val DebugLog = false
+
+@RequiresOptIn(message = "Accompanist Pager is experimental. The API may be changed in the future.")
+@Retention(AnnotationRetention.BINARY)
+annotation class ExperimentalPagerApi
+
+/** Contains the default values used by [HorizontalPager] and [VerticalPager]. */
+@ExperimentalPagerApi
+object PagerDefaults {
+ /**
+ * Remember the default [FlingBehavior] that represents the scroll curve.
+ *
+ * @param state The [PagerState] to update.
+ * @param decayAnimationSpec The decay animation spec to use for decayed flings.
+ * @param snapAnimationSpec The animation spec to use when snapping.
+ */
+ @Composable
+ fun flingBehavior(
+ state: PagerState,
+ decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(),
+ snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec,
+ ): FlingBehavior =
+ rememberSnappingFlingBehavior(
+ lazyListState = state.lazyListState,
+ decayAnimationSpec = decayAnimationSpec,
+ snapAnimationSpec = snapAnimationSpec,
+ )
+
+ @Deprecated(
+ "Replaced with PagerDefaults.flingBehavior()",
+ ReplaceWith("PagerDefaults.flingBehavior(state, decayAnimationSpec, snapAnimationSpec)")
+ )
+ @Composable
+ fun rememberPagerFlingConfig(
+ state: PagerState,
+ decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(),
+ snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec,
+ ): FlingBehavior = flingBehavior(state, decayAnimationSpec, snapAnimationSpec)
+}
+
+/**
+ * A horizontally scrolling layout that allows users to flip between items to the left and right.
+ *
+ * @sample com.google.accompanist.sample.pager.HorizontalPagerSample
+ *
+ * @param count the number of pages.
+ * @param modifier the modifier to apply to this layout.
+ * @param state the state object to be used to control or observe the pager's state.
+ * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be
+ * composed from the end to the start and [PagerState.currentPage] == 0 will mean the first item is
+ * located at the end.
+ * @param itemSpacing horizontal spacing to add between items.
+ * @param flingBehavior logic describing fling behavior.
+ * @param key the scroll position will be maintained based on the key, which means if you add/remove
+ * items before the current visible item the item with the given key will be kept as the first
+ * visible one.
+ * @param content a block which describes the content. Inside this block you can reference
+ * [PagerScope.currentPage] and other properties in [PagerScope].
+ */
+@ExperimentalPagerApi
+@Composable
+fun HorizontalPager(
+ count: Int,
+ modifier: Modifier = Modifier,
+ state: PagerState = rememberPagerState(),
+ reverseLayout: Boolean = false,
+ itemSpacing: Dp = 0.dp,
+ flingBehavior: FlingBehavior = PagerDefaults.flingBehavior(state),
+ verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
+ key: ((page: Int) -> Any)? = null,
+ contentPadding: PaddingValues = PaddingValues(0.dp),
+ content: @Composable PagerScope.(page: Int) -> Unit,
+) {
+ Pager(
+ count = count,
+ state = state,
+ modifier = modifier,
+ isVertical = false,
+ reverseLayout = reverseLayout,
+ itemSpacing = itemSpacing,
+ verticalAlignment = verticalAlignment,
+ flingBehavior = flingBehavior,
+ key = key,
+ contentPadding = contentPadding,
+ content = content
+ )
+}
+
+/**
+ * A vertically scrolling layout that allows users to flip between items to the top and bottom.
+ *
+ * @sample com.google.accompanist.sample.pager.VerticalPagerSample
+ *
+ * @param count the number of pages.
+ * @param modifier the modifier to apply to this layout.
+ * @param state the state object to be used to control or observe the pager's state.
+ * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be
+ * composed from the bottom to the top and [PagerState.currentPage] == 0 will mean the first item is
+ * located at the bottom.
+ * @param itemSpacing vertical spacing to add between items.
+ * @param flingBehavior logic describing fling behavior.
+ * @param key the scroll position will be maintained based on the key, which means if you add/remove
+ * items before the current visible item the item with the given key will be kept as the first
+ * visible one.
+ * @param content a block which describes the content. Inside this block you can reference
+ * [PagerScope.currentPage] and other properties in [PagerScope].
+ */
+@ExperimentalPagerApi
+@Composable
+fun VerticalPager(
+ count: Int,
+ modifier: Modifier = Modifier,
+ state: PagerState = rememberPagerState(),
+ reverseLayout: Boolean = false,
+ itemSpacing: Dp = 0.dp,
+ flingBehavior: FlingBehavior = PagerDefaults.flingBehavior(state),
+ horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ key: ((page: Int) -> Any)? = null,
+ contentPadding: PaddingValues = PaddingValues(0.dp),
+ content: @Composable PagerScope.(page: Int) -> Unit,
+) {
+ Pager(
+ count = count,
+ state = state,
+ modifier = modifier,
+ isVertical = true,
+ reverseLayout = reverseLayout,
+ itemSpacing = itemSpacing,
+ horizontalAlignment = horizontalAlignment,
+ flingBehavior = flingBehavior,
+ key = key,
+ contentPadding = contentPadding,
+ content = content
+ )
+}
+
+@ExperimentalPagerApi
+@Composable
+internal fun Pager(
+ count: Int,
+ modifier: Modifier,
+ state: PagerState,
+ reverseLayout: Boolean,
+ itemSpacing: Dp,
+ isVertical: Boolean,
+ flingBehavior: FlingBehavior,
+ key: ((page: Int) -> Any)?,
+ contentPadding: PaddingValues,
+ verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
+ horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally,
+ content: @Composable PagerScope.(page: Int) -> Unit,
+) {
+ require(count >= 0) { "pageCount must be >= 0" }
+
+ // Provide our PagerState with access to the SnappingFlingBehavior animation target
+ // TODO: can this be done in a better way?
+ state.flingAnimationTarget = { (flingBehavior as? SnappingFlingBehavior)?.animationTarget }
+
+ LaunchedEffect(count) {
+ state.currentPage = minOf(count - 1, state.currentPage).coerceAtLeast(0)
+ }
+
+ // Once a fling (scroll) has finished, notify the state
+ LaunchedEffect(state) {
+ // When a 'scroll' has finished, notify the state
+ snapshotFlow { state.isScrollInProgress }
+ .filter { !it }
+ .collect { state.onScrollFinished() }
+ }
+
+ val pagerScope = remember(state) { PagerScopeImpl(state) }
+
+ // We only consume nested flings in the main-axis, allowing cross-axis flings to propagate
+ // as normal
+ val consumeFlingNestedScrollConnection =
+ ConsumeFlingNestedScrollConnection(
+ consumeHorizontal = !isVertical,
+ consumeVertical = isVertical,
+ )
+
+ if (isVertical) {
+ LazyColumn(
+ state = state.lazyListState,
+ verticalArrangement = Arrangement.spacedBy(itemSpacing, verticalAlignment),
+ horizontalAlignment = horizontalAlignment,
+ flingBehavior = flingBehavior,
+ reverseLayout = reverseLayout,
+ contentPadding = contentPadding,
+ modifier = modifier,
+ ) {
+ items(
+ count = count,
+ key = key,
+ ) { page ->
+ Box(
+ Modifier
+ // We don't any nested flings to continue in the pager, so we add a
+ // connection which consumes them.
+ // See: https://github.com/google/accompanist/issues/347
+ .nestedScroll(connection = consumeFlingNestedScrollConnection)
+ // Constraint the content to be <= than the size of the pager.
+ .fillParentMaxHeight()
+ .wrapContentSize()
+ ) { pagerScope.content(page) }
+ }
+ }
+ } else {
+ LazyRow(
+ state = state.lazyListState,
+ verticalAlignment = verticalAlignment,
+ horizontalArrangement = Arrangement.spacedBy(itemSpacing, horizontalAlignment),
+ flingBehavior = flingBehavior,
+ reverseLayout = reverseLayout,
+ contentPadding = contentPadding,
+ modifier = modifier,
+ ) {
+ items(
+ count = count,
+ key = key,
+ ) { page ->
+ Box(
+ Modifier
+ // We don't any nested flings to continue in the pager, so we add a
+ // connection which consumes them.
+ // See: https://github.com/google/accompanist/issues/347
+ .nestedScroll(connection = consumeFlingNestedScrollConnection)
+ // Constraint the content to be <= than the size of the pager.
+ .fillParentMaxWidth()
+ .wrapContentSize()
+ ) { pagerScope.content(page) }
+ }
+ }
+ }
+}
+
+private class ConsumeFlingNestedScrollConnection(
+ private val consumeHorizontal: Boolean,
+ private val consumeVertical: Boolean,
+) : NestedScrollConnection {
+ override fun onPostScroll(
+ consumed: Offset,
+ available: Offset,
+ source: NestedScrollSource
+ ): Offset =
+ when (source) {
+ // We can consume all resting fling scrolls so that they don't propagate up to the
+ // Pager
+ NestedScrollSource.Fling -> available.consume(consumeHorizontal, consumeVertical)
+ else -> Offset.Zero
+ }
+
+ override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+ // We can consume all post fling velocity on the main-axis
+ // so that it doesn't propagate up to the Pager
+ return available.consume(consumeHorizontal, consumeVertical)
+ }
+}
+
+private fun Offset.consume(
+ consumeHorizontal: Boolean,
+ consumeVertical: Boolean,
+): Offset =
+ Offset(
+ x = if (consumeHorizontal) this.x else 0f,
+ y = if (consumeVertical) this.y else 0f,
+ )
+
+private fun Velocity.consume(
+ consumeHorizontal: Boolean,
+ consumeVertical: Boolean,
+): Velocity =
+ Velocity(
+ x = if (consumeHorizontal) this.x else 0f,
+ y = if (consumeVertical) this.y else 0f,
+ )
+
+/** Scope for [HorizontalPager] content. */
+@ExperimentalPagerApi
+@Stable
+interface PagerScope {
+ /** Returns the current selected page */
+ val currentPage: Int
+
+ /** The current offset from the start of [currentPage], as a ratio of the page width. */
+ val currentPageOffset: Float
+}
+
+@ExperimentalPagerApi
+private class PagerScopeImpl(
+ private val state: PagerState,
+) : PagerScope {
+ override val currentPage: Int
+ get() = state.currentPage
+ override val currentPageOffset: Float
+ get() = state.currentPageOffset
+}
+
+/**
+ * Calculate the offset for the given [page] from the current scroll position. This is useful when
+ * using the scroll position to apply effects or animations to items.
+ *
+ * The returned offset can positive or negative, depending on whether which direction the [page] is
+ * compared to the current scroll position.
+ *
+ * @sample com.google.accompanist.sample.pager.HorizontalPagerWithOffsetTransition
+ */
+@ExperimentalPagerApi
+fun PagerScope.calculateCurrentOffsetForPage(page: Int): Float {
+ return (currentPage + currentPageOffset) - page
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt
new file mode 100644
index 000000000000..288c26eb1199
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.compose.layout.pager
+
+import androidx.annotation.FloatRange
+import androidx.annotation.IntRange
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.spring
+import androidx.compose.foundation.MutatePriority
+import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.foundation.gestures.ScrollableState
+import androidx.compose.foundation.interaction.InteractionSource
+import androidx.compose.foundation.lazy.LazyListItemInfo
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.Saver
+import androidx.compose.runtime.saveable.listSaver
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import kotlin.math.absoluteValue
+import kotlin.math.roundToInt
+
+@Deprecated(
+ "Replaced with rememberPagerState(initialPage) and count parameter on Pager composables",
+ ReplaceWith("rememberPagerState(initialPage)"),
+ level = DeprecationLevel.ERROR,
+)
+@Suppress("UNUSED_PARAMETER", "NOTHING_TO_INLINE")
+@ExperimentalPagerApi
+@Composable
+inline fun rememberPagerState(
+ @IntRange(from = 0) pageCount: Int,
+ @IntRange(from = 0) initialPage: Int = 0,
+ @FloatRange(from = 0.0, to = 1.0) initialPageOffset: Float = 0f,
+ @IntRange(from = 1) initialOffscreenLimit: Int = 1,
+ infiniteLoop: Boolean = false
+): PagerState {
+ return rememberPagerState(initialPage = initialPage)
+}
+
+/**
+ * Creates a [PagerState] that is remembered across compositions.
+ *
+ * Changes to the provided values for [initialPage] will **not** result in the state being recreated
+ * or changed in any way if it has already been created.
+ *
+ * @param initialPage the initial value for [PagerState.currentPage]
+ */
+@ExperimentalPagerApi
+@Composable
+fun rememberPagerState(
+ @IntRange(from = 0) initialPage: Int = 0,
+): PagerState =
+ rememberSaveable(saver = PagerState.Saver) {
+ PagerState(
+ currentPage = initialPage,
+ )
+ }
+
+/**
+ * A state object that can be hoisted to control and observe scrolling for [HorizontalPager].
+ *
+ * In most cases, this will be created via [rememberPagerState].
+ *
+ * @param currentPage the initial value for [PagerState.currentPage]
+ */
+@ExperimentalPagerApi
+@Stable
+class PagerState(
+ @IntRange(from = 0) currentPage: Int = 0,
+) : ScrollableState {
+ // Should this be public?
+ internal val lazyListState = LazyListState(firstVisibleItemIndex = currentPage)
+
+ private var _currentPage by mutableStateOf(currentPage)
+
+ private val currentLayoutPageInfo: LazyListItemInfo?
+ get() =
+ lazyListState.layoutInfo.visibleItemsInfo
+ .asSequence()
+ .filter { it.offset <= 0 && it.offset + it.size > 0 }
+ .lastOrNull()
+
+ private val currentLayoutPageOffset: Float
+ get() =
+ currentLayoutPageInfo?.let { current ->
+ // We coerce since itemSpacing can make the offset > 1f.
+ // We don't want to count spacing in the offset so cap it to 1f
+ (-current.offset / current.size.toFloat()).coerceIn(0f, 1f)
+ }
+ ?: 0f
+
+ /**
+ * [InteractionSource] that will be used to dispatch drag events when this list is being
+ * dragged. If you want to know whether the fling (or animated scroll) is in progress, use
+ * [isScrollInProgress].
+ */
+ val interactionSource: InteractionSource
+ get() = lazyListState.interactionSource
+
+ /** The number of pages to display. */
+ @get:IntRange(from = 0)
+ val pageCount: Int by derivedStateOf { lazyListState.layoutInfo.totalItemsCount }
+
+ /**
+ * The index of the currently selected page. This may not be the page which is currently
+ * displayed on screen.
+ *
+ * To update the scroll position, use [scrollToPage] or [animateScrollToPage].
+ */
+ @get:IntRange(from = 0)
+ var currentPage: Int
+ get() = _currentPage
+ internal set(value) {
+ if (value != _currentPage) {
+ _currentPage = value
+ }
+ }
+
+ /**
+ * The current offset from the start of [currentPage], as a ratio of the page width.
+ *
+ * To update the scroll position, use [scrollToPage] or [animateScrollToPage].
+ */
+ val currentPageOffset: Float by derivedStateOf {
+ currentLayoutPageInfo?.let {
+ // The current page offset is the current layout page delta from `currentPage`
+ // (which is only updated after a scroll/animation).
+ // We calculate this by looking at the current layout page + it's offset,
+ // then subtracting the 'current page'.
+ it.index + currentLayoutPageOffset - _currentPage
+ }
+ ?: 0f
+ }
+
+ /** The target page for any on-going animations. */
+ private var animationTargetPage: Int? by mutableStateOf(null)
+
+ internal var flingAnimationTarget: (() -> Int?)? by mutableStateOf(null)
+
+ /**
+ * The target page for any on-going animations or scrolls by the user. Returns the current page
+ * if a scroll or animation is not currently in progress.
+ */
+ val targetPage: Int
+ get() =
+ animationTargetPage
+ ?: flingAnimationTarget?.invoke()
+ ?: when {
+ // If a scroll isn't in progress, return the current page
+ !isScrollInProgress -> currentPage
+ // If the offset is 0f (or very close), return the current page
+ currentPageOffset.absoluteValue < 0.001f -> currentPage
+ // If we're offset towards the start, guess the previous page
+ currentPageOffset < -0.5f -> (currentPage - 1).coerceAtLeast(0)
+ // If we're offset towards the end, guess the next page
+ else -> (currentPage + 1).coerceAtMost(pageCount - 1)
+ }
+
+ @Deprecated(
+ "Replaced with animateScrollToPage(page, pageOffset)",
+ ReplaceWith("animateScrollToPage(page = page, pageOffset = pageOffset)")
+ )
+ @Suppress("UNUSED_PARAMETER")
+ suspend fun animateScrollToPage(
+ @IntRange(from = 0) page: Int,
+ @FloatRange(from = 0.0, to = 1.0) pageOffset: Float = 0f,
+ animationSpec: AnimationSpec<Float> = spring(),
+ initialVelocity: Float = 0f,
+ skipPages: Boolean = true,
+ ) {
+ animateScrollToPage(page = page, pageOffset = pageOffset)
+ }
+
+ /**
+ * Animate (smooth scroll) to the given page to the middle of the viewport.
+ *
+ * Cancels the currently running scroll, if any, and suspends until the cancellation is
+ * complete.
+ *
+ * @param page the page to animate to. Must be between 0 and [pageCount] (inclusive).
+ * @param pageOffset the percentage of the page width to offset, from the start of [page]. Must
+ * be in the range 0f..1f.
+ */
+ suspend fun animateScrollToPage(
+ @IntRange(from = 0) page: Int,
+ @FloatRange(from = 0.0, to = 1.0) pageOffset: Float = 0f,
+ ) {
+ requireCurrentPage(page, "page")
+ requireCurrentPageOffset(pageOffset, "pageOffset")
+ try {
+ animationTargetPage = page
+
+ if (pageOffset <= 0.005f) {
+ // If the offset is (close to) zero, just call animateScrollToItem and we're done
+ lazyListState.animateScrollToItem(index = page)
+ } else {
+ // Else we need to figure out what the offset is in pixels...
+
+ var target =
+ lazyListState.layoutInfo.visibleItemsInfo.firstOrNull { it.index == page }
+
+ if (target != null) {
+ // If we have access to the target page layout, we can calculate the pixel
+ // offset from the size
+ lazyListState.animateScrollToItem(
+ index = page,
+ scrollOffset = (target.size * pageOffset).roundToInt()
+ )
+ } else {
+ // If we don't, we use the current page size as a guide
+ val currentSize = currentLayoutPageInfo!!.size
+ lazyListState.animateScrollToItem(
+ index = page,
+ scrollOffset = (currentSize * pageOffset).roundToInt()
+ )
+
+ // The target should be visible now
+ target = lazyListState.layoutInfo.visibleItemsInfo.first { it.index == page }
+
+ if (target.size != currentSize) {
+ // If the size we used for calculating the offset differs from the actual
+ // target page size, we need to scroll again. This doesn't look great,
+ // but there's not much else we can do.
+ lazyListState.animateScrollToItem(
+ index = page,
+ scrollOffset = (target.size * pageOffset).roundToInt()
+ )
+ }
+ }
+ }
+ } finally {
+ // We need to manually call this, as the `animateScrollToItem` call above will happen
+ // in 1 frame, which is usually too fast for the LaunchedEffect in Pager to detect
+ // the change. This is especially true when running unit tests.
+ onScrollFinished()
+ }
+ }
+
+ /**
+ * Instantly brings the item at [page] to the middle of the viewport.
+ *
+ * Cancels the currently running scroll, if any, and suspends until the cancellation is
+ * complete.
+ *
+ * @param page the page to snap to. Must be between 0 and [pageCount] (inclusive).
+ */
+ suspend fun scrollToPage(
+ @IntRange(from = 0) page: Int,
+ @FloatRange(from = 0.0, to = 1.0) pageOffset: Float = 0f,
+ ) {
+ requireCurrentPage(page, "page")
+ requireCurrentPageOffset(pageOffset, "pageOffset")
+ try {
+ animationTargetPage = page
+
+ // First scroll to the given page. It will now be laid out at offset 0
+ lazyListState.scrollToItem(index = page)
+
+ // If we have a start spacing, we need to offset (scroll) by that too
+ if (pageOffset > 0.0001f) {
+ scroll { currentLayoutPageInfo?.let { scrollBy(it.size * pageOffset) } }
+ }
+ } finally {
+ // We need to manually call this, as the `scroll` call above will happen in 1 frame,
+ // which is usually too fast for the LaunchedEffect in Pager to detect the change.
+ // This is especially true when running unit tests.
+ onScrollFinished()
+ }
+ }
+
+ internal fun onScrollFinished() {
+ // Then update the current page to our layout page
+ currentPage = currentLayoutPageInfo?.index ?: 0
+ // Clear the animation target page
+ animationTargetPage = null
+ }
+
+ override suspend fun scroll(
+ scrollPriority: MutatePriority,
+ block: suspend ScrollScope.() -> Unit
+ ) = lazyListState.scroll(scrollPriority, block)
+
+ override fun dispatchRawDelta(delta: Float): Float {
+ return lazyListState.dispatchRawDelta(delta)
+ }
+
+ override val isScrollInProgress: Boolean
+ get() = lazyListState.isScrollInProgress
+
+ override fun toString(): String =
+ "PagerState(" +
+ "pageCount=$pageCount, " +
+ "currentPage=$currentPage, " +
+ "currentPageOffset=$currentPageOffset" +
+ ")"
+
+ private fun requireCurrentPage(value: Int, name: String) {
+ if (pageCount == 0) {
+ require(value == 0) { "$name must be 0 when pageCount is 0" }
+ } else {
+ require(value in 0 until pageCount) { "$name[$value] must be >= 0 and < pageCount" }
+ }
+ }
+
+ private fun requireCurrentPageOffset(value: Float, name: String) {
+ if (pageCount == 0) {
+ require(value == 0f) { "$name must be 0f when pageCount is 0" }
+ } else {
+ require(value in 0f..1f) { "$name must be >= 0 and <= 1" }
+ }
+ }
+
+ companion object {
+ /** The default [Saver] implementation for [PagerState]. */
+ val Saver: Saver<PagerState, *> =
+ listSaver(
+ save = {
+ listOf<Any>(
+ it.currentPage,
+ )
+ },
+ restore = {
+ PagerState(
+ currentPage = it[0] as Int,
+ )
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt
new file mode 100644
index 000000000000..0b53f5324a7d
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.compose.layout.pager
+
+import androidx.compose.animation.core.AnimationSpec
+import androidx.compose.animation.core.AnimationState
+import androidx.compose.animation.core.DecayAnimationSpec
+import androidx.compose.animation.core.animateDecay
+import androidx.compose.animation.core.animateTo
+import androidx.compose.animation.core.calculateTargetValue
+import androidx.compose.animation.core.spring
+import androidx.compose.animation.rememberSplineBasedDecay
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.ScrollScope
+import androidx.compose.foundation.lazy.LazyListItemInfo
+import androidx.compose.foundation.lazy.LazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import kotlin.math.abs
+
+/** Default values used for [SnappingFlingBehavior] & [rememberSnappingFlingBehavior]. */
+internal object SnappingFlingBehaviorDefaults {
+ /** TODO */
+ val snapAnimationSpec: AnimationSpec<Float> = spring(stiffness = 600f)
+}
+
+/**
+ * Create and remember a snapping [FlingBehavior] to be used with [LazyListState].
+ *
+ * TODO: move this to a new module and make it public
+ *
+ * @param lazyListState The [LazyListState] to update.
+ * @param decayAnimationSpec The decay animation spec to use for decayed flings.
+ * @param snapAnimationSpec The animation spec to use when snapping.
+ */
+@Composable
+internal fun rememberSnappingFlingBehavior(
+ lazyListState: LazyListState,
+ decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(),
+ snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec,
+): SnappingFlingBehavior =
+ remember(lazyListState, decayAnimationSpec, snapAnimationSpec) {
+ SnappingFlingBehavior(
+ lazyListState = lazyListState,
+ decayAnimationSpec = decayAnimationSpec,
+ snapAnimationSpec = snapAnimationSpec,
+ )
+ }
+
+/**
+ * A snapping [FlingBehavior] for [LazyListState]. Typically this would be created via
+ * [rememberSnappingFlingBehavior].
+ *
+ * @param lazyListState The [LazyListState] to update.
+ * @param decayAnimationSpec The decay animation spec to use for decayed flings.
+ * @param snapAnimationSpec The animation spec to use when snapping.
+ */
+internal class SnappingFlingBehavior(
+ private val lazyListState: LazyListState,
+ private val decayAnimationSpec: DecayAnimationSpec<Float>,
+ private val snapAnimationSpec: AnimationSpec<Float>,
+) : FlingBehavior {
+ /** The target item index for any on-going animations. */
+ var animationTarget: Int? by mutableStateOf(null)
+ private set
+
+ override suspend fun ScrollScope.performFling(initialVelocity: Float): Float {
+ val itemInfo = currentItemInfo ?: return initialVelocity
+
+ // If the decay fling can scroll past the current item, fling with decay
+ return if (decayAnimationSpec.canFlingPastCurrentItem(itemInfo, initialVelocity)) {
+ performDecayFling(initialVelocity, itemInfo)
+ } else {
+ // Otherwise we 'spring' to current/next item
+ performSpringFling(
+ index =
+ when {
+ // If the velocity is greater than 1 item per second (velocity is px/s),
+ // spring
+ // in the relevant direction
+ initialVelocity > itemInfo.size -> {
+ (itemInfo.index + 1).coerceAtMost(
+ lazyListState.layoutInfo.totalItemsCount - 1
+ )
+ }
+ initialVelocity < -itemInfo.size -> itemInfo.index
+ // If the velocity is 0 (or less than the size of the item), spring to
+ // whichever item is closest to the snap point
+ itemInfo.offset < -itemInfo.size / 2 -> itemInfo.index + 1
+ else -> itemInfo.index
+ },
+ initialVelocity = initialVelocity,
+ )
+ }
+ }
+
+ private suspend fun ScrollScope.performDecayFling(
+ initialVelocity: Float,
+ startItem: LazyListItemInfo,
+ ): Float {
+ val index =
+ when {
+ initialVelocity > 0 -> startItem.index + 1
+ else -> startItem.index
+ }
+ val forward = index > (currentItemInfo?.index ?: return initialVelocity)
+
+ // Update the animationTarget
+ animationTarget = index
+
+ var velocityLeft = initialVelocity
+ var lastValue = 0f
+ AnimationState(
+ initialValue = 0f,
+ initialVelocity = initialVelocity,
+ )
+ .animateDecay(decayAnimationSpec) {
+ val delta = value - lastValue
+ val consumed = scrollBy(delta)
+ lastValue = value
+ velocityLeft = this.velocity
+
+ val current = currentItemInfo
+ if (current == null) {
+ cancelAnimation()
+ return@animateDecay
+ }
+
+ if (
+ !forward &&
+ (current.index < index || current.index == index && current.offset >= 0)
+ ) {
+ // 'snap back' to the item as we may have scrolled past it
+ scrollBy(lazyListState.calculateScrollOffsetToItem(index).toFloat())
+ cancelAnimation()
+ } else if (
+ forward &&
+ (current.index > index || current.index == index && current.offset <= 0)
+ ) {
+ // 'snap back' to the item as we may have scrolled past it
+ scrollBy(lazyListState.calculateScrollOffsetToItem(index).toFloat())
+ cancelAnimation()
+ } else if (abs(delta - consumed) > 0.5f) {
+ // avoid rounding errors and stop if anything is unconsumed
+ cancelAnimation()
+ }
+ }
+ animationTarget = null
+ return velocityLeft
+ }
+
+ private suspend fun ScrollScope.performSpringFling(
+ index: Int,
+ scrollOffset: Int = 0,
+ initialVelocity: Float = 0f,
+ ): Float {
+ // If we don't have a current layout, we can't snap
+ val initialItem = currentItemInfo ?: return initialVelocity
+
+ val forward = index > initialItem.index
+ // We add 10% on to the size of the current item, to compensate for any item spacing, etc
+ val target = (if (forward) initialItem.size else -initialItem.size) * 1.1f
+
+ // Update the animationTarget
+ animationTarget = index
+
+ var velocityLeft = initialVelocity
+ var lastValue = 0f
+ AnimationState(
+ initialValue = 0f,
+ initialVelocity = initialVelocity,
+ )
+ .animateTo(
+ targetValue = target,
+ animationSpec = snapAnimationSpec,
+ ) {
+ // Springs can overshoot their target, clamp to the desired range
+ val coercedValue =
+ if (forward) {
+ value.coerceAtMost(target)
+ } else {
+ value.coerceAtLeast(target)
+ }
+ val delta = coercedValue - lastValue
+ val consumed = scrollBy(delta)
+ lastValue = coercedValue
+ velocityLeft = this.velocity
+
+ val current = currentItemInfo
+ if (current == null) {
+ cancelAnimation()
+ return@animateTo
+ }
+
+ if (scrolledPastItem(initialVelocity, current, index, scrollOffset)) {
+ // If we've scrolled to/past the item, stop the animation. We may also need to
+ // 'snap back' to the item as we may have scrolled past it
+ scrollBy(lazyListState.calculateScrollOffsetToItem(index).toFloat())
+ cancelAnimation()
+ } else if (abs(delta - consumed) > 0.5f) {
+ // avoid rounding errors and stop if anything is unconsumed
+ cancelAnimation()
+ }
+ }
+ animationTarget = null
+ return velocityLeft
+ }
+
+ private fun LazyListState.calculateScrollOffsetToItem(index: Int): Int {
+ return layoutInfo.visibleItemsInfo.firstOrNull { it.index == index }?.offset ?: 0
+ }
+
+ private val currentItemInfo: LazyListItemInfo?
+ get() =
+ lazyListState.layoutInfo.visibleItemsInfo
+ .asSequence()
+ .filter { it.offset <= 0 && it.offset + it.size > 0 }
+ .lastOrNull()
+}
+
+private fun scrolledPastItem(
+ initialVelocity: Float,
+ currentItem: LazyListItemInfo,
+ targetIndex: Int,
+ targetScrollOffset: Int = 0,
+): Boolean {
+ return if (initialVelocity > 0) {
+ // forward
+ currentItem.index > targetIndex ||
+ (currentItem.index == targetIndex && currentItem.offset <= targetScrollOffset)
+ } else {
+ // backwards
+ currentItem.index < targetIndex ||
+ (currentItem.index == targetIndex && currentItem.offset >= targetScrollOffset)
+ }
+}
+
+private fun DecayAnimationSpec<Float>.canFlingPastCurrentItem(
+ currentItem: LazyListItemInfo,
+ initialVelocity: Float,
+): Boolean {
+ val targetValue =
+ calculateTargetValue(
+ initialValue = currentItem.offset.toFloat(),
+ initialVelocity = initialVelocity,
+ )
+ return when {
+ // forward. We add 10% onto the size to cater for any item spacing
+ initialVelocity > 0 -> targetValue <= -(currentItem.size * 1.1f)
+ // backwards. We add 10% onto the size to cater for any item spacing
+ else -> targetValue >= (currentItem.size * 0.1f)
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt
new file mode 100644
index 000000000000..3b13c0b78cbe
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.compose.modifiers
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.LayoutModifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
+import androidx.compose.ui.unit.offset
+
+// This file was mostly copy/pasted from by androidx.compose.foundation.layout.Padding.kt and
+// contains modifiers with lambda parameters to change the padding of a Composable without
+// triggering recomposition when the paddings change.
+//
+// These should be used instead of the traditional size modifiers when the size changes often, for
+// instance when it is animated.
+//
+// TODO(b/247473910): Remove these modifiers once they can be fully replaced by layout animations
+// APIs.
+
+/** @see androidx.compose.foundation.layout.padding */
+fun Modifier.padding(
+ start: Density.() -> Int = PaddingUnspecified,
+ top: Density.() -> Int = PaddingUnspecified,
+ end: Density.() -> Int = PaddingUnspecified,
+ bottom: Density.() -> Int = PaddingUnspecified,
+) =
+ this.then(
+ PaddingModifier(
+ start,
+ top,
+ end,
+ bottom,
+ rtlAware = true,
+ inspectorInfo =
+ debugInspectorInfo {
+ name = "padding"
+ properties["start"] = start
+ properties["top"] = top
+ properties["end"] = end
+ properties["bottom"] = bottom
+ }
+ )
+ )
+
+/** @see androidx.compose.foundation.layout.padding */
+fun Modifier.padding(
+ horizontal: Density.() -> Int = PaddingUnspecified,
+ vertical: Density.() -> Int = PaddingUnspecified,
+): Modifier {
+ return this.then(
+ PaddingModifier(
+ start = horizontal,
+ top = vertical,
+ end = horizontal,
+ bottom = vertical,
+ rtlAware = true,
+ inspectorInfo =
+ debugInspectorInfo {
+ name = "padding"
+ properties["horizontal"] = horizontal
+ properties["vertical"] = vertical
+ }
+ )
+ )
+}
+
+private val PaddingUnspecified: Density.() -> Int = { 0 }
+
+private class PaddingModifier(
+ val start: Density.() -> Int,
+ val top: Density.() -> Int,
+ val end: Density.() -> Int,
+ val bottom: Density.() -> Int,
+ val rtlAware: Boolean,
+ inspectorInfo: InspectorInfo.() -> Unit
+) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ val start = start()
+ val top = top()
+ val end = end()
+ val bottom = bottom()
+
+ val horizontal = start + end
+ val vertical = top + bottom
+
+ val placeable = measurable.measure(constraints.offset(-horizontal, -vertical))
+
+ val width = constraints.constrainWidth(placeable.width + horizontal)
+ val height = constraints.constrainHeight(placeable.height + vertical)
+ return layout(width, height) {
+ if (rtlAware) {
+ placeable.placeRelative(start, top)
+ } else {
+ placeable.place(start, top)
+ }
+ }
+ }
+
+ override fun hashCode(): Int {
+ var result = start.hashCode()
+ result = 31 * result + top.hashCode()
+ result = 31 * result + end.hashCode()
+ result = 31 * result + bottom.hashCode()
+ result = 31 * result + rtlAware.hashCode()
+ return result
+ }
+
+ override fun equals(other: Any?): Boolean {
+ val otherModifier = other as? PaddingModifier ?: return false
+ return start == otherModifier.start &&
+ top == otherModifier.top &&
+ end == otherModifier.end &&
+ bottom == otherModifier.bottom &&
+ rtlAware == otherModifier.rtlAware
+ }
+}
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt
new file mode 100644
index 000000000000..570d24312c80
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.compose.modifiers
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.IntrinsicMeasurable
+import androidx.compose.ui.layout.IntrinsicMeasureScope
+import androidx.compose.ui.layout.LayoutModifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.platform.InspectorInfo
+import androidx.compose.ui.platform.InspectorValueInfo
+import androidx.compose.ui.platform.debugInspectorInfo
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.Density
+import androidx.compose.ui.unit.constrain
+import androidx.compose.ui.unit.constrainHeight
+import androidx.compose.ui.unit.constrainWidth
+
+// This file was mostly copy pasted from androidx.compose.foundation.layout.Size.kt and contains
+// modifiers with lambda parameters to change the (min/max) size of a Composable without triggering
+// recomposition when the sizes change.
+//
+// These should be used instead of the traditional size modifiers when the size changes often, for
+// instance when it is animated.
+//
+// TODO(b/247473910): Remove these modifiers once they can be fully replaced by layout animations
+// APIs.
+
+/** @see androidx.compose.foundation.layout.width */
+fun Modifier.width(width: Density.() -> Int) =
+ this.then(
+ SizeModifier(
+ minWidth = width,
+ maxWidth = width,
+ enforceIncoming = true,
+ inspectorInfo =
+ debugInspectorInfo {
+ name = "width"
+ value = width
+ }
+ )
+ )
+
+/** @see androidx.compose.foundation.layout.height */
+fun Modifier.height(height: Density.() -> Int) =
+ this.then(
+ SizeModifier(
+ minHeight = height,
+ maxHeight = height,
+ enforceIncoming = true,
+ inspectorInfo =
+ debugInspectorInfo {
+ name = "height"
+ value = height
+ }
+ )
+ )
+
+/** @see androidx.compose.foundation.layout.size */
+fun Modifier.size(width: Density.() -> Int, height: Density.() -> Int) =
+ this.then(
+ SizeModifier(
+ minWidth = width,
+ maxWidth = width,
+ minHeight = height,
+ maxHeight = height,
+ enforceIncoming = true,
+ inspectorInfo =
+ debugInspectorInfo {
+ name = "size"
+ properties["width"] = width
+ properties["height"] = height
+ }
+ )
+ )
+
+private val SizeUnspecified: Density.() -> Int = { 0 }
+
+private class SizeModifier(
+ private val minWidth: Density.() -> Int = SizeUnspecified,
+ private val minHeight: Density.() -> Int = SizeUnspecified,
+ private val maxWidth: Density.() -> Int = SizeUnspecified,
+ private val maxHeight: Density.() -> Int = SizeUnspecified,
+ private val enforceIncoming: Boolean,
+ inspectorInfo: InspectorInfo.() -> Unit
+) : LayoutModifier, InspectorValueInfo(inspectorInfo) {
+ private val Density.targetConstraints: Constraints
+ get() {
+ val maxWidth =
+ if (maxWidth != SizeUnspecified) {
+ maxWidth().coerceAtLeast(0)
+ } else {
+ Constraints.Infinity
+ }
+ val maxHeight =
+ if (maxHeight != SizeUnspecified) {
+ maxHeight().coerceAtLeast(0)
+ } else {
+ Constraints.Infinity
+ }
+ val minWidth =
+ if (minWidth != SizeUnspecified) {
+ minWidth().coerceAtMost(maxWidth).coerceAtLeast(0).let {
+ if (it != Constraints.Infinity) it else 0
+ }
+ } else {
+ 0
+ }
+ val minHeight =
+ if (minHeight != SizeUnspecified) {
+ minHeight().coerceAtMost(maxHeight).coerceAtLeast(0).let {
+ if (it != Constraints.Infinity) it else 0
+ }
+ } else {
+ 0
+ }
+ return Constraints(
+ minWidth = minWidth,
+ minHeight = minHeight,
+ maxWidth = maxWidth,
+ maxHeight = maxHeight
+ )
+ }
+
+ override fun MeasureScope.measure(
+ measurable: Measurable,
+ constraints: Constraints
+ ): MeasureResult {
+ val wrappedConstraints =
+ targetConstraints.let { targetConstraints ->
+ if (enforceIncoming) {
+ constraints.constrain(targetConstraints)
+ } else {
+ val resolvedMinWidth =
+ if (minWidth != SizeUnspecified) {
+ targetConstraints.minWidth
+ } else {
+ constraints.minWidth.coerceAtMost(targetConstraints.maxWidth)
+ }
+ val resolvedMaxWidth =
+ if (maxWidth != SizeUnspecified) {
+ targetConstraints.maxWidth
+ } else {
+ constraints.maxWidth.coerceAtLeast(targetConstraints.minWidth)
+ }
+ val resolvedMinHeight =
+ if (minHeight != SizeUnspecified) {
+ targetConstraints.minHeight
+ } else {
+ constraints.minHeight.coerceAtMost(targetConstraints.maxHeight)
+ }
+ val resolvedMaxHeight =
+ if (maxHeight != SizeUnspecified) {
+ targetConstraints.maxHeight
+ } else {
+ constraints.maxHeight.coerceAtLeast(targetConstraints.minHeight)
+ }
+ Constraints(
+ resolvedMinWidth,
+ resolvedMaxWidth,
+ resolvedMinHeight,
+ resolvedMaxHeight
+ )
+ }
+ }
+ val placeable = measurable.measure(wrappedConstraints)
+ return layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) }
+ }
+
+ override fun IntrinsicMeasureScope.minIntrinsicWidth(
+ measurable: IntrinsicMeasurable,
+ height: Int
+ ): Int {
+ val constraints = targetConstraints
+ return if (constraints.hasFixedWidth) {
+ constraints.maxWidth
+ } else {
+ constraints.constrainWidth(measurable.minIntrinsicWidth(height))
+ }
+ }
+
+ override fun IntrinsicMeasureScope.minIntrinsicHeight(
+ measurable: IntrinsicMeasurable,
+ width: Int
+ ): Int {
+ val constraints = targetConstraints
+ return if (constraints.hasFixedHeight) {
+ constraints.maxHeight
+ } else {
+ constraints.constrainHeight(measurable.minIntrinsicHeight(width))
+ }
+ }
+
+ override fun IntrinsicMeasureScope.maxIntrinsicWidth(
+ measurable: IntrinsicMeasurable,
+ height: Int
+ ): Int {
+ val constraints = targetConstraints
+ return if (constraints.hasFixedWidth) {
+ constraints.maxWidth
+ } else {
+ constraints.constrainWidth(measurable.maxIntrinsicWidth(height))
+ }
+ }
+
+ override fun IntrinsicMeasureScope.maxIntrinsicHeight(
+ measurable: IntrinsicMeasurable,
+ width: Int
+ ): Int {
+ val constraints = targetConstraints
+ return if (constraints.hasFixedHeight) {
+ constraints.maxHeight
+ } else {
+ constraints.constrainHeight(measurable.maxIntrinsicHeight(width))
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is SizeModifier) return false
+ return minWidth == other.minWidth &&
+ minHeight == other.minHeight &&
+ maxWidth == other.maxWidth &&
+ maxHeight == other.maxHeight &&
+ enforceIncoming == other.enforceIncoming
+ }
+
+ override fun hashCode() =
+ (((((minWidth.hashCode() * 31 + minHeight.hashCode()) * 31) + maxWidth.hashCode()) * 31) +
+ maxHeight.hashCode()) * 31
+}
diff --git a/packages/SystemUI/compose/gallery/Android.bp b/packages/SystemUI/compose/gallery/Android.bp
deleted file mode 100644
index 5a7a1e1807a3..000000000000
--- a/packages/SystemUI/compose/gallery/Android.bp
+++ /dev/null
@@ -1,86 +0,0 @@
-// Copyright (C) 2022 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES 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_packages_SystemUI_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"],
-}
-
-android_library {
- name: "SystemUIComposeGalleryLib",
- manifest: "AndroidManifest.xml",
-
- srcs: [
- "src/**/*.kt",
- ":SystemUI-tests-utils",
- ],
-
- resource_dirs: [
- "res",
- ],
-
- static_libs: [
- "SystemUI-core",
- "SystemUIComposeCore",
- "SystemUIComposeFeatures",
-
- "androidx.compose.runtime_runtime",
- "androidx.compose.material3_material3",
- "androidx.compose.material_material-icons-extended",
- "androidx.activity_activity-compose",
- "androidx.navigation_navigation-compose",
-
- "androidx.appcompat_appcompat",
-
- // TODO(b/240431193): Remove the dependencies and depend on
- // SystemUI-test-utils directly.
- "androidx.test.runner",
- "mockito-target-extended-minus-junit4",
- "testables",
- "truth-prebuilt",
- "androidx.test.uiautomator",
- "kotlinx_coroutines_test",
- ],
-
- libs: [
- "android.test.mock",
- ],
-
- kotlincflags: ["-Xjvm-default=all"],
-}
-
-android_app {
- name: "SystemUIComposeGallery",
- defaults: ["platform_app_defaults"],
- manifest: "app/AndroidManifest.xml",
-
- static_libs: [
- "SystemUIComposeGalleryLib",
- ],
-
- platform_apis: true,
- system_ext_specific: true,
- certificate: "platform",
- privileged: true,
-
- optimize: {
- proguard_flags_files: ["proguard-rules.pro"],
- },
-
- dxflags: ["--multi-dex"],
-}
diff --git a/packages/SystemUI/compose/gallery/AndroidManifest.xml b/packages/SystemUI/compose/gallery/AndroidManifest.xml
deleted file mode 100644
index 2f30651a6acf..000000000000
--- a/packages/SystemUI/compose/gallery/AndroidManifest.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.systemui.compose.gallery">
- <!-- To emulate a display size and density. -->
- <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-
- <application
- android:name="android.app.Application"
- android:appComponentFactory="androidx.core.app.AppComponentFactory"
- tools:replace="android:name,android:appComponentFactory">
- <!-- Disable providers from SystemUI -->
- <provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider"
- android:authorities="com.android.systemui.test.keyguard.disabled"
- android:enabled="false"
- tools:replace="android:authorities"
- tools:node="remove" />
- <provider android:name="com.google.android.systemui.keyguard.KeyguardSliceProviderGoogle"
- android:authorities="com.android.systemui.test.keyguard.disabled"
- android:enabled="false"
- tools:replace="android:authorities"
- tools:node="remove" />
- <provider android:name="com.android.keyguard.clock.ClockOptionsProvider"
- android:authorities="com.android.systemui.test.keyguard.clock.disabled"
- android:enabled="false"
- tools:replace="android:authorities"
- tools:node="remove" />
- <provider android:name="com.android.systemui.people.PeopleProvider"
- android:authorities="com.android.systemui.test.people.disabled"
- android:enabled="false"
- tools:replace="android:authorities"
- tools:node="remove" />
- <provider android:name="androidx.core.content.FileProvider"
- android:authorities="com.android.systemui.test.fileprovider.disabled"
- android:enabled="false"
- tools:replace="android:authorities"
- tools:node="remove"/>
- </application>
-</manifest>
diff --git a/packages/SystemUI/compose/gallery/TEST_MAPPING b/packages/SystemUI/compose/gallery/TEST_MAPPING
deleted file mode 100644
index c7f8a9216418..000000000000
--- a/packages/SystemUI/compose/gallery/TEST_MAPPING
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "presubmit": [
- {
- "name": "SystemUIComposeGalleryTests",
- "options": [
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- }
- ]
-} \ No newline at end of file
diff --git a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml
deleted file mode 100644
index 1f3fd8c312d9..000000000000
--- a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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"
- xmlns:tools="http://schemas.android.com/tools"
- package="com.android.systemui.compose.gallery.app">
- <application
- android:allowBackup="true"
- android:icon="@mipmap/ic_launcher"
- android:label="@string/app_name"
- android:roundIcon="@mipmap/ic_launcher_round"
- android:supportsRtl="true"
- android:theme="@style/Theme.SystemUI.Gallery"
- tools:replace="android:icon,android:theme,android:label">
- <activity
- android:name="com.android.systemui.compose.gallery.GalleryActivity"
- android:exported="true"
- android:label="@string/app_name">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-</manifest>
diff --git a/packages/SystemUI/compose/gallery/proguard-rules.pro b/packages/SystemUI/compose/gallery/proguard-rules.pro
deleted file mode 100644
index 481bb4348141..000000000000
--- a/packages/SystemUI/compose/gallery/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile \ No newline at end of file
diff --git a/packages/SystemUI/compose/gallery/res/values/colors.xml b/packages/SystemUI/compose/gallery/res/values/colors.xml
deleted file mode 100644
index a2fcbffc26c0..000000000000
--- a/packages/SystemUI/compose/gallery/res/values/colors.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources>
- <color name="ic_launcher_background">#FFFFFF</color>
-</resources> \ No newline at end of file
diff --git a/packages/SystemUI/compose/gallery/res/values/strings.xml b/packages/SystemUI/compose/gallery/res/values/strings.xml
deleted file mode 100644
index 86bdb0568837..000000000000
--- a/packages/SystemUI/compose/gallery/res/values/strings.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources>
- <!-- Application name [CHAR LIMIT=NONE] -->
- <string name="app_name">SystemUI Gallery</string>
-</resources> \ No newline at end of file
diff --git a/packages/SystemUI/compose/gallery/res/values/themes.xml b/packages/SystemUI/compose/gallery/res/values/themes.xml
deleted file mode 100644
index 45fa1f5dfb5c..000000000000
--- a/packages/SystemUI/compose/gallery/res/values/themes.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<resources xmlns:tools="http://schemas.android.com/tools">
- <style name="Theme.SystemUI.Gallery">
- <item name="android:windowActionBar">false</item>
- <item name="android:windowNoTitle">true</item>
-
- <item name="android:statusBarColor" tools:targetApi="l">
- @android:color/transparent
- </item>
- <item name="android:navigationBarColor" tools:targetApi="l">
- @android:color/transparent
- </item>
- <item name="android:windowLightStatusBar">true</item>
- </style>
-</resources>
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt
deleted file mode 100644
index 881a1def113a..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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:OptIn(ExperimentalMaterial3Api::class)
-
-package com.android.systemui.compose.gallery
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import com.android.systemui.compose.SysUiButton
-import com.android.systemui.compose.SysUiOutlinedButton
-import com.android.systemui.compose.SysUiTextButton
-
-@Composable
-fun ButtonsScreen(
- modifier: Modifier = Modifier,
-) {
- Column(
- modifier = modifier,
- ) {
- SysUiButton(
- onClick = {},
- ) {
- Text("SysUiButton")
- }
-
- SysUiButton(
- onClick = {},
- enabled = false,
- ) {
- Text("SysUiButton - disabled")
- }
-
- SysUiOutlinedButton(
- onClick = {},
- ) {
- Text("SysUiOutlinedButton")
- }
-
- SysUiOutlinedButton(
- onClick = {},
- enabled = false,
- ) {
- Text("SysUiOutlinedButton - disabled")
- }
-
- SysUiTextButton(
- onClick = {},
- ) {
- Text("SysUiTextButton")
- }
-
- SysUiTextButton(
- onClick = {},
- enabled = false,
- ) {
- Text("SysUiTextButton - disabled")
- }
- }
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt
deleted file mode 100644
index dfa1b26f464e..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.compose.gallery
-
-import androidx.compose.foundation.background
-import androidx.compose.foundation.border
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.size
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.unit.dp
-import com.android.systemui.compose.theme.LocalAndroidColorScheme
-
-/** The screen that shows all the Material 3 colors. */
-@Composable
-fun MaterialColorsScreen() {
- val colors = MaterialTheme.colorScheme
- ColorsScreen(
- listOf(
- "primary" to colors.primary,
- "onPrimary" to colors.onPrimary,
- "primaryContainer" to colors.primaryContainer,
- "onPrimaryContainer" to colors.onPrimaryContainer,
- "inversePrimary" to colors.inversePrimary,
- "secondary" to colors.secondary,
- "onSecondary" to colors.onSecondary,
- "secondaryContainer" to colors.secondaryContainer,
- "onSecondaryContainer" to colors.onSecondaryContainer,
- "tertiary" to colors.tertiary,
- "onTertiary" to colors.onTertiary,
- "tertiaryContainer" to colors.tertiaryContainer,
- "onTertiaryContainer" to colors.onTertiaryContainer,
- "background" to colors.background,
- "onBackground" to colors.onBackground,
- "surface" to colors.surface,
- "onSurface" to colors.onSurface,
- "surfaceVariant" to colors.surfaceVariant,
- "onSurfaceVariant" to colors.onSurfaceVariant,
- "inverseSurface" to colors.inverseSurface,
- "inverseOnSurface" to colors.inverseOnSurface,
- "error" to colors.error,
- "onError" to colors.onError,
- "errorContainer" to colors.errorContainer,
- "onErrorContainer" to colors.onErrorContainer,
- "outline" to colors.outline,
- )
- )
-}
-
-/** The screen that shows all the Android colors. */
-@Composable
-fun AndroidColorsScreen() {
- val colors = LocalAndroidColorScheme.current
- ColorsScreen(
- listOf(
- "colorPrimary" to colors.colorPrimary,
- "colorPrimaryDark" to colors.colorPrimaryDark,
- "colorAccent" to colors.colorAccent,
- "colorAccentPrimary" to colors.colorAccentPrimary,
- "colorAccentSecondary" to colors.colorAccentSecondary,
- "colorAccentTertiary" to colors.colorAccentTertiary,
- "colorAccentPrimaryVariant" to colors.colorAccentPrimaryVariant,
- "colorAccentSecondaryVariant" to colors.colorAccentSecondaryVariant,
- "colorAccentTertiaryVariant" to colors.colorAccentTertiaryVariant,
- "colorSurface" to colors.colorSurface,
- "colorSurfaceHighlight" to colors.colorSurfaceHighlight,
- "colorSurfaceVariant" to colors.colorSurfaceVariant,
- "colorSurfaceHeader" to colors.colorSurfaceHeader,
- "colorError" to colors.colorError,
- "colorBackground" to colors.colorBackground,
- "colorBackgroundFloating" to colors.colorBackgroundFloating,
- "panelColorBackground" to colors.panelColorBackground,
- "textColorPrimary" to colors.textColorPrimary,
- "textColorSecondary" to colors.textColorSecondary,
- "textColorTertiary" to colors.textColorTertiary,
- "textColorPrimaryInverse" to colors.textColorPrimaryInverse,
- "textColorSecondaryInverse" to colors.textColorSecondaryInverse,
- "textColorTertiaryInverse" to colors.textColorTertiaryInverse,
- "textColorOnAccent" to colors.textColorOnAccent,
- "colorForeground" to colors.colorForeground,
- "colorForegroundInverse" to colors.colorForegroundInverse,
- )
- )
-}
-
-@Composable
-private fun ColorsScreen(
- colors: List<Pair<String, Color>>,
-) {
- LazyColumn(
- Modifier.fillMaxWidth(),
- ) {
- colors.forEach { (name, color) -> item { ColorTile(color, name) } }
- }
-}
-
-@Composable
-private fun ColorTile(
- color: Color,
- name: String,
-) {
- Row(
- Modifier.padding(16.dp),
- verticalAlignment = Alignment.CenterVertically,
- ) {
- val shape = RoundedCornerShape(16.dp)
- Spacer(
- Modifier.border(1.dp, MaterialTheme.colorScheme.onBackground, shape)
- .background(color, shape)
- .size(64.dp)
- )
- Spacer(Modifier.width(16.dp))
- Text(name)
- }
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt
deleted file mode 100644
index 990d060207df..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt
+++ /dev/null
@@ -1,210 +0,0 @@
-package com.android.systemui.compose.gallery
-
-import android.graphics.Point
-import android.os.UserHandle
-import android.view.Display
-import android.view.WindowManagerGlobal
-import androidx.compose.foundation.layout.RowScope
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.lazy.LazyRow
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.DarkMode
-import androidx.compose.material.icons.filled.FormatSize
-import androidx.compose.material.icons.filled.FormatTextdirectionLToR
-import androidx.compose.material.icons.filled.FormatTextdirectionRToL
-import androidx.compose.material.icons.filled.InvertColors
-import androidx.compose.material.icons.filled.LightMode
-import androidx.compose.material.icons.filled.Smartphone
-import androidx.compose.material.icons.filled.Tablet
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.Icon
-import androidx.compose.material3.Text
-import androidx.compose.material3.TextButton
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.vector.ImageVector
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import kotlin.math.max
-import kotlin.math.min
-
-enum class FontScale(val scale: Float) {
- Small(0.85f),
- Normal(1f),
- Big(1.15f),
- Bigger(1.30f),
-}
-
-/** A configuration panel that allows to toggle the theme, font scale and layout direction. */
-@Composable
-fun ConfigurationControls(
- theme: Theme,
- fontScale: FontScale,
- layoutDirection: LayoutDirection,
- onChangeTheme: () -> Unit,
- onChangeLayoutDirection: () -> Unit,
- onChangeFontScale: () -> Unit,
- modifier: Modifier = Modifier,
-) {
- // The display we are emulating, if any.
- var emulatedDisplayName by rememberSaveable { mutableStateOf<String?>(null) }
- val emulatedDisplay =
- emulatedDisplayName?.let { name -> EmulatedDisplays.firstOrNull { it.name == name } }
-
- LaunchedEffect(emulatedDisplay) {
- val wm = WindowManagerGlobal.getWindowManagerService()
-
- val defaultDisplayId = Display.DEFAULT_DISPLAY
- if (emulatedDisplay == null) {
- wm.clearForcedDisplayDensityForUser(defaultDisplayId, UserHandle.myUserId())
- wm.clearForcedDisplaySize(defaultDisplayId)
- } else {
- val density = emulatedDisplay.densityDpi
-
- // Emulate the display and make sure that we use the maximum available space possible.
- val initialSize = Point()
- wm.getInitialDisplaySize(defaultDisplayId, initialSize)
- val width = emulatedDisplay.width
- val height = emulatedDisplay.height
- val minOfSize = min(width, height)
- val maxOfSize = max(width, height)
- if (initialSize.x < initialSize.y) {
- wm.setForcedDisplaySize(defaultDisplayId, minOfSize, maxOfSize)
- } else {
- wm.setForcedDisplaySize(defaultDisplayId, maxOfSize, minOfSize)
- }
- wm.setForcedDisplayDensityForUser(defaultDisplayId, density, UserHandle.myUserId())
- }
- }
-
- // TODO(b/231131244): Fork FlowRow from Accompanist and use that instead to make sure that users
- // don't miss any available configuration.
- LazyRow(modifier) {
- // Dark/light theme.
- item {
- TextButton(onChangeTheme) {
- val text: String
- val icon: ImageVector
-
- when (theme) {
- Theme.System -> {
- icon = Icons.Default.InvertColors
- text = "System"
- }
- Theme.Dark -> {
- icon = Icons.Default.DarkMode
- text = "Dark"
- }
- Theme.Light -> {
- icon = Icons.Default.LightMode
- text = "Light"
- }
- }
-
- Icon(icon, null)
- Spacer(Modifier.width(8.dp))
- Text(text)
- }
- }
-
- // Font scale.
- item {
- TextButton(onChangeFontScale) {
- Icon(Icons.Default.FormatSize, null)
- Spacer(Modifier.width(8.dp))
-
- Text(fontScale.name)
- }
- }
-
- // Layout direction.
- item {
- TextButton(onChangeLayoutDirection) {
- when (layoutDirection) {
- LayoutDirection.Ltr -> {
- Icon(Icons.Default.FormatTextdirectionLToR, null)
- Spacer(Modifier.width(8.dp))
- Text("LTR")
- }
- LayoutDirection.Rtl -> {
- Icon(Icons.Default.FormatTextdirectionRToL, null)
- Spacer(Modifier.width(8.dp))
- Text("RTL")
- }
- }
- }
- }
-
- // Display emulation.
- EmulatedDisplays.forEach { display ->
- item {
- DisplayButton(
- display,
- emulatedDisplay == display,
- { emulatedDisplayName = it?.name },
- )
- }
- }
- }
-}
-
-@Composable
-private fun DisplayButton(
- display: EmulatedDisplay,
- selected: Boolean,
- onChangeEmulatedDisplay: (EmulatedDisplay?) -> Unit,
-) {
- val onClick = {
- if (selected) {
- onChangeEmulatedDisplay(null)
- } else {
- onChangeEmulatedDisplay(display)
- }
- }
-
- val content: @Composable RowScope.() -> Unit = {
- Icon(display.icon, null)
- Spacer(Modifier.width(8.dp))
- Text(display.name)
- }
-
- if (selected) {
- Button(onClick, contentPadding = ButtonDefaults.TextButtonContentPadding, content = content)
- } else {
- TextButton(onClick, content = content)
- }
-}
-
-/** The displays that can be emulated from this Gallery app. */
-private val EmulatedDisplays =
- listOf(
- EmulatedDisplay(
- "Phone",
- Icons.Default.Smartphone,
- width = 1440,
- height = 3120,
- densityDpi = 560,
- ),
- EmulatedDisplay(
- "Tablet",
- Icons.Default.Tablet,
- width = 2560,
- height = 1600,
- densityDpi = 320,
- ),
- )
-
-private data class EmulatedDisplay(
- val name: String,
- val icon: ImageVector,
- val width: Int,
- val height: Int,
- val densityDpi: Int,
-)
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt
deleted file mode 100644
index bb2d2feba39f..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.compose.gallery
-
-import android.app.UiModeManager
-import android.content.Context
-import android.os.Bundle
-import androidx.activity.ComponentActivity
-import androidx.activity.compose.setContent
-import androidx.compose.foundation.isSystemInDarkTheme
-import androidx.compose.runtime.SideEffect
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.graphics.Color
-import androidx.core.view.WindowCompat
-import com.android.systemui.compose.rememberSystemUiController
-
-class GalleryActivity : ComponentActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- WindowCompat.setDecorFitsSystemWindows(window, false)
- val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
-
- setContent {
- var theme by rememberSaveable { mutableStateOf(Theme.System) }
- val onChangeTheme = {
- // Change to the next theme for a toggle behavior.
- theme =
- when (theme) {
- Theme.System -> Theme.Dark
- Theme.Dark -> Theme.Light
- Theme.Light -> Theme.System
- }
- }
-
- val isSystemInDarkTheme = isSystemInDarkTheme()
- val isDark = theme == Theme.Dark || (theme == Theme.System && isSystemInDarkTheme)
- val useDarkIcons = !isDark
- val systemUiController = rememberSystemUiController()
- SideEffect {
- systemUiController.setSystemBarsColor(
- color = Color.Transparent,
- darkIcons = useDarkIcons,
- )
-
- uiModeManager.setApplicationNightMode(
- when (theme) {
- Theme.System -> UiModeManager.MODE_NIGHT_AUTO
- Theme.Dark -> UiModeManager.MODE_NIGHT_YES
- Theme.Light -> UiModeManager.MODE_NIGHT_NO
- }
- )
- }
-
- GalleryApp(theme, onChangeTheme)
- }
- }
-}
-
-enum class Theme {
- System,
- Dark,
- Light,
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
deleted file mode 100644
index 2e6456bcc4e1..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt
+++ /dev/null
@@ -1,155 +0,0 @@
-package com.android.systemui.compose.gallery
-
-import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.systemBarsPadding
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.CompositionLocalProvider
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalDensity
-import androidx.compose.ui.platform.LocalLayoutDirection
-import androidx.compose.ui.unit.Density
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-import androidx.navigation.compose.NavHost
-import androidx.navigation.compose.rememberNavController
-import com.android.systemui.compose.theme.SystemUITheme
-
-/** The gallery app screens. */
-object GalleryAppScreens {
- val Typography = ChildScreen("typography") { TypographyScreen() }
- val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() }
- val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() }
- val Buttons = ChildScreen("buttons") { ButtonsScreen() }
- val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() }
-
- val PeopleEmpty =
- ChildScreen("people_empty") { navController ->
- EmptyPeopleScreen(onResult = { navController.popBackStack() })
- }
- val PeopleFew =
- ChildScreen("people_few") { navController ->
- FewPeopleScreen(onResult = { navController.popBackStack() })
- }
- val PeopleFull =
- ChildScreen("people_full") { navController ->
- FullPeopleScreen(onResult = { navController.popBackStack() })
- }
- val People =
- ParentScreen(
- "people",
- mapOf(
- "Empty" to PeopleEmpty,
- "Few" to PeopleFew,
- "Full" to PeopleFull,
- )
- )
-
- val Home =
- ParentScreen(
- "home",
- mapOf(
- "Typography" to Typography,
- "Material colors" to MaterialColors,
- "Android colors" to AndroidColors,
- "Example feature" to ExampleFeature,
- "Buttons" to Buttons,
- "People" to People,
- )
- )
-}
-
-/** The main content of the app, that shows [GalleryAppScreens.Home] by default. */
-@Composable
-private fun MainContent(onControlToggleRequested: () -> Unit) {
- Box(Modifier.fillMaxSize()) {
- val navController = rememberNavController()
- NavHost(
- navController = navController,
- startDestination = GalleryAppScreens.Home.identifier,
- ) {
- screen(GalleryAppScreens.Home, navController, onControlToggleRequested)
- }
- }
-}
-
-/**
- * The top-level composable shown when starting the app. This composable always shows a
- * [ConfigurationControls] at the top of the screen, above the [MainContent].
- */
-@Composable
-fun GalleryApp(
- theme: Theme,
- onChangeTheme: () -> Unit,
-) {
- val systemFontScale = LocalDensity.current.fontScale
- var fontScale: FontScale by rememberSaveable {
- mutableStateOf(
- FontScale.values().firstOrNull { it.scale == systemFontScale } ?: FontScale.Normal
- )
- }
- val context = LocalContext.current
- val density = Density(context.resources.displayMetrics.density, fontScale.scale)
- val onChangeFontScale = {
- fontScale =
- when (fontScale) {
- FontScale.Small -> FontScale.Normal
- FontScale.Normal -> FontScale.Big
- FontScale.Big -> FontScale.Bigger
- FontScale.Bigger -> FontScale.Small
- }
- }
-
- val systemLayoutDirection = LocalLayoutDirection.current
- var layoutDirection by rememberSaveable { mutableStateOf(systemLayoutDirection) }
- val onChangeLayoutDirection = {
- layoutDirection =
- when (layoutDirection) {
- LayoutDirection.Ltr -> LayoutDirection.Rtl
- LayoutDirection.Rtl -> LayoutDirection.Ltr
- }
- }
-
- CompositionLocalProvider(
- LocalDensity provides density,
- LocalLayoutDirection provides layoutDirection,
- ) {
- SystemUITheme {
- Surface(
- Modifier.fillMaxSize(),
- color = MaterialTheme.colorScheme.background,
- ) {
- Column(Modifier.fillMaxSize().systemBarsPadding()) {
- var showControls by rememberSaveable { mutableStateOf(true) }
-
- if (showControls) {
- ConfigurationControls(
- theme,
- fontScale,
- layoutDirection,
- onChangeTheme,
- onChangeLayoutDirection,
- onChangeFontScale,
- Modifier.padding(horizontal = 16.dp),
- )
-
- Spacer(Modifier.height(4.dp))
- }
-
- MainContent(onControlToggleRequested = { showControls = !showControls })
- }
- }
- }
- }
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/PeopleScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/PeopleScreen.kt
deleted file mode 100644
index 2f0df7790ffd..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/PeopleScreen.kt
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.compose.gallery
-
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.LocalContext
-import com.android.systemui.people.emptyPeopleSpaceViewModel
-import com.android.systemui.people.fewPeopleSpaceViewModel
-import com.android.systemui.people.fullPeopleSpaceViewModel
-import com.android.systemui.people.ui.compose.PeopleScreen
-import com.android.systemui.people.ui.viewmodel.PeopleViewModel
-
-@Composable
-fun EmptyPeopleScreen(onResult: (PeopleViewModel.Result) -> Unit) {
- val context = LocalContext.current.applicationContext
- val viewModel = emptyPeopleSpaceViewModel(context)
- PeopleScreen(viewModel, onResult)
-}
-
-@Composable
-fun FewPeopleScreen(onResult: (PeopleViewModel.Result) -> Unit) {
- val context = LocalContext.current.applicationContext
- val viewModel = fewPeopleSpaceViewModel(context)
- PeopleScreen(viewModel, onResult)
-}
-
-@Composable
-fun FullPeopleScreen(onResult: (PeopleViewModel.Result) -> Unit) {
- val context = LocalContext.current.applicationContext
- val viewModel = fullPeopleSpaceViewModel(context)
- PeopleScreen(viewModel, onResult)
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt
deleted file mode 100644
index d7d0d721b01c..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.compose.gallery
-
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.shape.CircleShape
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Surface
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-import androidx.navigation.NavController
-import androidx.navigation.NavGraphBuilder
-import androidx.navigation.compose.composable
-import androidx.navigation.compose.navigation
-
-/**
- * A screen in an app. It is either an [ParentScreen] which lists its child screens to navigate to
- * them or a [ChildScreen] which shows some content.
- */
-sealed class Screen(val identifier: String)
-
-class ParentScreen(
- identifier: String,
- val children: Map<String, Screen>,
-) : Screen(identifier)
-
-class ChildScreen(
- identifier: String,
- val content: @Composable (NavController) -> Unit,
-) : Screen(identifier)
-
-/** Create the navigation graph for [screen]. */
-fun NavGraphBuilder.screen(
- screen: Screen,
- navController: NavController,
- onControlToggleRequested: () -> Unit,
-) {
- when (screen) {
- is ChildScreen -> composable(screen.identifier) { screen.content(navController) }
- is ParentScreen -> {
- val menuRoute = "${screen.identifier}_menu"
- navigation(startDestination = menuRoute, route = screen.identifier) {
- // The menu to navigate to one of the children screens.
- composable(menuRoute) {
- ScreenMenu(screen, navController, onControlToggleRequested)
- }
-
- // The content of the child screens.
- screen.children.forEach { (_, child) ->
- screen(
- child,
- navController,
- onControlToggleRequested,
- )
- }
- }
- }
- }
-}
-
-@Composable
-private fun ScreenMenu(
- screen: ParentScreen,
- navController: NavController,
- onControlToggleRequested: () -> Unit,
-) {
- LazyColumn(
- Modifier.padding(horizontal = 16.dp),
- verticalArrangement = Arrangement.spacedBy(8.dp),
- ) {
- item {
- Surface(
- Modifier.fillMaxWidth(),
- color = MaterialTheme.colorScheme.tertiaryContainer,
- shape = CircleShape,
- ) {
- Column(
- Modifier.clickable(onClick = onControlToggleRequested).padding(16.dp),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- Text("Toggle controls")
- }
- }
- }
-
- screen.children.forEach { (name, child) ->
- item {
- Surface(
- Modifier.fillMaxWidth(),
- color = MaterialTheme.colorScheme.secondaryContainer,
- shape = CircleShape,
- ) {
- Column(
- Modifier.clickable { navController.navigate(child.identifier) }
- .padding(16.dp),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- Text(name)
- }
- }
- }
- }
- }
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/TypographyScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/TypographyScreen.kt
deleted file mode 100644
index 147025ed1d60..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/TypographyScreen.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.compose.gallery
-
-import androidx.compose.foundation.horizontalScroll
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Text
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.TextStyle
-import androidx.compose.ui.text.style.TextOverflow
-
-/** The screen that shows the Material text styles. */
-@Composable
-fun TypographyScreen() {
- val typography = MaterialTheme.typography
-
- Column(
- Modifier.fillMaxSize()
- .horizontalScroll(rememberScrollState())
- .verticalScroll(rememberScrollState()),
- ) {
- FontLine("displayLarge", typography.displayLarge)
- FontLine("displayMedium", typography.displayMedium)
- FontLine("displaySmall", typography.displaySmall)
- FontLine("headlineLarge", typography.headlineLarge)
- FontLine("headlineMedium", typography.headlineMedium)
- FontLine("headlineSmall", typography.headlineSmall)
- FontLine("titleLarge", typography.titleLarge)
- FontLine("titleMedium", typography.titleMedium)
- FontLine("titleSmall", typography.titleSmall)
- FontLine("bodyLarge", typography.bodyLarge)
- FontLine("bodyMedium", typography.bodyMedium)
- FontLine("bodySmall", typography.bodySmall)
- FontLine("labelLarge", typography.labelLarge)
- FontLine("labelMedium", typography.labelMedium)
- FontLine("labelSmall", typography.labelSmall)
- }
-}
-
-@Composable
-private fun FontLine(name: String, style: TextStyle) {
- Text(
- "$name (${style.fontSize}/${style.lineHeight}, W${style.fontWeight?.weight})",
- style = style,
- maxLines = 1,
- overflow = TextOverflow.Visible,
- )
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/people/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/people/Fakes.kt
deleted file mode 100644
index 0966c3233ad5..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/people/Fakes.kt
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.people
-
-import android.content.Context
-import android.graphics.Bitmap
-import android.graphics.Canvas
-import android.graphics.Color
-import android.graphics.Paint
-import android.graphics.drawable.Icon
-import androidx.core.graphics.drawable.toIcon
-import com.android.systemui.R
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.people.data.model.PeopleTileModel
-import com.android.systemui.people.ui.viewmodel.PeopleViewModel
-import com.android.systemui.people.widget.PeopleTileKey
-
-/** A [PeopleViewModel] that does not have any conversations. */
-fun emptyPeopleSpaceViewModel(@Application context: Context): PeopleViewModel {
- return fakePeopleSpaceViewModel(context, emptyList(), emptyList())
-}
-
-/** A [PeopleViewModel] that has a few conversations. */
-fun fewPeopleSpaceViewModel(@Application context: Context): PeopleViewModel {
- return fakePeopleSpaceViewModel(
- context,
- priorityTiles =
- listOf(
- fakeTile(context, id = "0", Color.RED, "Priority"),
- fakeTile(context, id = "1", Color.BLUE, "Priority NewStory", hasNewStory = true),
- ),
- recentTiles =
- listOf(
- fakeTile(context, id = "2", Color.GREEN, "Recent Important", isImportant = true),
- fakeTile(context, id = "3", Color.CYAN, "Recent DndBlocking", isDndBlocking = true),
- ),
- )
-}
-
-/** A [PeopleViewModel] that has a lot of conversations. */
-fun fullPeopleSpaceViewModel(@Application context: Context): PeopleViewModel {
- return fakePeopleSpaceViewModel(
- context,
- priorityTiles =
- listOf(
- fakeTile(context, id = "0", Color.RED, "Priority"),
- fakeTile(context, id = "1", Color.BLUE, "Priority NewStory", hasNewStory = true),
- fakeTile(context, id = "2", Color.GREEN, "Priority Important", isImportant = true),
- fakeTile(
- context,
- id = "3",
- Color.CYAN,
- "Priority DndBlocking",
- isDndBlocking = true,
- ),
- fakeTile(
- context,
- id = "4",
- Color.MAGENTA,
- "Priority NewStory Important",
- hasNewStory = true,
- isImportant = true,
- ),
- ),
- recentTiles =
- listOf(
- fakeTile(
- context,
- id = "5",
- Color.RED,
- "Recent NewStory DndBlocking",
- hasNewStory = true,
- isDndBlocking = true,
- ),
- fakeTile(
- context,
- id = "6",
- Color.BLUE,
- "Recent Important DndBlocking",
- isImportant = true,
- isDndBlocking = true,
- ),
- fakeTile(
- context,
- id = "7",
- Color.GREEN,
- "Recent NewStory Important DndBlocking",
- hasNewStory = true,
- isImportant = true,
- isDndBlocking = true,
- ),
- fakeTile(context, id = "8", Color.CYAN, "Recent"),
- fakeTile(context, id = "9", Color.MAGENTA, "Recent"),
- ),
- )
-}
-
-private fun fakePeopleSpaceViewModel(
- @Application context: Context,
- priorityTiles: List<PeopleTileModel>,
- recentTiles: List<PeopleTileModel>,
-): PeopleViewModel {
- return PeopleViewModel(
- context,
- FakePeopleTileRepository(priorityTiles, recentTiles),
- FakePeopleWidgetRepository(),
- )
-}
-
-private fun fakeTile(
- @Application context: Context,
- id: String,
- iconColor: Int,
- username: String,
- hasNewStory: Boolean = false,
- isImportant: Boolean = false,
- isDndBlocking: Boolean = false
-): PeopleTileModel {
- return PeopleTileModel(
- PeopleTileKey(id, /* userId= */ 0, /* packageName */ ""),
- username,
- fakeUserIcon(context, iconColor),
- hasNewStory,
- isImportant,
- isDndBlocking,
- )
-}
-
-private fun fakeUserIcon(@Application context: Context, color: Int): Icon {
- val size = context.resources.getDimensionPixelSize(R.dimen.avatar_size_for_medium)
- val bitmap =
- Bitmap.createBitmap(
- size,
- size,
- Bitmap.Config.ARGB_8888,
- )
- val canvas = Canvas(bitmap)
- val paint = Paint().apply { this.color = color }
- val radius = size / 2f
- canvas.drawCircle(/* cx= */ radius, /* cy= */ radius, /* radius= */ radius, paint)
- return bitmap.toIcon()
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
deleted file mode 100644
index 6588e22721fb..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.footer
-
-import android.content.Context
-import android.os.UserHandle
-import android.view.View
-import com.android.internal.util.UserIcons
-import com.android.systemui.R
-import com.android.systemui.animation.Expandable
-import com.android.systemui.classifier.FalsingManagerFake
-import com.android.systemui.common.shared.model.Icon
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.globalactions.GlobalActionsDialogLite
-import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel
-import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor
-import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig
-import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
-import com.android.systemui.util.mockito.mock
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.flowOf
-
-/** A list of fake [FooterActionsViewModel] to be used in screenshot tests and the gallery. */
-fun fakeFooterActionsViewModels(
- @Application context: Context,
-): List<FooterActionsViewModel> {
- return listOf(
- fakeFooterActionsViewModel(context),
- fakeFooterActionsViewModel(context, showPowerButton = false, isGuestUser = true),
- fakeFooterActionsViewModel(context, showUserSwitcher = false),
- fakeFooterActionsViewModel(context, showUserSwitcher = false, foregroundServices = 4),
- fakeFooterActionsViewModel(
- context,
- foregroundServices = 4,
- hasNewForegroundServices = true,
- userId = 1,
- ),
- fakeFooterActionsViewModel(
- context,
- securityText = "Security",
- foregroundServices = 4,
- showUserSwitcher = false,
- ),
- fakeFooterActionsViewModel(
- context,
- securityText = "Security (not clickable)",
- securityClickable = false,
- foregroundServices = 4,
- hasNewForegroundServices = true,
- userId = 2,
- ),
- )
-}
-
-private fun fakeFooterActionsViewModel(
- @Application context: Context,
- securityText: String? = null,
- securityClickable: Boolean = true,
- foregroundServices: Int = 0,
- hasNewForegroundServices: Boolean = false,
- showUserSwitcher: Boolean = true,
- showPowerButton: Boolean = true,
- userId: Int = UserHandle.USER_OWNER,
- isGuestUser: Boolean = false,
-): FooterActionsViewModel {
- val interactor =
- FakeFooterActionsInteractor(
- securityButtonConfig =
- flowOf(
- securityText?.let { text ->
- SecurityButtonConfig(
- icon =
- Icon.Resource(
- R.drawable.ic_info_outline,
- contentDescription = null,
- ),
- text = text,
- isClickable = securityClickable,
- )
- }
- ),
- foregroundServicesCount = flowOf(foregroundServices),
- hasNewForegroundServices = flowOf(hasNewForegroundServices),
- userSwitcherStatus =
- flowOf(
- if (showUserSwitcher) {
- UserSwitcherStatusModel.Enabled(
- currentUserName = "foo",
- currentUserImage =
- UserIcons.getDefaultUserIcon(
- context.resources,
- userId,
- /* light= */ false,
- ),
- isGuestUser = isGuestUser,
- )
- } else {
- UserSwitcherStatusModel.Disabled
- }
- ),
- deviceMonitoringDialogRequests = flowOf(),
- )
-
- return FooterActionsViewModel(
- context,
- interactor,
- FalsingManagerFake(),
- globalActionsDialogLite = mock(),
- showPowerButton = showPowerButton,
- )
-}
-
-private class FakeFooterActionsInteractor(
- override val securityButtonConfig: Flow<SecurityButtonConfig?> = flowOf(null),
- override val foregroundServicesCount: Flow<Int> = flowOf(0),
- override val hasNewForegroundServices: Flow<Boolean> = flowOf(false),
- override val userSwitcherStatus: Flow<UserSwitcherStatusModel> =
- flowOf(UserSwitcherStatusModel.Disabled),
- override val deviceMonitoringDialogRequests: Flow<Unit> = flowOf(),
- private val onShowDeviceMonitoringDialogFromView: (View) -> Unit = {},
- private val onShowDeviceMonitoringDialog: (Context) -> Unit = {},
- private val onShowForegroundServicesDialog: (View) -> Unit = {},
- private val onShowPowerMenuDialog: (GlobalActionsDialogLite, View) -> Unit = { _, _ -> },
- private val onShowSettings: (Expandable) -> Unit = {},
- private val onShowUserSwitcher: (View) -> Unit = {},
-) : FooterActionsInteractor {
- override fun showDeviceMonitoringDialog(view: View) {
- onShowDeviceMonitoringDialogFromView(view)
- }
-
- override fun showDeviceMonitoringDialog(quickSettingsContext: Context) {
- onShowDeviceMonitoringDialog(quickSettingsContext)
- }
-
- override fun showForegroundServicesDialog(view: View) {
- onShowForegroundServicesDialog(view)
- }
-
- override fun showPowerMenuDialog(globalActionsDialogLite: GlobalActionsDialogLite, view: View) {
- onShowPowerMenuDialog(globalActionsDialogLite, view)
- }
-
- override fun showSettings(expandable: Expandable) {
- onShowSettings(expandable)
- }
-
- override fun showUserSwitcher(view: View) {
- onShowUserSwitcher(view)
- }
-}
diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
deleted file mode 100644
index 02d76f404a32..000000000000
--- a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.user
-
-import android.content.Context
-import androidx.appcompat.content.res.AppCompatResources
-import com.android.systemui.common.shared.model.Text
-import com.android.systemui.compose.gallery.R
-import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.power.data.repository.FakePowerRepository
-import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.user.domain.interactor.UserInteractor
-import com.android.systemui.user.shared.model.UserActionModel
-import com.android.systemui.user.shared.model.UserModel
-import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
-import com.android.systemui.util.mockito.mock
-
-object Fakes {
- private val USER_TINT_COLORS =
- arrayOf(
- 0x000000,
- 0x0000ff,
- 0x00ff00,
- 0x00ffff,
- 0xff0000,
- 0xff00ff,
- 0xffff00,
- 0xffffff,
- )
-
- fun fakeUserSwitcherViewModel(
- context: Context,
- userCount: Int,
- ): UserSwitcherViewModel {
- return UserSwitcherViewModel.Factory(
- userInteractor =
- UserInteractor(
- repository =
- FakeUserRepository().apply {
- setUsers(
- (0 until userCount).map { index ->
- UserModel(
- id = index,
- name = Text.Loaded("user_$index"),
- image =
- checkNotNull(
- AppCompatResources.getDrawable(
- context,
- R.drawable.ic_avatar_guest_user
- )
- ),
- isSelected = index == 0,
- isSelectable = true,
- )
- }
- )
- setActions(
- UserActionModel.values().mapNotNull {
- if (it == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) {
- null
- } else {
- it
- }
- }
- )
- },
- controller = mock(),
- activityStarter = mock(),
- keyguardInteractor =
- KeyguardInteractor(
- repository =
- FakeKeyguardRepository().apply { setKeyguardShowing(false) },
- ),
- ),
- powerInteractor =
- PowerInteractor(
- repository = FakePowerRepository(),
- )
- )
- .create(UserSwitcherViewModel::class.java)
- }
-}
diff --git a/packages/SystemUI/compose/gallery/tests/AndroidManifest.xml b/packages/SystemUI/compose/gallery/tests/AndroidManifest.xml
deleted file mode 100644
index 5eeb3ad24e5a..000000000000
--- a/packages/SystemUI/compose/gallery/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT 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.systemui.compose.gallery.tests" >
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.systemui.compose.gallery.tests"
- android:label="Tests for SystemUIComposeGallery"/>
-
-</manifest> \ No newline at end of file
diff --git a/packages/SystemUI/compose/gallery/tests/src/com/android/systemui/compose/gallery/ScreenshotsTests.kt b/packages/SystemUI/compose/gallery/tests/src/com/android/systemui/compose/gallery/ScreenshotsTests.kt
deleted file mode 100644
index 66ecc8d4fde5..000000000000
--- a/packages/SystemUI/compose/gallery/tests/src/com/android/systemui/compose/gallery/ScreenshotsTests.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.compose.gallery
-
-import androidx.compose.ui.test.junit4.createComposeRule
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.systemui.compose.theme.SystemUITheme
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class ScreenshotsTests {
- @get:Rule val composeRule = createComposeRule()
-
- @Test
- fun exampleFeatureScreenshotTest() {
- // TODO(b/230832101): Wire this with the screenshot diff testing infra. We should reuse the
- // configuration of the features in the gallery app to populate the UIs.
- composeRule.setContent { SystemUITheme { ExampleFeatureScreen() } }
- }
-}
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index bfbccb85b13f..8307fbc4dca1 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -498,7 +498,6 @@
-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
-packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
--packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLogger.kt
-packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiActivityModel.kt
@@ -814,7 +813,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/panelstate/PanelExpansionStateManagerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
@@ -829,7 +828,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherAdapterTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputQuickSettingsDisablerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SafetyControllerTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/VariableDateViewControllerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/WalletControllerImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/window/StatusBarWindowStateControllerTest.kt
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
index 1aaf19e8793f..c50340cfd247 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/FalsingManager.java
@@ -147,10 +147,10 @@ public interface FalsingManager {
}
/**
- * Listener that is alerted when a double tap is required to confirm a single tap.
+ * Listener that is alerted when an additional tap is required to confirm a single tap.
**/
interface FalsingTapListener {
- void onDoubleTapRequired();
+ void onAdditionalTapRequired();
}
/** Passed to {@link FalsingManager#onProximityEvent}. */
diff --git a/packages/SystemUI/res-keyguard/drawable/kg_bouncer_secondary_button.xml b/packages/SystemUI/res-keyguard/drawable/kg_bouncer_secondary_button.xml
new file mode 100644
index 000000000000..732fc57dbded
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/kg_bouncer_secondary_button.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:insetTop="6dp"
+ android:insetBottom="6dp">
+ <ripple android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="rectangle">
+ <solid android:color="?android:attr/textColorSecondary"/>
+ <corners android:radius="24dp"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="rectangle">
+ <corners android:radius="24dp"/>
+ <solid android:color="@android:color/transparent"/>
+ <stroke android:color="?androidprv:attr/colorAccentTertiary"
+ android:width="1dp"
+ />
+ <padding android:left="16dp"
+ android:top="12dp"
+ android:right="16dp"
+ android:bottom="12dp"/>
+ </shape>
+ </item>
+ </ripple>
+</inset>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_off.xml
new file mode 100644
index 000000000000..a34712386d52
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_off.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="17"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M7.38 -0.28 C5.69,-2.81 1.5,-8.5 0.01,-7.32 C0.03,-6.22 -0.06,6.16 -0.06,7.78 C1.56,7.81 5.44,7.19 7.37,2.06 C7.37,1.21 7.35,1.69 7.35,1 C7.35,0.09 7.38,0.59 7.38,-0.28c "
+ android:valueTo="M6 -2.38 C1.81,-6.69 -0.5,-10 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 3,7.75 5.47,4.91 C6.33,3.91 7.21,2.42 7.24,1.13 C7.26,-0.05 6.87,-1.49 6,-2.38c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="33"
+ android:propertyName="pathData"
+ android:startOffset="17"
+ android:valueFrom="M6 -2.38 C1.81,-6.69 -0.5,-10 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 3,7.75 5.47,4.91 C6.33,3.91 7.21,2.42 7.24,1.13 C7.26,-0.05 6.87,-1.49 6,-2.38c "
+ android:valueTo="M4.94 -3.95 C-0.31,-9.06 0.52,-9.42 -5.75,-3.22 C-8.31,-0.69 -7.05,3 -6.94,3.22 C-3.63,9.31 2.63,9.5 5.94,4.59 C6.61,3.6 7.43,1.16 7.12,0 C6.72,-1.45 6.01,-2.9 4.94,-3.95c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="50"
+ android:valueFrom="M4.94 -3.95 C-0.31,-9.06 0.52,-9.42 -5.75,-3.22 C-8.31,-0.69 -7.05,3 -6.94,3.22 C-3.63,9.31 2.63,9.5 5.94,4.59 C6.61,3.6 7.43,1.16 7.12,0 C6.72,-1.45 6.01,-2.9 4.94,-3.95c "
+ android:valueTo="M3.07 -5.83 C-0.44,-10.25 -2.56,-6.87 -6.11,-2.6 C-7.03,-1.49 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.1,-4.1 3.07,-5.83c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="83"
+ android:propertyName="pathData"
+ android:startOffset="117"
+ android:valueFrom="M3.07 -5.83 C-0.44,-10.25 -2.56,-6.87 -6.11,-2.6 C-7.03,-1.49 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.1,-4.1 3.07,-5.83c "
+ android:valueTo="M0 -8.18 C-1.81,-7.06 -3.89,-5.21 -5.95,-2.81 C-7.56,-0.94 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.99 0,7.99 C0,6.34 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-6 0,-8.18c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="433"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M7.38 -0.28 C5.69,-2.81 1.5,-8.5 0.01,-7.32 C0.03,-6.22 -0.06,6.16 -0.06,7.78 C1.56,7.81 5.44,7.19 7.37,2.06 C7.37,1.21 7.35,1.69 7.35,1 C7.35,0.09 7.38,0.59 7.38,-0.28c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.19 C0,-7.19 2.93,-4.32 4.38,-2.87 C5.25,-2 5.88,-0.62 5.88,1.19 C5.88,4.5 2.8,6.97 0,7 C-2.8,7.03 -6,4.37 -6,1.13 C-6,-0.43 -5.38,-1.9 -4.25,-3.01 C-4.25,-3.01 0,-7.19 0,-7.19 M-5.65 -4.44 C-5.65,-4.44 -5.65,-4.44 -5.65,-4.44 C-7.1,-3.01 -8,-1.04 -8,1.13 C-8,5.48 -4.42,9 0,9 C4.42,9 8,5.48 8,1.13 C8,-1.04 7.1,-3.01 5.65,-4.44 C5.65,-4.44 5.65,-4.44 5.65,-4.44 C5.65,-4.44 0,-10 0,-10 C0,-10 -5.65,-4.44 -5.65,-4.44c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_on.xml
new file mode 100644
index 000000000000..c5609d8e465b
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_invert_colors_icon_on.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M0 -7.49 C-2.58,-7.49 -7.47,-2.59 -7.47,0.57 C-7.47,0.77 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c "
+ android:valueTo="M3.11 -6.83 C-1.37,-10.12 -6.04,-3.87 -7.22,0.09 C-7.26,0.29 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="100"
+ android:valueFrom="M3.11 -6.83 C-1.37,-10.12 -6.04,-3.87 -7.22,0.09 C-7.26,0.29 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueTo="M5 -3.95 C-0.25,-9.87 -0.47,-8.74 -5,-3.63 C-7.94,-0.31 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 5.06,-2.75 5,-3.95c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="167"
+ android:valueFrom="M5 -3.95 C-0.25,-9.87 -0.47,-8.74 -5,-3.63 C-7.94,-0.31 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 5.06,-2.75 5,-3.95c "
+ android:valueTo="M4.82 -3.81 C1,-7.87 0.75,-9.37 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.38,7.38 6.4,3.22 C6.77,2.46 7,1.69 6.74,0 C6.56,-1.16 5.85,-2.71 4.82,-3.81c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="233"
+ android:valueFrom="M4.82 -3.81 C1,-7.87 0.75,-9.37 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.38,7.38 6.4,3.22 C6.77,2.46 7,1.69 6.74,0 C6.56,-1.16 5.85,-2.71 4.82,-3.81c "
+ android:valueTo="M5.63 -2.97 C3.65,-4.93 0.75,-8.37 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 4.13,7.94 6.31,3.44 C6.68,2.66 7,1.37 6.87,0.06 C6.77,-0.84 6.13,-2.47 5.63,-2.97c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="433"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.49 C-2.58,-7.49 -7.47,-2.59 -7.47,0.57 C-7.47,0.77 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.19 C0,-7.19 2.93,-4.32 4.38,-2.87 C5.25,-2 5.88,-0.62 5.88,1.19 C5.88,4.5 2.8,6.97 0,7 C-2.8,7.03 -6,4.37 -6,1.13 C-6,-0.43 -5.38,-1.9 -4.25,-3.01 C-4.25,-3.01 0,-7.19 0,-7.19 M-5.65 -4.44 C-5.65,-4.44 -5.65,-4.44 -5.65,-4.44 C-7.1,-3.01 -8,-1.04 -8,1.13 C-8,5.48 -4.42,9 0,9 C4.42,9 8,5.48 8,1.13 C8,-1.04 7.1,-3.01 5.65,-4.44 C5.65,-4.44 5.65,-4.44 5.65,-4.44 C5.65,-4.44 0,-10 0,-10 C0,-10 -5.65,-4.44 -5.65,-4.44c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_off.xml b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_off.xml
new file mode 100644
index 000000000000..5c0a7c820887
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_off.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="20"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M7.38 -2.03 C6.38,-4.66 3.47,-7.32 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 5.22,6.17 7.37,2.06 C7.37,1.21 7.37,0.69 7.37,0 C7.37,-0.91 7.38,-1.16 7.38,-2.03c "
+ android:valueTo="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="40"
+ android:propertyName="pathData"
+ android:startOffset="20"
+ android:valueFrom="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+ android:valueTo="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="40"
+ android:propertyName="pathData"
+ android:startOffset="60"
+ android:valueFrom="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+ android:valueTo="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="100"
+ android:valueFrom="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueTo="M0 -7.49 C-2.58,-7.49 -7.45,-4.78 -7.45,-1.62 C-7.45,-1.42 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="217"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.08 C-2.69,-7.08 -7.03,-5.19 -7.03,0.03 C-7.03,5.25 -2.19,7.08 0,7.08 C3.03,7.08 7.08,3.91 7.08,0 C7.08,-3.91 3.69,-7.08 0,-7.08c M-8.33 0 C-8.33,-4.6 -4.6,-8.33 0,-8.33 C4.6,-8.33 8.33,-4.6 8.33,0 C8.33,4.6 4.6,8.33 0,8.33 C-4.6,8.33 -8.33,4.6 -8.33,0c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M7.38 -2.03 C6.38,-4.66 3.47,-7.32 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 5.22,6.17 7.37,2.06 C7.37,1.21 7.37,0.69 7.37,0 C7.37,-0.91 7.38,-1.16 7.38,-2.03c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_on.xml b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_on.xml
new file mode 100644
index 000000000000..a96b7480ec94
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/drawable/qs_light_dark_theme_icon_on.xml
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M0 -7.49 C-2.58,-7.49 -7.45,-4.78 -7.45,-1.62 C-7.45,-1.42 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c "
+ android:valueTo="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="100"
+ android:valueFrom="M3.11 -6.83 C-0.23,-9.49 -6.06,-6.24 -7.23,-2.29 C-7.28,-2.09 -7.23,1.85 -7.18,2.06 C-5.84,7.25 -0.33,9.23 3.14,6.75 C3.17,5.1 3.17,3.58 3.22,0 C3.25,-3.04 3.14,-5.1 3.11,-6.83c "
+ android:valueTo="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="pathData"
+ android:startOffset="167"
+ android:valueFrom="M7.06 -3.94 C3.5,-8.81 -3.31,-9.44 -7,-3.66 C-7.12,-3.47 -7.05,3 -6.94,3.22 C-3.63,9.31 2.94,9.19 7,3.59 C7.06,1.94 7,3.19 7.12,0 C7.19,-2 7.12,-2.75 7.06,-3.94c "
+ android:valueTo="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="100"
+ android:propertyName="pathData"
+ android:startOffset="233"
+ android:valueFrom="M7.25 -2.44 C5.46,-5.86 1.53,-8.3 -1.72,-7.1 C-1.73,-6.22 -1.73,5.88 -1.7,7.16 C0.25,8.45 4.43,6.91 7.28,2.47 C7.29,1.42 7.27,1.3 7.3,0 C7.32,-1.17 7.27,-1.49 7.25,-2.44c "
+ android:valueTo="M7.38 -2.03 C6.38,-4.66 3.47,-7.32 0.01,-7.32 C0.03,-6.22 -0.03,5.66 -0.03,7.28 C1.59,7.31 5.22,6.17 7.37,2.06 C7.37,1.21 7.37,0.69 7.37,0 C7.37,-0.91 7.38,-1.16 7.38,-2.03c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.08 C-2.69,-7.08 -7.03,-5.19 -7.03,0.03 C-7.03,5.25 -2.19,7.08 0,7.08 C3.03,7.08 7.08,3.91 7.08,0 C7.08,-3.91 3.69,-7.08 0,-7.08c M-8.33 0 C-8.33,-4.6 -4.6,-8.33 0,-8.33 C4.6,-8.33 8.33,-4.6 8.33,0 C8.33,4.6 4.6,8.33 0,8.33 C-4.6,8.33 -8.33,4.6 -8.33,0c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M0 -7.49 C-2.58,-7.49 -7.45,-4.78 -7.45,-1.62 C-7.45,-1.42 -7.37,0.88 -7.37,1.09 C-7.37,6.31 -2.19,7.55 0,7.55 C0,5.91 -0.01,3.91 -0.01,0 C-0.01,-3.91 0,-5.31 0,-7.49c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml b/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml
index db508c91e0de..67fd5b9a78bd 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_esim_area.xml
@@ -17,15 +17,17 @@
*/
-->
-<!-- This contains disable esim buttonas shared by sim_pin/sim_puk screens -->
-<com.android.keyguard.KeyguardEsimArea
- xmlns:android="http://schemas.android.com/apk/res/android"
+<!-- This contains disable eSim buttons shared by sim_pin/sim_puk screens -->
+<com.android.keyguard.KeyguardEsimArea xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/keyguard_disable_esim"
+ style="@style/Keyguard.TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:id="@+id/keyguard_disable_esim"
- android:visibility="gone"
+ android:background="@drawable/kg_bouncer_secondary_button"
+ android:drawablePadding="10dp"
+ android:drawableStart="@drawable/ic_no_sim"
+ android:drawableTint="?android:attr/textColorPrimary"
android:text="@string/disable_carrier_button_text"
- style="@style/Keyguard.TextView"
- android:textAppearance="?android:attr/textAppearanceMedium"
+ android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/kg_status_line_font_size"
- android:textAllCaps="@bool/kg_use_all_caps" />
+ android:visibility="gone" />
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
index f2fe520f340f..7db0fe908ec0 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_pin_view.xml
@@ -47,8 +47,7 @@
<include layout="@layout/keyguard_esim_area"
android:id="@+id/keyguard_esim_area"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/eca_overlap" />
+ android:layout_height="wrap_content" />
<RelativeLayout
android:id="@+id/row0"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
index a21ec29267fe..422bd4c12e8e 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_sim_puk_view.xml
@@ -51,8 +51,7 @@
<include layout="@layout/keyguard_esim_area"
android:id="@+id/keyguard_esim_area"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/eca_overlap" />
+ android:layout_height="wrap_content" />
<RelativeLayout
android:id="@+id/row0"
diff --git a/packages/SystemUI/res-keyguard/values-ro/strings.xml b/packages/SystemUI/res-keyguard/values-ro/strings.xml
index dcc9050c5a2d..cad7159489a9 100644
--- a/packages/SystemUI/res-keyguard/values-ro/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ro/strings.xml
@@ -20,9 +20,9 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"Introduceți codul PIN"</string>
- <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Introduceți modelul"</string>
- <string name="keyguard_enter_your_password" msgid="7225626204122735501">"Introduceți parola"</string>
+ <string name="keyguard_enter_your_pin" msgid="5429932527814874032">"Introdu codul PIN"</string>
+ <string name="keyguard_enter_your_pattern" msgid="351503370332324745">"Introdu modelul"</string>
+ <string name="keyguard_enter_your_password" msgid="7225626204122735501">"Introdu parola"</string>
<string name="keyguard_sim_error_message_short" msgid="633630844240494070">"Card nevalid"</string>
<string name="keyguard_charged" msgid="5478247181205188995">"Încărcată"</string>
<string name="keyguard_plugged_in_wireless" msgid="2537874724955057383">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă wireless"</string>
@@ -31,13 +31,13 @@
<string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă rapid"</string>
<string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Se încarcă lent"</string>
<string name="keyguard_plugged_in_charging_limited" msgid="1709413803451065875">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Încărcarea s-a întrerupt pentru a proteja bateria"</string>
- <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Apăsați pe Meniu pentru a debloca."</string>
+ <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Apasă pe Meniu pentru a debloca."</string>
<string name="keyguard_network_locked_message" msgid="407096292844868608">"Rețea blocată"</string>
<string name="keyguard_missing_sim_message_short" msgid="704159478161444907">"Niciun card SIM"</string>
- <string name="keyguard_missing_sim_instructions" msgid="1162120926141335918">"Introduceți un card SIM."</string>
- <string name="keyguard_missing_sim_instructions_long" msgid="2712623293749378570">"Cardul SIM lipsește sau nu poate fi citit. Introduceți un card SIM."</string>
+ <string name="keyguard_missing_sim_instructions" msgid="1162120926141335918">"Introdu un card SIM."</string>
+ <string name="keyguard_missing_sim_instructions_long" msgid="2712623293749378570">"Cardul SIM lipsește sau nu poate fi citit. Introdu un card SIM."</string>
<string name="keyguard_permanent_disabled_sim_message_short" msgid="5842745213110966962">"Card SIM inutilizabil."</string>
- <string name="keyguard_permanent_disabled_sim_instructions" msgid="2490584154727897806">"Cardul dvs. SIM este dezactivat definitiv.\n Contactați furnizorul de servicii wireless pentru a obține un alt card SIM."</string>
+ <string name="keyguard_permanent_disabled_sim_instructions" msgid="2490584154727897806">"Cardul SIM e dezactivat definitiv.\n Contactează furnizorul de servicii wireless pentru a obține un alt card SIM."</string>
<string name="keyguard_sim_locked_message" msgid="4343544458476911044">"Cardul SIM este blocat."</string>
<string name="keyguard_sim_puk_locked_message" msgid="6253830777745450550">"Cardul SIM este blocat cu codul PUK."</string>
<string name="keyguard_sim_unlock_progress_dialog_message" msgid="2394023844117630429">"Se deblochează cardul SIM…"</string>
@@ -45,35 +45,35 @@
<string name="keyguard_accessibility_password" msgid="3524161948484801450">"Parola dispozitivului"</string>
<string name="keyguard_accessibility_sim_pin_area" msgid="6272116591533888062">"Zona codului PIN pentru cardul SIM"</string>
<string name="keyguard_accessibility_sim_puk_area" msgid="5537294043180237374">"Zona codului PUK pentru cardul SIM"</string>
- <string name="keyboardview_keycode_delete" msgid="8489719929424895174">"Ștergeți"</string>
- <string name="disable_carrier_button_text" msgid="7153361131709275746">"Dezactivați cardul eSIM"</string>
+ <string name="keyboardview_keycode_delete" msgid="8489719929424895174">"Șterge"</string>
+ <string name="disable_carrier_button_text" msgid="7153361131709275746">"Dezactivează cardul eSIM"</string>
<string name="error_disable_esim_title" msgid="3802652622784813119">"Nu se poate dezactiva cardul eSIM"</string>
<string name="error_disable_esim_msg" msgid="2441188596467999327">"Cardul eSIM nu poate fi dezactivat din cauza unei erori."</string>
- <string name="keyboardview_keycode_enter" msgid="6727192265631761174">"Introduceți"</string>
+ <string name="keyboardview_keycode_enter" msgid="6727192265631761174">"Introdu"</string>
<string name="kg_wrong_pattern" msgid="5907301342430102842">"Model greșit"</string>
<string name="kg_wrong_password" msgid="4143127991071670512">"Parolă greșită"</string>
<string name="kg_wrong_pin" msgid="4160978845968732624">"Cod PIN greșit"</string>
- <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Reîncercați peste o secundă.}few{Reîncercați peste # secunde.}other{Reîncercați peste # de secunde.}}"</string>
- <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Introduceți codul PIN al cardului SIM."</string>
- <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Introduceți codul PIN al cardului SIM pentru „<xliff:g id="CARRIER">%1$s</xliff:g>”."</string>
- <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Dezactivați cardul eSIM pentru a folosi dispozitivul fără serviciu mobil."</string>
- <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"Cardul SIM este acum dezactivat. Pentru a continua, introduceți codul PUK. Pentru detalii, contactați operatorul."</string>
- <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"Cardul SIM „<xliff:g id="CARRIER">%1$s</xliff:g>\" este acum dezactivat. Pentru a continua, introduceți codul PUK. Pentru detalii, contactați operatorul."</string>
- <string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Introduceți codul PIN dorit"</string>
- <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Confirmați codul PIN dorit"</string>
+ <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Reîncearcă peste o secundă.}few{Reîncearcă peste # secunde.}other{Reîncearcă peste # de secunde.}}"</string>
+ <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Introdu codul PIN al cardului SIM."</string>
+ <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Introdu codul PIN al cardului SIM pentru „<xliff:g id="CARRIER">%1$s</xliff:g>”."</string>
+ <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Dezactivează cardul eSIM pentru a folosi dispozitivul fără serviciu mobil."</string>
+ <string name="kg_puk_enter_puk_hint" msgid="3005288372875367017">"Cardul SIM e acum dezactivat. Pentru a continua, introdu codul PUK. Pentru detalii, contactează operatorul."</string>
+ <string name="kg_puk_enter_puk_hint_multi" msgid="4876780689904862943">"Cardul SIM „<xliff:g id="CARRIER">%1$s</xliff:g>\" e acum dezactivat. Pentru a continua, introdu codul PUK. Pentru detalii, contactează operatorul."</string>
+ <string name="kg_puk_enter_pin_hint" msgid="6028432138916150399">"Introdu codul PIN dorit"</string>
+ <string name="kg_enter_confirm_pin_hint" msgid="4261064020391799132">"Confirmă codul PIN dorit"</string>
<string name="kg_sim_unlock_progress_dialog_message" msgid="4251352015304070326">"Se deblochează cardul SIM…"</string>
- <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Introduceți un cod PIN alcătuit din 4 până la 8 cifre."</string>
+ <string name="kg_invalid_sim_pin_hint" msgid="2762202646949552978">"Introdu un cod PIN alcătuit din 4 până la 8 cifre."</string>
<string name="kg_invalid_sim_puk_hint" msgid="5319756880543857694">"Codul PUK trebuie să aibă minimum 8 cifre."</string>
- <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Ați introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
- <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Ați introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
- <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncercați din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
- <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Codul PIN pentru cardul SIM este incorect. Contactați operatorul pentru a vă debloca dispozitivul."</string>
+ <string name="kg_too_many_failed_pin_attempts_dialog_message" msgid="544687656831558971">"Ai introdus incorect codul PIN de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori.\n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_too_many_failed_password_attempts_dialog_message" msgid="190984061975729494">"Ai introdus incorect parola de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_too_many_failed_pattern_attempts_dialog_message" msgid="4252405904570284368">"Ai desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. \n\nÎncearcă din nou peste <xliff:g id="NUMBER_1">%2$d</xliff:g> secunde."</string>
+ <string name="kg_password_wrong_pin_code_pukked" msgid="8047350661459040581">"Codul PIN pentru cardul SIM este incorect. Contactează operatorul pentru a debloca dispozitivul."</string>
<string name="kg_password_wrong_pin_code" msgid="5629415765976820357">"{count,plural, =1{Codul PIN pentru cardul SIM este incorect. V-a mai rămas # încercare, după care va trebui să contactați operatorul pentru a vă debloca dispozitivul.}few{Codul PIN pentru cardul SIM este incorect. V-au mai rămas # încercări. }other{Codul PIN pentru cardul SIM este incorect. V-au mai rămas # de încercări. }}"</string>
- <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Cardul SIM nu poate fi utilizat. Contactați operatorul."</string>
+ <string name="kg_password_wrong_puk_code_dead" msgid="3698285357028468617">"Cardul SIM nu poate fi utilizat. Contactează operatorul."</string>
<string name="kg_password_wrong_puk_code" msgid="6820515467645087827">"{count,plural, =1{Codul PUK pentru cardul SIM este incorect. V-a mai rămas # încercare până când cardul SIM va deveni inutilizabil definitiv.}few{Codul PUK pentru cardul SIM este incorect. V-au mai rămas # încercări până când cardul SIM va deveni inutilizabil definitiv.}other{Codul PUK pentru cardul SIM este incorect. V-au mai rămas # de încercări până când cardul SIM va deveni inutilizabil definitiv.}}"</string>
<string name="kg_password_pin_failed" msgid="5136259126330604009">"Deblocarea cu ajutorul codului PIN pentru cardul SIM nu a reușit!"</string>
<string name="kg_password_puk_failed" msgid="6778867411556937118">"Deblocarea cu ajutorul codului PUK pentru cardul SIM nu a reușit!"</string>
- <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Comutați metoda de introducere"</string>
+ <string name="accessibility_ime_switch_button" msgid="9082358310194861329">"Schimbă metoda de introducere"</string>
<string name="airplane_mode" msgid="2528005343938497866">"Mod Avion"</string>
<string name="kg_prompt_reason_restart_pattern" msgid="4720554342633852066">"Modelul este necesar după repornirea dispozitivului"</string>
<string name="kg_prompt_reason_restart_pin" msgid="1587671566498057656">"Codul PIN este necesar după repornirea dispozitivului"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
index e80cfafdd71a..a1068c65bae2 100644
--- a/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values-sw600dp/dimens.xml
@@ -31,8 +31,4 @@
<!-- Overload default clock widget parameters -->
<dimen name="widget_big_font_size">100dp</dimen>
<dimen name="widget_label_font_size">18sp</dimen>
-
- <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
- Should be 0 on devices with plenty of room (e.g. tablets) -->
- <dimen name="eca_overlap">0dip</dimen>
</resources>
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 32871f0abb4f..46f6ab2399d1 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -44,10 +44,6 @@
<dimen name="keyguard_eca_top_margin">18dp</dimen>
<dimen name="keyguard_eca_bottom_margin">12dp</dimen>
- <!-- EmergencyCarrierArea overlap - amount to overlap the emergency button and carrier text.
- Should be 0 on devices with plenty of room (e.g. tablets) -->
- <dimen name="eca_overlap">-10dip</dimen>
-
<!-- Slice header -->
<dimen name="widget_title_font_size">20dp</dimen>
<dimen name="widget_title_line_height">24dp</dimen>
@@ -84,6 +80,10 @@
<!-- The translation for disappearing security views after having solved them. -->
<dimen name="disappear_y_translation">-32dp</dimen>
+ <!-- Dimens for animation for the Bouncer PIN view -->
+ <dimen name="pin_view_trans_y_entry">120dp</dimen>
+ <dimen name="pin_view_trans_y_entry_offset">10dp</dimen>
+
<!-- Spacing around each button used for PIN view -->
<dimen name="num_pad_key_width">72dp</dimen>
<dimen name="num_pad_entry_row_margin_bottom">12dp</dimen>
diff --git a/packages/SystemUI/res-product/values-ro/strings.xml b/packages/SystemUI/res-product/values-ro/strings.xml
index 54dc73ae727c..807ebfe03bef 100644
--- a/packages/SystemUI/res-product/values-ro/strings.xml
+++ b/packages/SystemUI/res-product/values-ro/strings.xml
@@ -21,28 +21,28 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="dock_alignment_slow_charging" product="default" msgid="6997633396534416792">"Repoziționați telefonul pentru încărcare mai rapidă"</string>
<string name="dock_alignment_not_charging" product="default" msgid="3980752926226749808">"Repoziționați telefonul pentru încărcarea wireless"</string>
- <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Dispozitivul Android TV se va opri în curând. Apăsați un buton pentru a-l menține pornit."</string>
- <string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"Dispozitivul se va opri în curând. Apăsați pentru a-l menține pornit."</string>
+ <string name="inattentive_sleep_warning_message" product="tv" msgid="6844464574089665063">"Dispozitivul Android TV se va opri în curând. Apasă un buton pentru a-l menține pornit."</string>
+ <string name="inattentive_sleep_warning_message" product="default" msgid="5693904520452332224">"Dispozitivul se va opri în curând. Apasă pentru a-l menține pornit."</string>
<string name="keyguard_missing_sim_message" product="tablet" msgid="5018086454277963787">"Nu există card SIM în tabletă."</string>
<string name="keyguard_missing_sim_message" product="default" msgid="7053347843877341391">"Nu există card SIM în telefon."</string>
<string name="kg_invalid_confirm_pin_hint" product="default" msgid="6278551068943958651">"Codurile PIN nu coincid"</string>
<string name="kg_failed_attempts_almost_at_wipe" product="tablet" msgid="302165994845009232">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, această tabletă va fi resetată, iar toate datele acesteia vor fi șterse."</string>
- <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest telefon va fi resetat, iar toate datele acestuia vor fi șterse."</string>
+ <string name="kg_failed_attempts_almost_at_wipe" product="default" msgid="2594813176164266847">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest telefon va fi resetat, iar toate datele acestuia vor fi șterse."</string>
<string name="kg_failed_attempts_now_wiping" product="tablet" msgid="8710104080409538587">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Această tabletă va fi resetată, iar toate datele acesteia vor fi șterse."</string>
- <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acest telefon va fi resetat, iar toate datele acestuia vor fi șterse."</string>
- <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string>
- <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string>
- <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string>
- <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
- <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Ați efectuat <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
- <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Ați efectuat <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
+ <string name="kg_failed_attempts_now_wiping" product="default" msgid="6381835450014881813">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acest telefon va fi resetat, iar toate datele acestuia vor fi șterse."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="tablet" msgid="7325071812832605911">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string>
+ <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string>
+ <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Acest utilizator va fi eliminat, iar toate datele utilizatorului vor fi șterse."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a tabletei. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
+ <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"Ai făcut <xliff:g id="NUMBER_0">%1$d</xliff:g> încercări incorecte de deblocare a telefonului. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a tabletei. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
+ <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"Ai făcut <xliff:g id="NUMBER">%d</xliff:g> încercări incorecte de deblocare a telefonului. Profilul de serviciu va fi eliminat, iar toate datele profilului vor fi șterse."</string>
<string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați tableta cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
<string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"Ați desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%1$d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%2$d</xliff:g> încercări nereușite, vi se va solicita să deblocați telefonul cu ajutorul unui cont de e-mail.\n\n Încercați din nou peste <xliff:g id="NUMBER_2">%3$d</xliff:g> secunde."</string>
- <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Deblocați telefonul pentru mai multe opțiuni"</string>
- <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Deblocați tableta pentru mai multe opțiuni"</string>
- <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Deblocați dispozitivul pentru mai multe opțiuni"</string>
+ <string name="global_action_lock_message" product="default" msgid="7092460751050168771">"Deblochează telefonul pentru mai multe opțiuni"</string>
+ <string name="global_action_lock_message" product="tablet" msgid="1024230056230539493">"Deblochează tableta pentru mai multe opțiuni"</string>
+ <string name="global_action_lock_message" product="device" msgid="3165224897120346096">"Deblochează dispozitivul pentru mai multe opțiuni"</string>
<string name="media_transfer_playing_this_device" product="default" msgid="5795784619523545556">"Se redă pe acest telefon"</string>
<string name="media_transfer_playing_this_device" product="tablet" msgid="222514408550408633">"Se redă pe această tabletă"</string>
</resources>
diff --git a/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml b/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml
index 1ab0d4d3bf12..92c952794f57 100644
--- a/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml
+++ b/packages/SystemUI/res/drawable/ic_magnification_menu_large.xml
@@ -14,12 +14,12 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:width="24dp"
- android:height="24dp">
- <path
- android:pathData="M3 21L21 21C22.1 21 23 20.1 23 19L23 5C23 3.9 22.1 3 21 3L3 3C1.9 3 1 3.9 1 5L1 19C1 20.1 1.9 21 3 21ZM3 5L21 5L21 19L3 19L3 5ZM4 15L16.75 15L16.75 6L4 6L4 15Z"
- android:fillType="evenOdd"
- android:fillColor="#000000" />
-</vector> \ No newline at end of file
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M9.7,29.6H33.75V13.7H9.7ZM7,40Q5.8,40 4.9,39.1Q4,38.2 4,37V11Q4,9.8 4.9,8.9Q5.8,8 7,8H41Q42.2,8 43.1,8.9Q44,9.8 44,11V37Q44,38.2 43.1,39.1Q42.2,40 41,40ZM7,37H41Q41,37 41,37Q41,37 41,37V11Q41,11 41,11Q41,11 41,11H7Q7,11 7,11Q7,11 7,11V37Q7,37 7,37Q7,37 7,37ZM7,37Q7,37 7,37Q7,37 7,37V11Q7,11 7,11Q7,11 7,11Q7,11 7,11Q7,11 7,11V37Q7,37 7,37Q7,37 7,37Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml b/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml
index 6fc89a69f36e..209681f29548 100644
--- a/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml
+++ b/packages/SystemUI/res/drawable/ic_magnification_menu_medium.xml
@@ -14,12 +14,12 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:width="24dp"
- android:height="24dp">
- <path
- android:pathData="M3 21L21 21C22.1 21 23 20.1 23 19L23 5C23 3.9 22.1 3 21 3L3 3C1.9 3 1 3.9 1 5L1 19C1 20.1 1.9 21 3 21ZM3 5L21 5L21 19L3 19L3 5ZM4 12.75L13.75 12.75L13.75 6L4 6L4 12.75Z"
- android:fillType="evenOdd"
- android:fillColor="#000000" />
-</vector> \ No newline at end of file
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M9.7,27.6H27.75V13.7H9.7ZM7,40Q5.8,40 4.9,39.1Q4,38.2 4,37V11Q4,9.8 4.9,8.9Q5.8,8 7,8H41Q42.2,8 43.1,8.9Q44,9.8 44,11V37Q44,38.2 43.1,39.1Q42.2,40 41,40ZM7,37H41Q41,37 41,37Q41,37 41,37V11Q41,11 41,11Q41,11 41,11H7Q7,11 7,11Q7,11 7,11V37Q7,37 7,37Q7,37 7,37ZM7,37Q7,37 7,37Q7,37 7,37V11Q7,11 7,11Q7,11 7,11Q7,11 7,11Q7,11 7,11V37Q7,37 7,37Q7,37 7,37Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml b/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml
index fd7357424f32..a3dc816f6755 100644
--- a/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml
+++ b/packages/SystemUI/res/drawable/ic_magnification_menu_small.xml
@@ -14,12 +14,12 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:width="24dp"
- android:height="24dp">
- <path
- android:pathData="M3 21L21 21C22.1 21 23 20.1 23 19L23 5C23 3.9 22.1 3 21 3L3 3C1.9 3 1 3.9 1 5L1 19C1 20.1 1.9 21 3 21ZM3 5L21 5L21 19L3 19L3 5ZM4 10.5L8.5 10.5L8.5 6L4 6L4 10.5Z"
- android:fillType="evenOdd"
- android:fillColor="#000000" />
-</vector> \ No newline at end of file
+ android:width="48dp"
+ android:height="48dp"
+ android:viewportWidth="48"
+ android:viewportHeight="48"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M9.7,21.6H17.75V13.7H9.7ZM7,40Q5.8,40 4.9,39.1Q4,38.2 4,37V11Q4,9.8 4.9,8.9Q5.8,8 7,8H41Q42.2,8 43.1,8.9Q44,9.8 44,11V37Q44,38.2 43.1,39.1Q42.2,40 41,40ZM7,37H41Q41,37 41,37Q41,37 41,37V11Q41,11 41,11Q41,11 41,11H7Q7,11 7,11Q7,11 7,11V37Q7,37 7,37Q7,37 7,37ZM7,37Q7,37 7,37Q7,37 7,37V11Q7,11 7,11Q7,11 7,11Q7,11 7,11Q7,11 7,11V37Q7,37 7,37Q7,37 7,37Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_move_magnification.xml b/packages/SystemUI/res/drawable/ic_move_magnification.xml
index 1bff559ff3df..dfedd288619c 100644
--- a/packages/SystemUI/res/drawable/ic_move_magnification.xml
+++ b/packages/SystemUI/res/drawable/ic_move_magnification.xml
@@ -1,43 +1,25 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
+ Copyright (C) 2020 The Android Open Source Project
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
- <item>
- <shape android:shape="rectangle">
- <solid
- android:color="@color/magnification_drag_handle_color" />
- <size
- android:height="@dimen/magnification_drag_view_size"
- android:width="@dimen/magnification_drag_view_size"/>
- <corners android:topLeftRadius="12dp"/>
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
- </shape>
- </item>
- <item
- android:gravity="center">
+ http://www.apache.org/licenses/LICENSE-2.0
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:width="24dp"
- android:height="24dp">
- <path
- android:pathData="M13.2217 21.7734C12.8857 22.1094 12.288 22.712 12 23C12 23 11.1143 22.1094 10.7783 21.7734L8.33494 19.3301L9.55662 18.1084L12 20.5518L14.4434 18.1084L15.665 19.3301L13.2217 21.7734ZM19.3301 15.665L18.1084 14.4433L20.5518 12L18.1084 9.5566L19.3301 8.33492L21.7735 10.7783C22.1094 11.1142 22.4241 11.4241 23 12C22.4241 12.5759 22.3963 12.5988 21.7735 13.2217L19.3301 15.665ZM14.4434 14.4433C13.7714 15.1153 12.957 15.4512 12 15.4512C11.043 15.4512 10.2285 15.1153 9.55662 14.4433C8.88469 13.7714 8.54873 12.957 8.54873 12C8.54873 11.043 8.88469 10.2285 9.55662 9.5566C10.2285 8.88468 11.043 8.54871 12 8.54871C12.957 8.54871 13.7714 8.88467 14.4434 9.5566C15.1153 10.2285 15.4512 11.043 15.4512 12C15.4512 12.957 15.1153 13.7714 14.4434 14.4433ZM4.66988 15.665L2.22651 13.2217C1.89055 12.8857 1.28791 12.288 1 12C1.28791 11.712 1.89055 11.1143 2.22651 10.7783L4.66988 8.33492L5.89157 9.5566L3.4482 12L5.89157 14.4433L4.66988 15.665ZM14.4434 5.89155L12 3.44818L9.55662 5.89155L8.33494 4.66987L10.7783 2.2265C11.1389 1.86592 11.2963 1.70369 12 1C12.5758 1.57585 12.8857 1.89053 13.2217 2.2265L15.665 4.66986L14.4434 5.89155Z"
- android:fillColor="#ffffff" />
- </vector>
- </item>
-</layer-list>
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M12,15Q10.75,15 9.875,14.125Q9,13.25 9,12Q9,10.75 9.875,9.875Q10.75,9 12,9Q13.25,9 14.125,9.875Q15,10.75 15,12Q15,13.25 14.125,14.125Q13.25,15 12,15ZM12,22 L7.75,17.75 9.15,16.35 12,19.15 14.85,16.35 16.25,17.75ZM6.25,16.25 L2,12 6.25,7.75 7.65,9.15 4.85,12 7.65,14.85ZM9.15,7.65 L7.75,6.25 12,2 16.25,6.25 14.85,7.65 12,4.85ZM17.75,16.25 L16.35,14.85 19.15,12 16.35,9.15 17.75,7.75 22,12Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_no_sim.xml b/packages/SystemUI/res/drawable/ic_no_sim.xml
new file mode 100644
index 000000000000..ccfb6ea642cc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_no_sim.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M20,17.175 L18,15.175V4Q18,4 18,4Q18,4 18,4H10.85L8.85,6L7.4,4.6L10,2H18Q18.825,2 19.413,2.587Q20,3.175 20,4ZM20.5,23.3 L6,8.8V20Q6,20 6,20Q6,20 6,20H18Q18,20 18,20Q18,20 18,20V17.975L20,19.975V20Q20,20.825 19.413,21.413Q18.825,22 18,22H6Q5.175,22 4.588,21.413Q4,20.825 4,20V8L4.6,7.4L0.7,3.5L2.125,2.1L21.9,21.875ZM13.525,10.675Q13.525,10.675 13.525,10.675Q13.525,10.675 13.525,10.675ZM11.65,14.475Q11.65,14.475 11.65,14.475Q11.65,14.475 11.65,14.475Q11.65,14.475 11.65,14.475Q11.65,14.475 11.65,14.475Z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/media_output_dialog_background.xml b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
new file mode 100644
index 000000000000..40bfd83038af
--- /dev/null
+++ b/packages/SystemUI/res/drawable/media_output_dialog_background.xml
@@ -0,0 +1,22 @@
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners
+ android:radius="28dp"/>
+ <solid android:color="@color/media_dialog_background" />
+</shape> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
index f239a8dd4291..e015ed3d0c09 100644
--- a/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
+++ b/packages/SystemUI/res/drawable/qs_airplane_icon_off.xml
@@ -15,11 +15,28 @@
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
<target android:name="time_group">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="183"
+ android:duration="333"
android:propertyName="translateX"
android:startOffset="0"
android:valueFrom="0"
@@ -38,13 +55,13 @@
<group
android:name="_R_G_L_0_G"
android:translateX="12"
- android:translateY="12">
+ android:translateY="11.969">
<path
android:name="_R_G_L_0_G_D_0_P_0"
android:fillAlpha="1"
- android:fillColor="#ffffff"
+ android:fillColor="#000000"
android:fillType="nonZero"
- android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c M1.5 1.5 C1.5,1.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5c " />
+ android:pathData=" M-3.5 8.5 C-3.5,8.5 -3.5,10 -3.5,10 C-3.5,10 0,9 0,9 C0,9 3.5,10 3.5,10 C3.5,10 3.5,8.5 3.5,8.5 C3.5,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.02 1.5,-8.02 C1.5,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.5,-8.44 -1.5,-8.02 C-1.5,-8.02 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -3.5,8.5 -3.5,8.5c " />
</group>
</group>
<group android:name="time_group" />
diff --git a/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
index d46fafa25816..a44a9b6fbf23 100644
--- a/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
+++ b/packages/SystemUI/res/drawable/qs_airplane_icon_on.xml
@@ -15,35 +15,118 @@
-->
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt">
- <target android:name="_R_G_L_1_G">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="667"
- android:pathData="M 12.125,34.75C 12.104,30.958 12.021,15.792 12,12"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
+ android:duration="250"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
</aapt:attr>
</target>
- <target android:name="_R_G_L_0_G">
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="517"
- android:pathData="M 12,12C 12.021,8.312 12.104,-6.436999999999999 12.125,-10.125"
- android:propertyName="translateXY"
- android:propertyXName="translateX"
- android:propertyYName="translateY"
- android:startOffset="0">
+ android:duration="383"
+ android:propertyName="scaleX"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1.1500000000000001"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="383"
+ android:propertyName="scaleY"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="1.1500000000000001"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.4,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleX"
+ android:startOffset="383"
+ android:valueFrom="1.1500000000000001"
+ android:valueTo="1.1500000000000001"
+ android:valueType="floatType">
<aapt:attr name="android:interpolator">
- <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.4,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="67"
+ android:propertyName="scaleY"
+ android:startOffset="383"
+ android:valueFrom="1.1500000000000001"
+ android:valueTo="1.1500000000000001"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.4,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="700"
+ android:propertyName="scaleX"
+ android:startOffset="450"
+ android:valueFrom="1.1500000000000001"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.4,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="700"
+ android:propertyName="scaleY"
+ android:startOffset="450"
+ android:valueFrom="1.1500000000000001"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.6,0 0.4,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="200"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M-3.5 8.5 C-3.5,8.5 -3.5,10 -3.5,10 C-3.5,10 0,9 0,9 C0,9 3.5,10 3.5,10 C3.5,10 3.5,8.5 3.5,8.5 C3.5,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.02 1.5,-8.02 C1.5,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.5,-8.44 -1.5,-8.02 C-1.5,-8.02 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -3.5,8.5 -3.5,8.5c "
+ android:valueTo="M-3.23 8.38 C-3.23,8.38 -3.23,9.4 -3.23,9.4 C-3.23,9.4 0,8.51 0,8.51 C0,8.51 3.23,9.4 3.23,9.4 C3.23,9.4 3.23,8.38 3.23,8.38 C3.23,8.38 1.26,6.51 1.26,6.51 C1.26,6.51 1.59,1.5 1.59,1.5 C1.59,1.5 10,3.66 10,3.66 C10,3.66 10,1.95 10,1.95 C10,1.95 1.59,-3.36 1.59,-3.36 C1.59,-3.36 1.77,-8.02 1.77,-8.02 C1.77,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.77,-8.44 -1.77,-8.02 C-1.77,-8.02 -1.59,-3.36 -1.59,-3.36 C-1.59,-3.36 -10,1.95 -10,1.95 C-10,1.95 -10,3.66 -10,3.66 C-10,3.66 -1.59,1.5 -1.59,1.5 C-1.59,1.5 -1.24,6.51 -1.24,6.51 C-1.24,6.51 -3.23,8.38 -3.23,8.38c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.667,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="383"
+ android:propertyName="pathData"
+ android:startOffset="200"
+ android:valueFrom="M-3.23 8.38 C-3.23,8.38 -3.23,9.4 -3.23,9.4 C-3.23,9.4 0,8.51 0,8.51 C0,8.51 3.23,9.4 3.23,9.4 C3.23,9.4 3.23,8.38 3.23,8.38 C3.23,8.38 1.26,6.51 1.26,6.51 C1.26,6.51 1.59,1.5 1.59,1.5 C1.59,1.5 10,3.66 10,3.66 C10,3.66 10,1.95 10,1.95 C10,1.95 1.59,-3.36 1.59,-3.36 C1.59,-3.36 1.77,-8.02 1.77,-8.02 C1.77,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.77,-8.44 -1.77,-8.02 C-1.77,-8.02 -1.59,-3.36 -1.59,-3.36 C-1.59,-3.36 -10,1.95 -10,1.95 C-10,1.95 -10,3.66 -10,3.66 C-10,3.66 -1.59,1.5 -1.59,1.5 C-1.59,1.5 -1.24,6.51 -1.24,6.51 C-1.24,6.51 -3.23,8.38 -3.23,8.38c "
+ android:valueTo="M-3.5 8.5 C-3.5,8.5 -3.5,10 -3.5,10 C-3.5,10 0,9 0,9 C0,9 3.5,10 3.5,10 C3.5,10 3.5,8.5 3.5,8.5 C3.5,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.02 1.5,-8.02 C1.5,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.5,-8.44 -1.5,-8.02 C-1.5,-8.02 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -3.5,8.5 -3.5,8.5c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.833,0.833 1.0,1.0" />
</aapt:attr>
</objectAnimator>
</set>
@@ -53,7 +136,7 @@
<aapt:attr name="android:animation">
<set android:ordering="together">
<objectAnimator
- android:duration="683"
+ android:duration="1167"
android:propertyName="translateX"
android:startOffset="0"
android:valueFrom="0"
@@ -70,26 +153,20 @@
android:viewportWidth="24">
<group android:name="_R_G">
<group
- android:name="_R_G_L_1_G"
- android:translateX="12.125"
- android:translateY="34.75">
- <path
- android:name="_R_G_L_1_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData=" M10 4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.5 1.5,-8.5 C1.5,-9.33 0.83,-10 0,-10 C-0.83,-10 -1.5,-9.33 -1.5,-8.5 C-1.5,-8.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4c " />
- </group>
- <group
android:name="_R_G_L_0_G"
android:translateX="12"
- android:translateY="12">
- <path
- android:name="_R_G_L_0_G_D_0_P_0"
- android:fillAlpha="1"
- android:fillColor="#ffffff"
- android:fillType="nonZero"
- android:pathData=" M-1.5 -3.02 C-1.5,-3.02 -1.5,-8.5 -1.5,-8.5 C-1.5,-9.33 -0.83,-10 0,-10 C0.83,-10 1.5,-9.33 1.5,-8.5 C1.5,-8.5 1.5,-3 1.5,-3 C1.5,-3 10,2 10,2 C10,2 10,4 10,4 C10,4 1.51,1.49 1.51,1.49 C1.51,1.49 -1.5,-3.02 -1.5,-3.02c M1.5 1.5 C1.5,1.5 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -4,8.5 -4,8.5 C-4,8.5 -4,10 -4,10 C-4,10 0,9 0,9 C0,9 4,10 4,10 C4,10 4,8.5 4,8.5 C4,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5c " />
+ android:translateY="11.969">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:scaleX="1"
+ android:scaleY="1">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M-3.5 8.5 C-3.5,8.5 -3.5,10 -3.5,10 C-3.5,10 0,9 0,9 C0,9 3.5,10 3.5,10 C3.5,10 3.5,8.5 3.5,8.5 C3.5,8.5 1.5,7 1.5,7 C1.5,7 1.5,1.5 1.5,1.5 C1.5,1.5 10,4 10,4 C10,4 10,2 10,2 C10,2 1.5,-3 1.5,-3 C1.5,-3 1.5,-8.02 1.5,-8.02 C1.5,-8.44 1.2,-9.52 0,-9.52 C-1.19,-9.52 -1.5,-8.44 -1.5,-8.02 C-1.5,-8.02 -1.5,-3 -1.5,-3 C-1.5,-3 -10,2 -10,2 C-10,2 -10,4 -10,4 C-10,4 -1.5,1.5 -1.5,1.5 C-1.5,1.5 -1.5,7 -1.5,7 C-1.5,7 -3.5,8.5 -3.5,8.5c " />
+ </group>
</group>
</group>
<group android:name="time_group" />
diff --git a/packages/SystemUI/res/drawable/qs_data_saver_icon_off.xml b/packages/SystemUI/res/drawable/qs_data_saver_icon_off.xml
new file mode 100644
index 000000000000..57777a670d33
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_data_saver_icon_off.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="133"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="150"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:pivotY="1"
+ android:rotation="180"
+ android:translateX="12"
+ android:translateY="10.984">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:rotation="1440"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M9.15 4.05 C9.15,4.05 6.55,2.55 6.55,2.55 C6.7,2.13 6.81,1.71 6.89,1.29 C6.96,0.86 7,0.43 7,0 C7,-1.77 6.43,-3.31 5.3,-4.62 C4.17,-5.94 2.73,-6.72 1,-6.95 C1,-6.95 1,-9.95 1,-9.95 C3.57,-9.7 5.71,-8.62 7.43,-6.72 C9.14,-4.82 10,-2.58 10,0 C10,0.7 9.94,1.39 9.81,2.08 C9.69,2.76 9.47,3.42 9.15,4.05c M0 10 C-1.38,10 -2.68,9.74 -3.9,9.21 C-5.12,8.69 -6.17,7.98 -7.07,7.08 C-7.97,6.18 -8.69,5.12 -9.21,3.9 C-9.74,2.68 -10,1.38 -10,0 C-10,-2.58 -9.14,-4.82 -7.42,-6.72 C-5.71,-8.62 -3.57,-9.7 -1,-9.95 C-1,-9.95 -1,-6.95 -1,-6.95 C-2.73,-6.72 -4.17,-5.94 -5.3,-4.62 C-6.43,-3.31 -7,-1.77 -7,0 C-7,1.95 -6.32,3.6 -4.96,4.96 C-3.6,6.32 -1.95,7 0,7 C1.07,7 2.08,6.78 3.04,6.33 C4,5.88 4.82,5.23 5.5,4.4 C5.5,4.4 8.1,5.9 8.1,5.9 C7.15,7.2 5.97,8.21 4.55,8.93 C3.13,9.64 1.62,10 0,10c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_data_saver_icon_on.xml b/packages/SystemUI/res/drawable/qs_data_saver_icon_on.xml
new file mode 100644
index 000000000000..b3764137793a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_data_saver_icon_on.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="333"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M1.02 2 C1.02,2 1,2 1,2 C1,2 1,1.98 1,1.98 C1,1.98 -1,1.98 -1,1.98 C-1,1.98 -1,2 -1,2 C-1,2 -1,2 -1,2 C-1,2 -1,0 -1,0 C-1,0 -1,0 -1,0 C-1,0 -1,0 -1,0 C-1,0 1,0 1,0 C1,0 1,0 1,0 C1,0 1.02,0 1.02,0 C1.02,0 1.02,2 1.02,2c "
+ android:valueTo="M3 2 C3,2 1,2 1,2 C1,2 1,4 1,4 C1,4 -1,4 -1,4 C-1,4 -1,2 -1,2 C-1,2 -3,2 -3,2 C-3,2 -3,0 -3,0 C-3,0 -1,0 -1,0 C-1,0 -1,-2 -1,-2 C-1,-2 1,-2 1,-2 C1,-2 1,0 1,0 C1,0 3,0 3,0 C3,0 3,2 3,2c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.333,0 0.2,0.2 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="500"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="613.191"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.086,0.422 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="rotation"
+ android:startOffset="500"
+ android:valueFrom="613.191"
+ android:valueTo="720"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.334,1.012 0.833,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="933"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="12"
+ android:translateY="10.75">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M1.02 2 C1.02,2 1,2 1,2 C1,2 1,1.98 1,1.98 C1,1.98 -1,1.98 -1,1.98 C-1,1.98 -1,2 -1,2 C-1,2 -1,2 -1,2 C-1,2 -1,0 -1,0 C-1,0 -1,0 -1,0 C-1,0 -1,0 -1,0 C-1,0 1,0 1,0 C1,0 1,0 1,0 C1,0 1.02,0 1.02,0 C1.02,0 1.02,2 1.02,2c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:rotation="0"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M9.15 4.05 C9.15,4.05 6.55,2.55 6.55,2.55 C6.7,2.13 6.81,1.71 6.89,1.29 C6.96,0.86 7,0.43 7,0 C7,-1.77 6.43,-3.31 5.3,-4.62 C4.17,-5.94 2.73,-6.72 1,-6.95 C1,-6.95 1,-9.95 1,-9.95 C3.57,-9.7 5.71,-8.62 7.43,-6.72 C9.14,-4.82 10,-2.58 10,0 C10,0.7 9.94,1.39 9.81,2.08 C9.69,2.76 9.47,3.42 9.15,4.05c M0 10 C-1.38,10 -2.68,9.74 -3.9,9.21 C-5.12,8.69 -6.17,7.98 -7.07,7.08 C-7.97,6.18 -8.69,5.12 -9.21,3.9 C-9.74,2.68 -10,1.38 -10,0 C-10,-2.58 -9.14,-4.82 -7.42,-6.72 C-5.71,-8.62 -3.57,-9.7 -1,-9.95 C-1,-9.95 -1,-6.95 -1,-6.95 C-2.73,-6.72 -4.17,-5.94 -5.3,-4.62 C-6.43,-3.31 -7,-1.77 -7,0 C-7,1.95 -6.32,3.6 -4.96,4.96 C-3.6,6.32 -1.95,7 0,7 C1.07,7 2.08,6.78 3.04,6.33 C4,5.88 4.82,5.23 5.5,4.4 C5.5,4.4 8.1,5.9 8.1,5.9 C7.15,7.2 5.97,8.21 4.55,8.93 C3.13,9.64 1.62,10 0,10c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_extra_dim_icon_off.xml b/packages/SystemUI/res/drawable/qs_extra_dim_icon_off.xml
new file mode 100644
index 000000000000..b0f85e7714c6
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_extra_dim_icon_off.xml
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M93 39.5 C93,39.55 93,41.52 93,44 C93,46.49 93,48.58 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c M94 37 C94,37 92,37 92,37 C92,37 92,35.44 92,35.44 C92,35.44 94,35.44 94,35.44 C94,35.44 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 84.31,45 84.31,45 C84.31,45 84.31,43 84.31,43 C84.31,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,52.69 94,52.69 C94,52.69 92,52.69 92,52.69 C92,52.69 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 101.75,43 101.75,43 C101.75,43 101.75,45 101.75,45 C101.75,45 100,45 100,45c M86.07 38.57 C86.07,38.57 87.48,37.16 87.48,37.16 C87.48,37.16 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 86.07,38.57 86.07,38.57c M87.57 50.87 C87.57,50.87 86.16,49.46 86.16,49.46 C86.16,49.46 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 87.57,50.87 87.57,50.87c M99.9 49.43 C99.9,49.43 98.49,50.84 98.49,50.84 C98.49,50.84 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 99.9,49.43 99.9,49.43c M98.52 37.13 C98.52,37.13 99.93,38.54 99.93,38.54 C99.93,38.54 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 98.52,37.13 98.52,37.13c "
+ android:valueTo="M93 39.5 C95.49,39.5 97.5,41.52 97.5,44 C97.5,46.49 95.49,48.5 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c M94 37 C94,37 92,37 92,37 C92,37 92,33 92,33 C92,33 94,33 94,33 C94,33 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 82,45 82,45 C82,45 82,43 82,43 C82,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,55 94,55 C94,55 92,55 92,55 C92,55 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 104,43 104,43 C104,43 104,45 104,45 C104,45 100,45 100,45c M85.22 37.64 C85.22,37.64 86.64,36.22 86.64,36.22 C86.64,36.22 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 85.22,37.64 85.22,37.64c M86.64 51.78 C86.64,51.78 85.22,50.36 85.22,50.36 C85.22,50.36 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 86.64,51.78 86.64,51.78c M100.78 50.36 C100.78,50.36 99.36,51.78 99.36,51.78 C99.36,51.78 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 100.78,50.36 100.78,50.36c M99.36 36.22 C99.36,36.22 100.78,37.64 100.78,37.64 C100.78,37.64 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 99.36,36.22 99.36,36.22c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M94 37 C94,37 92,37 92,37 C92,37 92,35.44 92,35.44 C92,35.44 94,35.44 94,35.44 C94,35.44 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 84.31,45 84.31,45 C84.31,45 84.31,43 84.31,43 C84.31,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,52.69 94,52.69 C94,52.69 92,52.69 92,52.69 C92,52.69 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 101.75,43 101.75,43 C101.75,43 101.75,45 101.75,45 C101.75,45 100,45 100,45c "
+ android:valueTo="M94 37 C94,37 92,37 92,37 C92,37 92,33 92,33 C92,33 94,33 94,33 C94,33 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 82,45 82,45 C82,45 82,43 82,43 C82,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,55 94,55 C94,55 92,55 92,55 C92,55 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 104,43 104,43 C104,43 104,45 104,45 C104,45 100,45 100,45c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#ffffff"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M86.07 38.57 C86.07,38.57 87.48,37.16 87.48,37.16 C87.48,37.16 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 86.07,38.57 86.07,38.57c M87.57 50.87 C87.57,50.87 86.16,49.46 86.16,49.46 C86.16,49.46 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 87.57,50.87 87.57,50.87c M99.9 49.43 C99.9,49.43 98.49,50.84 98.49,50.84 C98.49,50.84 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 99.9,49.43 99.9,49.43c M98.52 37.13 C98.52,37.13 99.93,38.54 99.93,38.54 C99.93,38.54 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 98.52,37.13 98.52,37.13c "
+ android:valueTo="M85.22 37.64 C85.22,37.64 86.64,36.22 86.64,36.22 C86.64,36.22 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 85.22,37.64 85.22,37.64c M86.64 51.78 C86.64,51.78 85.22,50.36 85.22,50.36 C85.22,50.36 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 86.64,51.78 86.64,51.78c M100.78 50.36 C100.78,50.36 99.36,51.78 99.36,51.78 C99.36,51.78 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 100.78,50.36 100.78,50.36c M99.36 36.22 C99.36,36.22 100.78,37.64 100.78,37.64 C100.78,37.64 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 99.36,36.22 99.36,36.22c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="517"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="-81"
+ android:translateY="-32">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:pathData=" M93 40 C95.21,40 97,41.79 97,44 C97,46.21 95.21,48 93,48 C90.79,48 89,46.21 89,44 C89,41.79 90.79,40 93,40c "
+ android:strokeAlpha="1"
+ android:strokeColor="#000000"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-81"
+ android:translateY="-32">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M93 39.5 C93,39.55 93,41.52 93,44 C93,46.49 93,48.58 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M94 37 C94,37 92,37 92,37 C92,37 92,35.44 92,35.44 C92,35.44 94,35.44 94,35.44 C94,35.44 94,37 94,37c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M86.07 38.57 C86.07,38.57 87.48,37.16 87.48,37.16 C87.48,37.16 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 86.07,38.57 86.07,38.57c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_extra_dim_icon_on.xml b/packages/SystemUI/res/drawable/qs_extra_dim_icon_on.xml
new file mode 100644
index 000000000000..5546b6f39813
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_extra_dim_icon_on.xml
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="strokeColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M93 39.5 C95.49,39.5 97.5,41.52 97.5,44 C97.5,46.49 95.49,48.5 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c M94 37 C94,37 92,37 92,37 C92,37 92,33 92,33 C92,33 94,33 94,33 C94,33 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 82,45 82,45 C82,45 82,43 82,43 C82,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,55 94,55 C94,55 92,55 92,55 C92,55 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 104,43 104,43 C104,43 104,45 104,45 C104,45 100,45 100,45c M85.22 37.64 C85.22,37.64 86.64,36.22 86.64,36.22 C86.64,36.22 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 85.22,37.64 85.22,37.64c M86.64 51.78 C86.64,51.78 85.22,50.36 85.22,50.36 C85.22,50.36 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 86.64,51.78 86.64,51.78c M100.78 50.36 C100.78,50.36 99.36,51.78 99.36,51.78 C99.36,51.78 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 100.78,50.36 100.78,50.36c M99.36 36.22 C99.36,36.22 100.78,37.64 100.78,37.64 C100.78,37.64 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 99.36,36.22 99.36,36.22c "
+ android:valueTo="M93 39.5 C93,39.55 93,41.52 93,44 C93,46.49 93,48.58 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c M94 37 C94,37 92,37 92,37 C92,37 92,35.44 92,35.44 C92,35.44 94,35.44 94,35.44 C94,35.44 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 84.31,45 84.31,45 C84.31,45 84.31,43 84.31,43 C84.31,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,52.69 94,52.69 C94,52.69 92,52.69 92,52.69 C92,52.69 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 101.75,43 101.75,43 C101.75,43 101.75,45 101.75,45 C101.75,45 100,45 100,45c M86.07 38.57 C86.07,38.57 87.48,37.16 87.48,37.16 C87.48,37.16 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 86.07,38.57 86.07,38.57c M87.57 50.87 C87.57,50.87 86.16,49.46 86.16,49.46 C86.16,49.46 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 87.57,50.87 87.57,50.87c M99.9 49.43 C99.9,49.43 98.49,50.84 98.49,50.84 C98.49,50.84 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 99.9,49.43 99.9,49.43c M98.52 37.13 C98.52,37.13 99.93,38.54 99.93,38.54 C99.93,38.54 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 98.52,37.13 98.52,37.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M94 37 C94,37 92,37 92,37 C92,37 92,33 92,33 C92,33 94,33 94,33 C94,33 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 82,45 82,45 C82,45 82,43 82,43 C82,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,55 94,55 C94,55 92,55 92,55 C92,55 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 104,43 104,43 C104,43 104,45 104,45 C104,45 100,45 100,45c "
+ android:valueTo="M94 37 C94,37 92,37 92,37 C92,37 92,35.44 92,35.44 C92,35.44 94,35.44 94,35.44 C94,35.44 94,37 94,37c M86 43 C86,43 86,45 86,45 C86,45 84.31,45 84.31,45 C84.31,45 84.31,43 84.31,43 C84.31,43 86,43 86,43c M92 51 C92,51 94,51 94,51 C94,51 94,52.69 94,52.69 C94,52.69 92,52.69 92,52.69 C92,52.69 92,51 92,51c M100 45 C100,45 100,43 100,43 C100,43 101.75,43 101.75,43 C101.75,43 101.75,45 101.75,45 C101.75,45 100,45 100,45c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#ffffff"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="350"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M85.22 37.64 C85.22,37.64 86.64,36.22 86.64,36.22 C86.64,36.22 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 85.22,37.64 85.22,37.64c M86.64 51.78 C86.64,51.78 85.22,50.36 85.22,50.36 C85.22,50.36 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 86.64,51.78 86.64,51.78c M100.78 50.36 C100.78,50.36 99.36,51.78 99.36,51.78 C99.36,51.78 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 100.78,50.36 100.78,50.36c M99.36 36.22 C99.36,36.22 100.78,37.64 100.78,37.64 C100.78,37.64 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 99.36,36.22 99.36,36.22c "
+ android:valueTo="M86.07 38.57 C86.07,38.57 87.48,37.16 87.48,37.16 C87.48,37.16 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 86.07,38.57 86.07,38.57c M87.57 50.87 C87.57,50.87 86.16,49.46 86.16,49.46 C86.16,49.46 87.34,48.24 87.34,48.24 C87.34,48.24 88.76,49.66 88.76,49.66 C88.76,49.66 87.57,50.87 87.57,50.87c M99.9 49.43 C99.9,49.43 98.49,50.84 98.49,50.84 C98.49,50.84 97.24,49.66 97.24,49.66 C97.24,49.66 98.66,48.24 98.66,48.24 C98.66,48.24 99.9,49.43 99.9,49.43c M98.52 37.13 C98.52,37.13 99.93,38.54 99.93,38.54 C99.93,38.54 98.66,39.76 98.66,39.76 C98.66,39.76 97.24,38.34 97.24,38.34 C97.24,38.34 98.52,37.13 98.52,37.13c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="517"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:translateX="-81"
+ android:translateY="-32">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:pathData=" M93 40 C95.21,40 97,41.79 97,44 C97,46.21 95.21,48 93,48 C90.79,48 89,46.21 89,44 C89,41.79 90.79,40 93,40c "
+ android:strokeAlpha="1"
+ android:strokeColor="#ffffff"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-81"
+ android:translateY="-32">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M93 39.5 C95.49,39.5 97.5,41.52 97.5,44 C97.5,46.49 95.49,48.5 93,48.5 C90.52,48.5 88.5,46.49 88.5,44 C88.5,41.52 90.52,39.5 93,39.5c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M94 37 C94,37 92,37 92,37 C92,37 92,33 92,33 C92,33 94,33 94,33 C94,33 94,37 94,37c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M85.22 37.64 C85.22,37.64 86.64,36.22 86.64,36.22 C86.64,36.22 88.76,38.34 88.76,38.34 C88.76,38.34 87.34,39.76 87.34,39.76 C87.34,39.76 85.22,37.64 85.22,37.64c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml b/packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml
new file mode 100644
index 000000000000..157e67a6e8e2
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_flashlight_icon_off.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M0 2.02 C0.42,2.02 0.77,1.88 1.05,1.59 C1.35,1.29 1.5,0.94 1.5,0.52 C1.5,0.1 1.35,-0.25 1.05,-0.53 C0.77,-0.83 0.42,-0.98 0,-0.98 C-0.42,-0.98 -0.78,-0.83 -1.08,-0.53 C-1.36,-0.25 -1.5,0.1 -1.5,0.52 C-1.5,0.94 -1.36,1.29 -1.08,1.59 C-0.78,1.88 -0.42,2.02 0,2.02c M0 8.62 C-0.42,8.62 -2.78,8.82 -3.07,8.54 C-3.36,8.24 -3.37,-1.87 -3.37,-2.28 C-4.25,-2.69 -4.29,-5.2 -4.01,-5.48 C-3.71,-5.78 -0.42,-5.75 0,-5.75 C0.42,-5.75 4.39,-5.78 4.36,-5.36 C4.22,-2.97 4.47,-3.03 3.34,-1.78 C3.07,-1.48 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+ android:valueTo="M0 3 C0.42,3 0.77,2.86 1.05,2.58 C1.35,2.28 1.5,1.92 1.5,1.5 C1.5,1.08 1.35,0.73 1.05,0.45 C0.77,0.15 0.42,0 0,0 C-0.42,0 -0.78,0.15 -1.08,0.45 C-1.36,0.73 -1.5,1.08 -1.5,1.5 C-1.5,1.92 -1.36,2.28 -1.08,2.58 C-0.78,2.86 -0.42,3 0,3c M0 8.62 C-0.42,8.62 -2.24,8.83 -2.54,8.55 C-2.83,8.25 -2.86,9.1 -2.86,8.69 C-2.86,8.27 -3.02,8.85 -2.74,8.57 C-2.44,8.26 -0.43,8.31 -0.02,8.31 C0.4,8.31 2.4,8.17 2.68,8.47 C2.98,8.75 2.93,8.33 2.93,8.75 C2.93,9.17 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1000"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:pivotY="24"
+ android:scaleX="0.33332999999999996"
+ android:scaleY="0.33332999999999996"
+ android:translateX="12"
+ android:translateY="-4">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#1f1f1f"
+ android:fillType="nonZero"
+ android:pathData=" M-12 -15 C-12,-15 12,-15 12,-15 C12,-15 12,-13.8 12,-13.8 C12,-13.8 6,-4.8 6,-4.8 C6,-4.8 6,24 6,24 C6,24 -6,24 -6,24 C-6,24 -6,-4.8 -6,-4.8 C-6,-4.8 -12,-13.8 -12,-13.8 C-12,-13.8 -12,-15 -12,-15c M12 -21 C12,-21 -12,-21 -12,-21 C-12,-21 -12,-24 -12,-24 C-12,-24 12,-24 12,-24 C12,-24 12,-21 12,-21c M-12 -3 C-12,-3 -12,30 -12,30 C-12,30 12,30 12,30 C12,30 12,-3 12,-3 C12,-3 18,-12 18,-12 C18,-12 18,-30 18,-30 C18,-30 -18,-30 -18,-30 C-18,-30 -18,-12 -18,-12 C-18,-12 -12,-3 -12,-3c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#1f1f1f"
+ android:fillType="nonZero"
+ android:pathData=" M0 2.02 C0.42,2.02 0.77,1.88 1.05,1.59 C1.35,1.29 1.5,0.94 1.5,0.52 C1.5,0.1 1.35,-0.25 1.05,-0.53 C0.77,-0.83 0.42,-0.98 0,-0.98 C-0.42,-0.98 -0.78,-0.83 -1.08,-0.53 C-1.36,-0.25 -1.5,0.1 -1.5,0.52 C-1.5,0.94 -1.36,1.29 -1.08,1.59 C-0.78,1.88 -0.42,2.02 0,2.02c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml b/packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml
new file mode 100644
index 000000000000..22f5e00dad69
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_flashlight_icon_on.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M0 3 C0.42,3 0.77,2.86 1.05,2.58 C1.35,2.28 1.5,1.92 1.5,1.5 C1.5,1.08 1.35,0.73 1.05,0.45 C0.77,0.15 0.42,0 0,0 C-0.42,0 -0.78,0.15 -1.08,0.45 C-1.36,0.73 -1.5,1.08 -1.5,1.5 C-1.5,1.92 -1.36,2.28 -1.08,2.58 C-0.78,2.86 -0.42,3 0,3c M0 8.62 C-0.42,8.62 -2.24,8.83 -2.54,8.55 C-2.83,8.25 -2.86,9.1 -2.86,8.69 C-2.86,8.27 -3.02,8.85 -2.74,8.57 C-2.44,8.26 -0.43,8.31 -0.02,8.31 C0.4,8.31 2.4,8.17 2.68,8.47 C2.98,8.75 2.93,8.33 2.93,8.75 C2.93,9.17 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+ android:valueTo="M0 2.02 C0.42,2.02 0.77,1.88 1.05,1.59 C1.35,1.29 1.5,0.94 1.5,0.52 C1.5,0.1 1.35,-0.25 1.05,-0.53 C0.77,-0.83 0.42,-0.98 0,-0.98 C-0.42,-0.98 -0.78,-0.83 -1.08,-0.53 C-1.36,-0.25 -1.5,0.1 -1.5,0.52 C-1.5,0.94 -1.36,1.29 -1.08,1.59 C-0.78,1.88 -0.42,2.02 0,2.02c M0 8.62 C-0.42,8.62 -2.78,8.82 -3.07,8.54 C-3.36,8.24 -3.37,-1.87 -3.37,-2.28 C-4.25,-2.69 -4.29,-5.2 -4.01,-5.48 C-3.71,-5.78 -0.42,-5.75 0,-5.75 C0.42,-5.75 4.39,-5.78 4.36,-5.36 C4.22,-2.97 4.47,-3.03 3.34,-1.78 C3.07,-1.48 3.32,8.21 3.02,8.51 C2.74,8.79 0.42,8.62 0,8.62c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.4,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1000"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:pivotY="24"
+ android:scaleX="0.33332999999999996"
+ android:scaleY="0.33332999999999996"
+ android:translateX="12"
+ android:translateY="-4">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#1f1f1f"
+ android:fillType="nonZero"
+ android:pathData=" M-12 -15 C-12,-15 12,-15 12,-15 C12,-15 12,-13.8 12,-13.8 C12,-13.8 6,-4.8 6,-4.8 C6,-4.8 6,24 6,24 C6,24 -6,24 -6,24 C-6,24 -6,-4.8 -6,-4.8 C-6,-4.8 -12,-13.8 -12,-13.8 C-12,-13.8 -12,-15 -12,-15c M12 -21 C12,-21 -12,-21 -12,-21 C-12,-21 -12,-24 -12,-24 C-12,-24 12,-24 12,-24 C12,-24 12,-21 12,-21c M-12 -3 C-12,-3 -12,30 -12,30 C-12,30 12,30 12,30 C12,30 12,-3 12,-3 C12,-3 18,-12 18,-12 C18,-12 18,-30 18,-30 C18,-30 -18,-30 -18,-30 C-18,-30 -18,-12 -18,-12 C-18,-12 -12,-3 -12,-3c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#1f1f1f"
+ android:fillType="nonZero"
+ android:pathData=" M0 3 C0.42,3 0.77,2.86 1.05,2.58 C1.35,2.28 1.5,1.92 1.5,1.5 C1.5,1.08 1.35,0.73 1.05,0.45 C0.77,0.15 0.42,0 0,0 C-0.42,0 -0.78,0.15 -1.08,0.45 C-1.36,0.73 -1.5,1.08 -1.5,1.5 C-1.5,1.92 -1.36,2.28 -1.08,2.58 C-0.78,2.86 -0.42,3 0,3c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_hotspot_icon_off.xml b/packages/SystemUI/res/drawable/qs_hotspot_icon_off.xml
new file mode 100644
index 000000000000..eb160de7f249
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_hotspot_icon_off.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="133"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="133"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="1"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="150"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -1 C-1.1,-1 -2,-0.1 -2,1 C-2,1.55 -1.77,2.05 -1.41,2.41 C-1.05,2.77 -0.55,3 0,3 C0.55,3 1.05,2.77 1.41,2.41 C1.77,2.05 2,1.55 2,1 C2,-0.1 1.1,-1 0,-1c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -5 C-3.31,-5 -6,-2.31 -6,1 C-6,2.66 -5.32,4.15 -4.24,5.24 C-4.24,5.24 -2.82,3.82 -2.82,3.82 C-3.55,3.1 -4,2.11 -4,1 C-4,-1.21 -2.21,-3 0,-3 C2.21,-3 4,-1.21 4,1 C4,2.11 3.55,3.1 2.82,3.82 C2.82,3.82 4.24,5.24 4.24,5.24 C5.32,4.15 6,2.66 6,1 C6,-2.31 3.31,-5 0,-5c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -9 C-5.52,-9 -10,-4.52 -10,1 C-10,3.76 -8.88,6.26 -7.07,8.07 C-7.07,8.07 -5.66,6.66 -5.66,6.66 C-7.1,5.21 -8,3.21 -8,1 C-8,-3.42 -4.42,-7 0,-7 C4.42,-7 8,-3.42 8,1 C8,3.21 7.1,5.2 5.65,6.65 C5.65,6.65 7.06,8.06 7.06,8.06 C8.88,6.26 10,3.76 10,1 C10,-4.52 5.52,-9 0,-9c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_hotspot_icon_on.xml b/packages/SystemUI/res/drawable/qs_hotspot_icon_on.xml
new file mode 100644
index 000000000000..de972a62d7f7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_hotspot_icon_on.xml
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0.3"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0.3"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="fillAlpha"
+ android:startOffset="167"
+ android:valueFrom="0.3"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="433"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -1 C-1.1,-1 -2,-0.1 -2,1 C-2,1.55 -1.77,2.05 -1.41,2.41 C-1.05,2.77 -0.55,3 0,3 C0.55,3 1.05,2.77 1.41,2.41 C1.77,2.05 2,1.55 2,1 C2,-0.1 1.1,-1 0,-1c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="0.3"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -5 C-3.31,-5 -6,-2.31 -6,1 C-6,2.66 -5.32,4.15 -4.24,5.24 C-4.24,5.24 -2.82,3.82 -2.82,3.82 C-3.55,3.1 -4,2.11 -4,1 C-4,-1.21 -2.21,-3 0,-3 C2.21,-3 4,-1.21 4,1 C4,2.11 3.55,3.1 2.82,3.82 C2.82,3.82 4.24,5.24 4.24,5.24 C5.32,4.15 6,2.66 6,1 C6,-2.31 3.31,-5 0,-5c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="0.3"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -9 C-5.52,-9 -10,-4.52 -10,1 C-10,3.76 -8.88,6.26 -7.07,8.07 C-7.07,8.07 -5.66,6.66 -5.66,6.66 C-7.1,5.21 -8,3.21 -8,1 C-8,-3.42 -4.42,-7 0,-7 C4.42,-7 8,-3.42 8,1 C8,3.21 7.1,5.2 5.65,6.65 C5.65,6.65 7.06,8.06 7.06,8.06 C8.88,6.26 10,3.76 10,1 C10,-4.52 5.52,-9 0,-9c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_hotspot_icon_search.xml b/packages/SystemUI/res/drawable/qs_hotspot_icon_search.xml
new file mode 100644
index 000000000000..e33b264ad528
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_hotspot_icon_search.xml
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_D_1_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0.3"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="fillAlpha"
+ android:startOffset="250"
+ android:valueFrom="1"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_2_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="167"
+ android:propertyName="fillAlpha"
+ android:startOffset="0"
+ android:valueFrom="0.3"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="fillAlpha"
+ android:startOffset="167"
+ android:valueFrom="0.3"
+ android:valueTo="1"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ <objectAnimator
+ android:duration="250"
+ android:propertyName="fillAlpha"
+ android:startOffset="417"
+ android:valueFrom="1"
+ android:valueTo="0.3"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="850"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -1 C-1.1,-1 -2,-0.1 -2,1 C-2,1.55 -1.77,2.05 -1.41,2.41 C-1.05,2.77 -0.55,3 0,3 C0.55,3 1.05,2.77 1.41,2.41 C1.77,2.05 2,1.55 2,1 C2,-0.1 1.1,-1 0,-1c " />
+ <path
+ android:name="_R_G_L_0_G_D_1_P_0"
+ android:fillAlpha="0.3"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -5 C-3.31,-5 -6,-2.31 -6,1 C-6,2.66 -5.32,4.15 -4.24,5.24 C-4.24,5.24 -2.82,3.82 -2.82,3.82 C-3.55,3.1 -4,2.11 -4,1 C-4,-1.21 -2.21,-3 0,-3 C2.21,-3 4,-1.21 4,1 C4,2.11 3.55,3.1 2.82,3.82 C2.82,3.82 4.24,5.24 4.24,5.24 C5.32,4.15 6,2.66 6,1 C6,-2.31 3.31,-5 0,-5c " />
+ <path
+ android:name="_R_G_L_0_G_D_2_P_0"
+ android:fillAlpha="0.3"
+ android:fillColor="#ffffff"
+ android:fillType="nonZero"
+ android:pathData=" M0 -9 C-5.52,-9 -10,-4.52 -10,1 C-10,3.76 -8.88,6.26 -7.07,8.07 C-7.07,8.07 -5.66,6.66 -5.66,6.66 C-7.1,5.21 -8,3.21 -8,1 C-8,-3.42 -4.42,-7 0,-7 C4.42,-7 8,-3.42 8,1 C8,3.21 7.1,5.2 5.65,6.65 C5.65,6.65 7.06,8.06 7.06,8.06 C8.88,6.26 10,3.76 10,1 C10,-4.52 5.52,-9 0,-9c " />
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_nightlight_icon_off.xml b/packages/SystemUI/res/drawable/qs_nightlight_icon_off.xml
new file mode 100644
index 000000000000..0769a85dae49
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_nightlight_icon_off.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="283"
+ android:propertyName="translateY"
+ android:startOffset="0"
+ android:valueFrom="12.125"
+ android:valueTo="12.312"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="283"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="-45"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="300"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G_T_1"
+ android:rotation="-45"
+ android:translateX="12.875"
+ android:translateY="12.125">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-2.375">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:pathData=" M3.33 8.62 C2.09,8.68 0.59,8.47 -0.64,7.95 C-1.65,7.53 -2.75,6.9 -3.59,6.09 C-4.38,5.32 -5.19,4.17 -5.61,3.08 C-6.04,1.99 -6.25,0.83 -6.25,-0.39 C-6.25,-1.72 -5.98,-2.85 -5.55,-3.94 C-5.13,-5.02 -4.37,-6 -3.59,-6.78 C-2.63,-7.75 -1.63,-8.28 -0.52,-8.77 C0.48,-9.2 2.04,-9.41 3.15,-9.38 C4.22,-9.35 4.94,-9.16 5.81,-8.79 C5.95,-8.73 6.28,-8.6 6.18,-8.55 C3.44,-6.63 1.83,-3.66 1.83,-0.39 C1.83,2.53 3.47,5.72 6.15,7.86 C6.16,7.9 6.03,7.97 5.91,8.04 C5.12,8.44 4.31,8.56 3.33,8.62c "
+ android:strokeAlpha="1"
+ android:strokeColor="#ffffff"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_nightlight_icon_on.xml b/packages/SystemUI/res/drawable/qs_nightlight_icon_on.xml
new file mode 100644
index 000000000000..5ffe262d8077
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_nightlight_icon_on.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ Copyright (C) 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="400"
+ android:propertyName="translateY"
+ android:startOffset="0"
+ android:valueFrom="12.312"
+ android:valueTo="12.125"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_T_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="400"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="-45"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.1,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_0_G_T_1"
+ android:rotation="0"
+ android:translateX="12.875"
+ android:translateY="12.312">
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="-2.375">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:pathData=" M3.33 8.62 C2.09,8.68 0.59,8.47 -0.64,7.95 C-1.65,7.53 -2.75,6.9 -3.59,6.09 C-4.38,5.32 -5.19,4.17 -5.61,3.08 C-6.04,1.99 -6.25,0.83 -6.25,-0.39 C-6.25,-1.72 -5.98,-2.85 -5.55,-3.94 C-5.13,-5.02 -4.37,-6 -3.6,-6.78 C-2.63,-7.75 -1.63,-8.28 -0.52,-8.77 C0.48,-9.2 2.04,-9.41 3.14,-9.38 C4.22,-9.35 4.94,-9.16 5.81,-8.79 C5.94,-8.73 6.28,-8.6 6.18,-8.55 C3.44,-6.63 1.83,-3.66 1.83,-0.39 C1.83,2.53 3.47,5.72 6.15,7.86 C6.16,7.9 6.03,7.97 5.91,8.04 C5.13,8.43 4.31,8.56 3.33,8.62c "
+ android:strokeAlpha="1"
+ android:strokeColor="#ffffff"
+ android:strokeLineCap="round"
+ android:strokeLineJoin="round"
+ android:strokeWidth="2" />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml b/packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml
new file mode 100644
index 000000000000..b3bdd361398d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_screen_record_icon_off.xml
@@ -0,0 +1,218 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#edf2eb"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M3.5 -2.5 C3.5,-2.5 3.5,2.5 3.5,2.5 C3.5,3.05 3.05,3.5 2.5,3.5 C2.5,3.5 -2.5,3.5 -2.5,3.5 C-3.05,3.5 -3.5,3.05 -3.5,2.5 C-3.5,2.5 -3.5,-2.5 -3.5,-2.5 C-3.5,-3.05 -3.05,-3.5 -2.5,-3.5 C-2.5,-3.5 2.5,-3.5 2.5,-3.5 C3.05,-3.5 3.5,-3.05 3.5,-2.5c "
+ android:valueTo="M3.5 0 C3.5,0.97 3.11,1.84 2.48,2.48 C1.84,3.11 0.97,3.5 0,3.5 C-0.97,3.5 -1.84,3.11 -2.47,2.48 C-3.11,1.84 -3.5,0.97 -3.5,0 C-3.5,-0.97 -3.11,-1.84 -2.47,-2.47 C-1.84,-3.11 -0.97,-3.5 0,-3.5 C0.97,-3.5 1.84,-3.11 2.48,-2.47 C3.11,-1.84 3.5,-0.97 3.5,0c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="90"
+ android:valueTo="0"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#edf2eb"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#edf2eb"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_2">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#edf2eb"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_3">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#000000"
+ android:valueTo="#edf2eb"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1000"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:rotation="90"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M3.5 -2.5 C3.5,-2.5 3.5,2.5 3.5,2.5 C3.5,3.05 3.05,3.5 2.5,3.5 C2.5,3.5 -2.5,3.5 -2.5,3.5 C-3.05,3.5 -3.5,3.05 -3.5,2.5 C-3.5,2.5 -3.5,-2.5 -3.5,-2.5 C-3.5,-3.05 -3.05,-3.5 -2.5,-3.5 C-2.5,-3.5 2.5,-3.5 2.5,-3.5 C3.05,-3.5 3.5,-3.05 3.5,-2.5c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M6.6 -4.47 C6.6,-4.47 8.04,-5.91 8.04,-5.91 C9.26,-4.26 9.99,-2.21 10,0.01 C10,2.23 9.26,4.27 8.04,5.93 C8.04,5.93 6.6,4.49 6.6,4.49 C7.47,3.2 7.99,1.66 7.99,0 C7.99,-1.65 7.47,-3.19 6.6,-4.47c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_1"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M-8.01 0 C-8.01,1.67 -7.49,3.22 -6.61,4.5 C-6.61,4.5 -8.04,5.93 -8.04,5.93 C-9.27,4.27 -10,2.22 -10,0 C-10,-2.22 -9.27,-4.26 -8.05,-5.92 C-8.05,-5.92 -6.62,-4.49 -6.62,-4.49 C-7.49,-3.21 -8.01,-1.66 -8.01,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_2_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_2"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M-0.01 8 C1.66,8 3.2,7.48 4.48,6.61 C4.48,6.61 5.91,8.05 5.91,8.05 C4.25,9.27 2.21,10 -0.01,10 C-2.22,10 -4.26,9.27 -5.92,8.05 C-5.92,8.05 -4.48,6.61 -4.48,6.61 C-3.21,7.48 -1.67,8 -0.01,8c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_3_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_3"
+ android:fillAlpha="1"
+ android:fillColor="#000000"
+ android:fillType="nonZero"
+ android:pathData=" M-5.93 -8.04 C-4.27,-9.27 -2.23,-10 -0.01,-10 C2.22,-10 4.26,-9.27 5.94,-8.03 C5.94,-8.03 4.5,-6.59 4.5,-6.59 C3.21,-7.47 1.67,-7.99 0,-7.99 C-1.67,-7.99 -3.21,-7.47 -4.49,-6.6 C-4.49,-6.6 -5.93,-8.04 -5.93,-8.04c " />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml b/packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml
new file mode 100644
index 000000000000..facbef5e4348
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_screen_record_icon_on.xml
@@ -0,0 +1,286 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#edf2eb"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="pathData"
+ android:startOffset="0"
+ android:valueFrom="M3.5 0 C3.5,0.97 3.11,1.84 2.48,2.48 C1.84,3.11 0.97,3.5 0,3.5 C-0.97,3.5 -1.84,3.11 -2.47,2.48 C-3.11,1.84 -3.5,0.97 -3.5,0 C-3.5,-0.97 -3.11,-1.84 -2.47,-2.47 C-1.84,-3.11 -0.97,-3.5 0,-3.5 C0.97,-3.5 1.84,-3.11 2.48,-2.47 C3.11,-1.84 3.5,-0.97 3.5,0c "
+ android:valueTo="M3.5 -2.5 C3.5,-2.5 3.5,2.5 3.5,2.5 C3.5,3.05 3.05,3.5 2.5,3.5 C2.5,3.5 -2.5,3.5 -2.5,3.5 C-3.05,3.5 -3.5,3.05 -3.5,2.5 C-3.5,2.5 -3.5,-2.5 -3.5,-2.5 C-3.5,-3.05 -3.05,-3.5 -2.5,-3.5 C-2.5,-3.5 2.5,-3.5 2.5,-3.5 C3.05,-3.5 3.5,-3.05 3.5,-2.5c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_1_G">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="90"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#edf2eb"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="833"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_1">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#edf2eb"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="833"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_2">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#edf2eb"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_2_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="833"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_3">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="417"
+ android:propertyName="fillColor"
+ android:startOffset="0"
+ android:valueFrom="#edf2eb"
+ android:valueTo="#000000"
+ android:valueType="colorType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,1 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_3_G_0_T_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="833"
+ android:propertyName="rotation"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:valueType="floatType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.7,0 0.2,0.53 1.0,1.0" />
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator
+ android:duration="1000"
+ android:propertyName="translateX"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType" />
+ </set>
+ </aapt:attr>
+ </target>
+ <aapt:attr name="android:drawable">
+ <vector
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group
+ android:name="_R_G_L_1_G"
+ android:rotation="0"
+ android:translateX="12"
+ android:translateY="12">
+ <path
+ android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M3.5 0 C3.5,0.97 3.11,1.84 2.48,2.48 C1.84,3.11 0.97,3.5 0,3.5 C-0.97,3.5 -1.84,3.11 -2.47,2.48 C-3.11,1.84 -3.5,0.97 -3.5,0 C-3.5,-0.97 -3.11,-1.84 -2.47,-2.47 C-1.84,-3.11 -0.97,-3.5 0,-3.5 C0.97,-3.5 1.84,-3.11 2.48,-2.47 C3.11,-1.84 3.5,-0.97 3.5,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12">
+ <group
+ android:name="_R_G_L_0_G_D_0_P_0_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M6.6 -4.47 C6.6,-4.47 8.04,-5.91 8.04,-5.91 C9.26,-4.26 9.99,-2.21 10,0.01 C10,2.23 9.26,4.27 8.04,5.93 C8.04,5.93 6.6,4.49 6.6,4.49 C7.47,3.2 7.99,1.66 7.99,0 C7.99,-1.65 7.47,-3.19 6.6,-4.47c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_1_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_1"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M-8.01 0 C-8.01,1.67 -7.49,3.22 -6.61,4.5 C-6.61,4.5 -8.04,5.93 -8.04,5.93 C-9.27,4.27 -10,2.22 -10,0 C-10,-2.22 -9.27,-4.26 -8.05,-5.92 C-8.05,-5.92 -6.62,-4.49 -6.62,-4.49 C-7.49,-3.21 -8.01,-1.66 -8.01,0c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_2_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_2"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M-0.01 8 C1.66,8 3.2,7.48 4.48,6.61 C4.48,6.61 5.91,8.05 5.91,8.05 C4.25,9.27 2.21,10 -0.01,10 C-2.22,10 -4.26,9.27 -5.92,8.05 C-5.92,8.05 -4.48,6.61 -4.48,6.61 C-3.21,7.48 -1.67,8 -0.01,8c " />
+ </group>
+ <group
+ android:name="_R_G_L_0_G_D_0_P_3_G_0_T_0"
+ android:rotation="0">
+ <path
+ android:name="_R_G_L_0_G_D_0_P_3"
+ android:fillAlpha="1"
+ android:fillColor="#edf2eb"
+ android:fillType="nonZero"
+ android:pathData=" M-5.93 -8.04 C-4.27,-9.27 -2.23,-10 -0.01,-10 C2.22,-10 4.26,-9.27 5.94,-8.03 C5.94,-8.03 4.5,-6.59 4.5,-6.59 C3.21,-7.47 1.67,-7.99 0,-7.99 C-1.67,-7.99 -3.21,-7.47 -4.49,-6.6 C-4.49,-6.6 -5.93,-8.04 -5.93,-8.04c " />
+ </group>
+ </group>
+ </group>
+ <group android:name="time_group" />
+ </vector>
+ </aapt:attr>
+</animated-vector>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
index acb47f7a037c..2d67d95ab17e 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -14,8 +14,9 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.dreams.complication.DoubleShadowTextClock
+<com.android.systemui.shared.shadow.DoubleShadowTextClock
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/time_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -25,4 +26,13 @@
android:format24Hour="@string/dream_time_complication_24_hr_time_format"
android:fontFeatureSettings="pnum, lnum"
android:letterSpacing="0.02"
- android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"/>
+ android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"
+ app:keyShadowBlur="@dimen/dream_overlay_clock_key_text_shadow_radius"
+ app:keyShadowOffsetX="@dimen/dream_overlay_clock_key_text_shadow_dx"
+ app:keyShadowOffsetY="@dimen/dream_overlay_clock_key_text_shadow_dy"
+ app:keyShadowAlpha="0.3"
+ app:ambientShadowBlur="@dimen/dream_overlay_clock_ambient_text_shadow_radius"
+ app:ambientShadowOffsetX="@dimen/dream_overlay_clock_ambient_text_shadow_dx"
+ app:ambientShadowOffsetY="@dimen/dream_overlay_clock_ambient_text_shadow_dy"
+ app:ambientShadowAlpha="0.3"
+/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
index c972624c04b1..006b260434bc 100644
--- a/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_status_bar_view.xml
@@ -40,7 +40,6 @@
android:layout_height="match_parent"
android:orientation="horizontal"
android:gravity="center_vertical"
- android:layout_marginEnd="@dimen/dream_overlay_status_bar_extra_margin"
app:layout_constraintEnd_toStartOf="@+id/dream_overlay_system_status" />
<LinearLayout
@@ -48,14 +47,15 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
- android:layout_marginStart="@dimen/dream_overlay_status_bar_extra_margin"
+ android:paddingStart="@dimen/dream_overlay_status_bar_extra_margin"
+ android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent">
<com.android.systemui.statusbar.AlphaOptimizedImageView
android:id="@+id/dream_overlay_alarm_set"
android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
android:src="@drawable/ic_alarm"
android:tint="@android:color/white"
android:visibility="gone"
@@ -65,7 +65,7 @@
android:id="@+id/dream_overlay_priority_mode"
android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
android:src="@drawable/ic_qs_dnd_on"
android:tint="@android:color/white"
android:visibility="gone"
@@ -75,7 +75,7 @@
android:id="@+id/dream_overlay_wifi_status"
android:layout_width="@dimen/dream_overlay_status_bar_icon_size"
android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
android:src="@drawable/ic_signal_wifi_off"
android:visibility="gone"
android:contentDescription="@string/dream_overlay_status_bar_wifi_off" />
@@ -84,7 +84,7 @@
android:id="@+id/dream_overlay_mic_off"
android:layout_width="@dimen/dream_overlay_grey_chip_width"
android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
android:src="@drawable/dream_overlay_mic_off"
android:visibility="gone"
android:contentDescription="@string/dream_overlay_status_bar_mic_off" />
@@ -93,7 +93,7 @@
android:id="@+id/dream_overlay_camera_off"
android:layout_width="@dimen/dream_overlay_grey_chip_width"
android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
android:src="@drawable/dream_overlay_camera_off"
android:visibility="gone"
android:contentDescription="@string/dream_overlay_status_bar_camera_off" />
@@ -102,7 +102,7 @@
android:id="@+id/dream_overlay_camera_mic_off"
android:layout_width="@dimen/dream_overlay_grey_chip_width"
android:layout_height="match_parent"
- android:layout_marginEnd="@dimen/dream_overlay_status_icon_margin"
+ android:layout_marginStart="@dimen/dream_overlay_status_icon_margin"
android:src="@drawable/dream_overlay_mic_and_camera_off"
android:visibility="gone"
android:contentDescription="@string/dream_overlay_status_bar_camera_mic_off" />
diff --git a/packages/SystemUI/res/layout/media_output_dialog.xml b/packages/SystemUI/res/layout/media_output_dialog.xml
index 93c16e4e119d..b76de5afdb73 100644
--- a/packages/SystemUI/res/layout/media_output_dialog.xml
+++ b/packages/SystemUI/res/layout/media_output_dialog.xml
@@ -20,6 +20,7 @@
android:id="@+id/media_output_dialog"
android:layout_width="@dimen/large_dialog_width"
android:layout_height="wrap_content"
+ android:background="@drawable/media_output_dialog_background"
android:orientation="vertical">
<LinearLayout
diff --git a/packages/SystemUI/res/layout/media_projection_app_selector.xml b/packages/SystemUI/res/layout/media_projection_app_selector.xml
index 4ad6849e9c45..226bc6afc5ab 100644
--- a/packages/SystemUI/res/layout/media_projection_app_selector.xml
+++ b/packages/SystemUI/res/layout/media_projection_app_selector.xml
@@ -36,13 +36,14 @@
android:background="@*android:drawable/bottomsheet_background">
<ImageView
- android:id="@*android:id/icon"
android:layout_width="@dimen/media_projection_app_selector_icon_size"
android:layout_height="@dimen/media_projection_app_selector_icon_size"
android:layout_marginTop="@*android:dimen/chooser_edge_margin_normal"
android:layout_marginBottom="@*android:dimen/chooser_edge_margin_normal"
android:importantForAccessibility="no"
- android:tint="?android:attr/textColorPrimary"/>
+ android:tint="?android:attr/textColorPrimary"
+ android:src="@drawable/ic_present_to_all"
+ />
<TextView android:id="@*android:id/title"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/media_projection_recent_tasks.xml b/packages/SystemUI/res/layout/media_projection_recent_tasks.xml
new file mode 100644
index 000000000000..a2b3c404f077
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_projection_recent_tasks.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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="wrap_content"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:background="?android:attr/colorBackground"
+ >
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="256dp">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/media_projection_recent_tasks_recycler"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="gone"
+ />
+
+ <ProgressBar
+ android:id="@+id/media_projection_recent_tasks_loader"
+ android:layout_width="@dimen/media_projection_app_selector_loader_size"
+ android:layout_height="@dimen/media_projection_app_selector_loader_size"
+ android:layout_gravity="center"
+ android:indeterminate="true"
+ android:indeterminateOnly="true" />
+ </FrameLayout>
+
+ <!-- Divider line -->
+ <ImageView
+ android:layout_width="72dp"
+ android:layout_height="2dp"
+ android:layout_marginBottom="8dp"
+ android:layout_marginTop="24dp"
+ android:importantForAccessibility="no"
+ android:src="@*android:drawable/ic_drag_handle"
+ android:tint="?android:attr/textColorSecondary" />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/media_projection_task_item.xml b/packages/SystemUI/res/layout/media_projection_task_item.xml
new file mode 100644
index 000000000000..75f73cd7a1e9
--- /dev/null
+++ b/packages/SystemUI/res/layout/media_projection_task_item.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <ImageView
+ android:id="@+id/task_icon"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_margin="8dp"
+ android:importantForAccessibility="no" />
+
+ <!-- TODO(b/240924926) use a custom view that will handle thumbnail cropping correctly -->
+ <!-- TODO(b/240924926) dynamically change the view size based on the screen size -->
+ <ImageView
+ android:id="@+id/task_thumbnail"
+ android:layout_width="100dp"
+ android:layout_height="216dp"
+ android:scaleType="centerCrop"
+ />
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml
index 0c57b934d27e..8388b67c1fa1 100644
--- a/packages/SystemUI/res/layout/super_notification_shade.xml
+++ b/packages/SystemUI/res/layout/super_notification_shade.xml
@@ -103,6 +103,7 @@
android:layout_width="match_parent"
android:layout_weight="1"
android:background="@android:color/transparent"
+ android:visibility="invisible"
android:clipChildren="false"
android:clipToPadding="false" />
</LinearLayout>
diff --git a/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json b/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json
index b1616094ade4..09ed22560885 100644
--- a/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json
+++ b/packages/SystemUI/res/raw/biometricprompt_portrait_base_topleft.json
@@ -1 +1 @@
-{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"biometricprompt_portrait_base_topleft","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":6,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null_Circle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey905","cl":"grey905","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"circle mask 3","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Finger_Flipped","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-24.98,-35.709,0],"ix":2,"l":2},"a":{"a":0,"k":[31.791,75.23,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.03,5.25],[-2.83,8.98],[-5.59,-0.26],[2.52,-11.02]],"o":[[-2.85,12.77],[2.07,-14.96],[1.9,-6],[1.4,8.05],[0,0]],"v":[[7.5,4.99],[-10.09,19.69],[-3.59,-16.61],[8.69,-24.92],[7.5,5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.8,24.94],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-7.01,22.23],[-1.2,-27.39],[4.09,-26.79],[15.73,14.18]],"o":[[5.64,-17.93],[2.45,56.06],[-22.4,-1.77],[17.73,-51.82]],"v":[[-7.57,-66.9],[30.82,-44.76],[26.65,75.23],[-31.78,50.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[31.79,75.23],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"circle mask 7","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"circle mask","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey904","cl":"grey904","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"circle mask 6","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey903","cl":"grey903","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"circle mask 2","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".blue400","cl":"blue400","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"circle mask 4","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":1,"nm":".grey902","cl":"grey902","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"circle mask 5","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":1,"nm":".black","cl":"black","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".grey800","cl":"grey800","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":".grey901","cl":"grey901","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-87.156,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[100.25,-94.656,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Shape Layer 4","parent":6,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":".grey900","cl":"grey900","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
+{"v":"5.9.0","fr":60,"ip":0,"op":21,"w":340,"h":340,"nm":"BiometricPrompt_Portrait_Base_TopLeft","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":6,"ty":3,"nm":"Null 16","sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":-90,"ix":10},"p":{"a":0,"k":[170,170,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":7,"ty":3,"nm":"Null_Circle","parent":6,"sr":1,"ks":{"o":{"a":0,"k":0,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[70.333,-88.75,0],"to":[-11.722,17.639,0],"ti":[11.722,-17.639,0]},{"t":-48,"s":[0,17.083,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":8,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":9,"ty":4,"nm":"circle mask 3","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":10,"ty":4,"nm":"Finger_Flipped","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":90,"ix":10},"p":{"a":0,"k":[-24.98,-35.709,0],"ix":2,"l":2},"a":{"a":0,"k":[31.791,75.23,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[5.03,5.25],[-2.83,8.98],[-5.59,-0.26],[2.52,-11.02]],"o":[[-2.85,12.77],[2.07,-14.96],[1.9,-6],[1.4,8.05],[0,0]],"v":[[7.5,4.99],[-10.09,19.69],[-3.59,-16.61],[8.69,-24.92],[7.5,5]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.760784373564,0.478431402468,0.400000029919,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.8,24.94],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-7.01,22.23],[-1.2,-27.39],[4.09,-26.79],[15.73,14.18]],"o":[[5.64,-17.93],[2.45,56.06],[-22.4,-1.77],[17.73,-51.82]],"v":[[-7.57,-66.9],[30.82,-44.76],[26.65,75.23],[-31.78,50.08]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.678431372549,0.403921598547,0.305882352941,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[31.79,75.23],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":900,"st":0,"bm":0},{"ddd":0,"ind":11,"ty":4,"nm":"circle mask 7","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":12,"ty":4,"nm":".grey600","cl":"grey600","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.25,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.501960784314,0.525490196078,0.545098039216,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":36.9,"ix":2},"o":{"a":0,"k":114.2,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":13,"ty":4,"nm":"circle mask","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":14,"ty":4,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[-0.5,0,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[114.218,-17.096],[-112.938,-17.096]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":10,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"tm","s":{"a":0,"k":0,"ix":1},"e":{"a":0,"k":100,"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":15,"ty":4,"nm":"circle mask 6","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":16,"ty":4,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.7],"y":[0]},"t":377,"s":[-180]},{"t":417,"s":[0]}],"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":377,"s":[-1.137,1.771,0],"to":[0.375,0,0],"ti":[-0.375,0,0]},{"t":417,"s":[1.113,1.771,0]}],"ix":2,"l":2},"a":{"a":0,"k":[6.238,5.063,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":77,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":107,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":137,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":167,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.333,"y":0},"t":197,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,-4.637],[-10.23,-3.195],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]},{"i":{"x":0.833,"y":1},"o":{"x":0.7,"y":0},"t":232,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":562,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-8.788,0.393],[-10.23,1.835],[-2.196,9.843],[5.988,1.659],[4.545,0.217],[-2.196,6.948]],"c":false}]},{"t":602,"s":[{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[-4.546,-0.421],[-5.988,1.021],[-2.196,4.813],[5.988,-3.371],[4.545,-4.813],[-2.196,1.918]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[6.238,5.063],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":17,"ty":4,"nm":"circle mask 2","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":18,"ty":4,"nm":".blue400","cl":"blue400","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,8.308,0],"ix":2,"l":2},"a":{"a":0,"k":[41.706,20.979,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[18.645,0],[0,18.645]],"o":[[0,18.645],[-18.644,0],[0,0]],"v":[[33.76,-16.88],[-0.001,16.88],[-33.76,-16.88]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,17.13],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[22.896,0],[0,22.896]],"o":[[0,22.896],[-22.896,0],[0,0]],"v":[[41.457,-20.729],[-0.001,20.729],[-41.457,-20.729]],"c":false},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[41.706,20.979],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":4,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":19,"ty":4,"nm":"circle mask 4","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":20,"ty":1,"nm":".grey900","cl":"grey900","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,66,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[52,52,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#202124","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":21,"ty":4,"nm":"circle mask 5","parent":7,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,0.333,0],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-108,"s":[0,0]},{"t":-48,"s":[202,202]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"st","c":{"a":0,"k":[0.250980392157,0.282352941176,0.294117647059,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"fl","c":{"a":0,"k":[0.078246000701,0.610494037703,0.787910970052,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,-17.333],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":22,"ty":1,"nm":".black","cl":"black","parent":7,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[0,-17.333,0],"ix":2,"l":2},"a":{"a":0,"k":[206,150,0],"ix":1,"l":2},"s":{"a":0,"k":[72,72,100],"ix":6,"l":2}},"ao":0,"sw":412,"sh":300,"sc":"#000000","ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":23,"ty":4,"nm":".grey800","cl":"grey800","parent":6,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-108,"s":[-192.25,99.933,0],"to":[5,3.333,0],"ti":[-5,-3.333,0]},{"t":-48,"s":[-162.25,119.933,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-163,100.85,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.2,0.2,0.833],"y":[1,1,1]},"o":{"x":[0.7,0.7,0.167],"y":[0,0,0]},"t":-108,"s":[100,100,100]},{"t":-48,"s":[59,59,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":0,"k":[326,201.699],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":8,"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"st","c":{"a":0,"k":[0.235294117647,0.250980392157,0.262745098039,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":2,"ix":5},"lc":1,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":24,"ty":4,"nm":".grey900","cl":"grey900","parent":23,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[100.25,-87.156,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[100.25,-94.656,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-171,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-141,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.512],[0,0.512],[3,3.512]],"c":false}]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":-111,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]},{"t":-81,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,3.967],[0,0.967],[3,3.967]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-199,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":25,"ty":4,"nm":"Shape Layer 4","parent":6,"td":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-116.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.7,"y":0},"t":-199,"s":[71,-101.083,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.833,"y":0.833},"o":{"x":0.167,"y":0.167},"t":365,"s":[71,-101.083,0],"to":[0,0,0],"ti":[16.833,-14.361,0]},{"t":405,"s":[-30,-14.917,0]}],"ix":2,"l":2},"a":{"a":0,"k":[0,0,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"rc","d":1,"s":{"a":1,"k":[{"i":{"x":[0.2,0.2],"y":[1,1]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-239,"s":[29,29]},{"i":{"x":[0.833,0.833],"y":[1,0.833]},"o":{"x":[0.7,0.7],"y":[0,0]},"t":-199,"s":[29,38]},{"i":{"x":[0.833,0.833],"y":[0.833,0.833]},"o":{"x":[0.167,0.167],"y":[0.167,0.167]},"t":365,"s":[29,36]},{"t":405,"s":[83,83]}],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":365,"s":[50]},{"t":405,"s":[50]}],"ix":4},"nm":"Rectangle Path 1","mn":"ADBE Vector Shape - Rect","hd":false},{"ty":"fl","c":{"a":0,"k":[0.400000029919,0.61568627451,0.964705942191,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Rectangle 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":645,"st":-255,"bm":0},{"ddd":0,"ind":26,"ty":4,"nm":".grey900","cl":"grey900","parent":6,"tt":1,"sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[71,-82.917,0],"to":[0,-1.25,0],"ti":[0,1.25,0]},{"t":-199,"s":[71,-90.417,0]}],"ix":2,"l":2},"a":{"a":0,"k":[5.5,4,0],"ix":1,"l":2},"s":{"a":0,"k":[100,100,100],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":1,"k":[{"i":{"x":0.2,"y":1},"o":{"x":0.7,"y":0},"t":-239,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-0.07,1.5],[0,-1.5],[-0.047,1.5]],"c":false}]},{"t":-199,"s":[{"i":[[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0]],"v":[[-3,1.5],[0,-1.5],[3,1.5]],"c":false}]}],"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"st","c":{"a":0,"k":[0.125490196078,0.129411764706,0.141176470588,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":1,"ix":5},"lc":1,"lj":1,"ml":10,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[5.5,4],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":-255,"op":-199,"st":-255,"bm":0}],"markers":[{"tm":255,"cm":"","dr":0},{"tm":364,"cm":"","dr":0},{"tm":482,"cm":"","dr":0},{"tm":600,"cm":"","dr":0}]} \ No newline at end of file
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index 7f6f006bb993..c7731775c296 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Sommige kenmerke is beperk terwyl foon afkoel.\nTik vir meer inligting"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Jou foon sal outomaties probeer om af te koel. Jy kan steeds jou foon gebruik, maar dit sal dalk stadiger wees.\n\nJou foon sal normaalweg werk nadat dit afgekoel het."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Sien versorgingstappe"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Prop jou toestel uit"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Jou toestel word tans warm naby die laaipoort. Prop dit uit as dit aan ’n laaier of USB-bykomstigheid gekoppel is. Wees versigtig, aangesien die kabel dalk ook warm is."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Sien versorgingstappe"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Links-kortpad"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Regs-kortpad"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index a3ebb60490ac..3df515b21e26 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"በርቷል"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ጠፍቷል"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"አይገኝም"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"የበለጠ ለመረዳት"</string>
<string name="nav_bar" msgid="4642708685386136807">"የአሰሳ አሞሌ"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"አቀማመጥ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ተጨማሪ የግራ አዝራር ዓይነት"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"አንዳንድ ባሕሪያት ስልኩ እየቀዘቀዘ እያለ ውስን ይሆናሉ።\nለተጨማሪ መረጃ መታ ያድርጉ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"የእርስዎ ስልክ በራስ-ሰር ለመቀዝቀዝ ይሞክራል። አሁንም ስልክዎን መጠቀም ይችላሉ፣ ነገር ግን ሊንቀራፈፍ ይችላል።\n\nአንዴ ስልክዎ ከቀዘቀዘ በኋላ በመደበኝነት ያሄዳል።"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"የእንክብካቤ ደረጃዎችን ይመልከቱ"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"መሣሪያዎን ይንቀሉ"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"መሣሪያዎ ከኃይል መሙያ ወደቡ አቅራቢያ እየሞቀ ነው። ከኃይል መሙያ ወይም ከዩኤስቢ ተጨማሪ መሣሪያ ጋር ከተገናኘ ይንቀሉት እና ገመዱ የሞቀ ሊሆን ስለሚችል ጥንቃቄ ያድርጉ።"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"የእንክብካቤ ደረጃዎችን ይመልከቱ"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"የግራ አቋራጭ"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"የቀኝ አቋራጭ"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 5a35e9023832..7672fc26319f 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"مفعّل"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"متوقف"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"غير متوفّر"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"مزيد من المعلومات"</string>
<string name="nav_bar" msgid="4642708685386136807">"شريط التنقل"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"التنسيق"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"نوع زر اليسار الإضافي"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"يتم تقييد عمل بعض الميزات إلى أن تنخفض درجة حرارة الهاتف.\nانقر للحصول على مزيد من المعلومات."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"سيحاول الهاتف تخفيض درجة حرارته تلقائيًا. سيظل بإمكانك استخدام هاتفك، ولكن قد يعمل بشكل أبطأ.\n\nبعد أن تنخفض درجة حرارة الهاتف، سيستعيد سرعته المعتادة."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"الاطّلاع على خطوات العناية"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"افصِل جهازك"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"‏تزداد حرارة الجهاز بالقرب من منفذ الشحن. إذا كان الجهاز متصلاً بشاحن أو ملحق USB، عليك فصله وتوخي الحذر لأن درجة حرارة الكابل قد تكون مرتفعة أيضًا."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"الاطّلاع على خطوات العناية"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"اختصار اليسار"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"اختصار اليمين"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index 912c82eac52c..990c812d44e3 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"অন"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"অফ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"উপলব্ধ নহয়"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"অধিক জানক"</string>
<string name="nav_bar" msgid="4642708685386136807">"নেভিগেশ্বন দণ্ড"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"লেআউট"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"বাওঁ বুটামৰ অতিৰিক্ত প্ৰকাৰ"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ফ’নটো ঠাণ্ডা হৈ থকাৰ সময়ত কিছুমান সুবিধা উপলব্ধ নহয়।\nঅধিক তথ্যৰ বাবে টিপক"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"আপোনাৰ ফ\'নটোৱে নিজে নিজে ঠাণ্ডা হ\'বলৈ স্বয়ংক্ৰিয়ভাৱে চেষ্টা কৰিব। আপুনি ফ\'নটো ব্যৱহাৰ কৰি থাকিব পাৰে কিন্তু ই লাহে লাহে চলিব পাৰে।\n\nফ\'নটো সম্পূৰ্ণভাৱে ঠাণ্ডা হোৱাৰ পিছত ই আগৰ নিচিনাকৈয়েই চলিব।"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"যত্ন লোৱাৰ পদক্ষেপসমূহ চাওক"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"আপোনাৰ ডিভাইচটো আনপ্লাগ কৰক"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"আপোনাৰ ডিভাইচটো চাৰ্জিং প’ৰ্টৰ ওচৰত গৰম হৈছে। যদি এইটো কোনো চার্জাৰ অথবা ইউএছবিৰ সহায়ক সামগ্ৰীৰ সৈতে সংযুক্ত হৈ আছে, ইয়াক আনপ্লাগ কৰক আৰু কে’বলডালো গৰম হ\'ব পাৰে, গতিকে যত্ন লওক।"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"যত্ন লোৱাৰ পদক্ষেপসমূহ চাওক"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"বাওঁ শ্বৰ্টকাট"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"সোঁ শ্বৰ্টকাট"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index fd2d848cc294..b21061aa427a 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Aktiv"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Deaktiv"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Əlçatan deyil"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ətraflı məlumat"</string>
<string name="nav_bar" msgid="4642708685386136807">"Naviqasiya paneli"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Tərtibat"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Əlavə sol düymə növü"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Telefon soyuyana kimi bəzi funksiyalar məhdudlaşdırılır.\nƏtraflı məlumat üçün toxunun"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonunuz avtomatik olaraq soyumağa başlayacaq. Telefon istifadəsinə davam edə bilərsiniz, lakin sürəti yavaşlaya bilər.\n\nTelefonunuz soyuduqdan sonra normal işləyəcək."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ehtiyat tədbiri mərhələlərinə baxın"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Cihazınızı ayırın"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Cihazınız şarj portunun yaxınlığında qızmağa başlayır. Şarj cihazına və ya USB aksesuarına qoşulubsa, onu ayırın və diqqətli olun, çünki kabel də qıza bilər."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ehtiyat tədbiri mərhələlərinə baxın"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Sol qısayol"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Sağ qısayol"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index dbef1e40eb81..78582cd3b5e4 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Uključeno"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Isključeno"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nedostupno"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"saznajte više"</string>
<string name="nav_bar" msgid="4642708685386136807">"Traka za navigaciju"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Raspored"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Dodatni tip levog dugmeta"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Neke funkcije su ograničene dok se telefon ne ohladi.\nDodirnite za više informacija"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon će automatski pokušati da se ohladi. I dalje ćete moći da koristite telefon, ali će sporije reagovati.\n\nKada se telefon ohladi, normalno će raditi."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pogledajte upozorenja"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Isključite uređaj"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Uređaj se zagreva u blizini porta za punjenje. Ako je povezan sa punjačem ili USB opremom, isključite je i budite pažljivi jer i kabl može da bude vruć."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Pogledajte upozorenja"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Leva prečica"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Desna prečica"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 372392c8131d..f1a84bc5f9ea 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Уключана"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Выключана"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Недаступна"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"даведацца больш"</string>
<string name="nav_bar" msgid="4642708685386136807">"Панэль навігацыі"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Раскладка"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Дадатковы тып кнопкі \"ўлева\""</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Некаторыя функцыі абмежаваны, пакуль тэлефон не астыне.\nНацісніце, каб даведацца больш"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ваш тэлефон аўтаматычна паспрабуе астыць. Вы можаце па-ранейшаму карыстацца сваім тэлефонам, але ён можа працаваць больш павольна.\n\nПасля таго як ваш тэлефон астыне, ён будзе працаваць у звычайным рэжыме."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Глядзець паэтапную дапамогу"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Адключыце прыладу"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Ваша прылада моцна награваецца ў месцы, дзе знаходзіцца зарадны порт. Калі яна падключана да зараднай прылады ці USB-прылады, адключыце яе і будзьце асцярожнымі з кабелем, які таксама можа награвацца."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Глядзець паэтапную дапамогу"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Ярлык \"улева\""</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Ярлык \"управа\""</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 1bca6a479f0c..46bd5c22939c 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Вкл."</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Изкл."</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Не е налице"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"научете повече"</string>
<string name="nav_bar" msgid="4642708685386136807">"Лента за навигация"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Оформление"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Тип на допълнителния ляв бутон"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Някои функции са ограничени, докато телефонът се охлажда.\nДокоснете за още информация"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефонът ви автоматично ще направи опит за охлаждане. Пак можете да го използвате, но той може да работи по-бавно.\n\nСлед като се охлади, ще работи нормално."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Вижте стъпките, които да предприемете"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Изключете устройството си"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Устройството ви се загрява до порта за зареждане. Ако е свързано със зарядно устройство или аксесоар за USB, изключете го и внимавайте, тъй като и кабелът може да е топъл."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Вижте стъпките, които да предприемете"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Ляв пряк път"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Десен пряк път"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 80beebc2f789..15cd7262e8dc 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ফোন ঠাণ্ডা না হওয়া পর্যন্ত কিছু ফিচার কাজ করে না।\nআরও তথ্যের জন্য ট্যাপ করুন"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"আপনার ফোনটি নিজে থেকেই ঠাণ্ডা হওয়ার চেষ্টা করবে৷ আপনি তবুও আপনার ফোন ব্যবহার করতে পারেন, কিন্তু এটি একটু ধীরে চলতে পারে৷\n\nআপনার ফোনটি পুরোপুরি ঠাণ্ডা হয়ে গেলে এটি স্বাভাবিকভাবে চলবে৷"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ডিভাইস রক্ষণাবেক্ষণের ধাপগুলি দেখুন"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"আপনার ডিভাইস আনপ্লাগ করা"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"চার্জিং পোর্টের কাছে আপনার ডিভাইসটি গরম হচ্ছে। এটি চার্জার বা ইউএসবি অ্যাক্সেসরির সাথে কানেক্ট করা থাকলে, আনপ্লাগ করুন এবং সতর্ক থাকুন কারণ কেবেলটিও গরম হতে পারে।"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"কী করতে হবে ধাপে ধাপে দেখুন"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"বাঁদিকের শর্টকাট"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"ডানদিকের শর্টকাট"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index ecb0e24ad742..8214ce014e95 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Uključeno"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Isključeno"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nedostupno"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"saznajte više"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigaciona traka"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Raspored"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Vrsta dodatnog dugmeta lijevo"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Neke funkcije su ograničene dok se telefon hladi.\nDodirnite za više informacija"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Vaš telefon će se automatski pokušati ohladiti. I dalje možete koristi telefon, ali će možda raditi sporije.\n\nNakon što se ohladi, telefon će normalno raditi."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pogledajte korake za zaštitu"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Iskopčajte uređaj"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Uređaj se zagrijava u blizini priključka za punjenje. Ako je povezan s punjačem ili USB dodatkom, iskopčajte ga i vodite računa jer i kabl može biti topao."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Prikaz koraka za zaštitu"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Prečica lijevo"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Prečica desno"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index bbffb029361e..7657933b05f6 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -251,7 +251,7 @@
<string name="quick_settings_connecting" msgid="2381969772953268809">"S\'està connectant..."</string>
<string name="quick_settings_hotspot_label" msgid="1199196300038363424">"Punt d\'accés Wi-Fi"</string>
<string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"S\'està activant…"</string>
- <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Economitzador activat"</string>
+ <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"Estalvi dades activat"</string>
<string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# dispositiu}other{# dispositius}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Llanterna"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Càmera en ús"</string>
@@ -593,13 +593,12 @@
<string name="accessibility_long_click_tile" msgid="210472753156768705">"Obre la configuració"</string>
<string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"Auriculars connectats"</string>
<string name="accessibility_status_bar_headset" msgid="2699275863720926104">"Auriculars connectats"</string>
- <string name="data_saver" msgid="3484013368530820763">"Economitzador de dades"</string>
- <string name="accessibility_data_saver_on" msgid="5394743820189757731">"L\'Economitzador de dades està activat"</string>
+ <string name="data_saver" msgid="3484013368530820763">"Estalvi de dades"</string>
+ <string name="accessibility_data_saver_on" msgid="5394743820189757731">"L\'Estalvi de dades està activat"</string>
<string name="switch_bar_on" msgid="1770868129120096114">"Activat"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desactivat"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"No disponible"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"més informació"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegació"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Disposició"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipus de botó addicional de l\'esquerra"</string>
@@ -624,7 +623,7 @@
<string name="right_keycode" msgid="2480715509844798438">"Codi de tecla de la dreta"</string>
<string name="left_icon" msgid="5036278531966897006">"Icona de l\'esquerra"</string>
<string name="right_icon" msgid="1103955040645237425">"Icona de la dreta"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén premut i arrossega per afegir mosaics"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"Mantén premut i arrossega per afegir icones"</string>
<string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Mantén premut i arrossega per reorganitzar els mosaics"</string>
<string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrossega aquí per suprimir"</string>
<string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessites com a mínim <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mosaics"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Algunes funcions estan limitades mentre el telèfon es refreda.\nToca per obtenir més informació"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"El telèfon provarà de refredar-se automàticament. Podràs continuar utilitzant-lo, però és possible que funcioni més lentament.\n\nUn cop s\'hagi refredat, funcionarà amb normalitat."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Mostra els passos de manteniment"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Desconnecta el dispositiu"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"El dispositiu s\'està escalfant a prop del port de càrrega. Si està connectat a un carregador o a un accessori USB, desconnecta\'l. Ves amb compte perquè el cable també pot haver-se escalfat."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Mostra els pasos de manteniment"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Drecera de l\'esquerra"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Drecera de la dreta"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 66874fdef434..70737ad187af 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Zapnuto"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Vypnuto"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nedostupné"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"další informace"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigační panel"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Rozvržení"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Zvláštní typ tlačítka vlevo"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Některé funkce jsou při chladnutí telefonu omezeny.\nKlepnutím zobrazíte další informace"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon se automaticky pokusí vychladnout. Lze jej nadále používat, ale může být pomalejší.\n\nAž telefon vychladne, bude fungovat normálně."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Zobrazit pokyny, co dělat"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Odpojte zařízení"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Zařízení se zahřívá v oblasti nabíjecího portu. Pokud je připojeno k nabíječce nebo příslušenství USB, odpojte ho (dejte pozor, kabel také může být zahřátý)."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Zobrazit pokyny, co dělat"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Zkratka vlevo"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Zkratka vpravo"</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index ab201d7522ac..65228d571bb0 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Til"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Fra"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Ikke tilgængelig"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"få flere oplysninger"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigationslinje"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra venstre knaptype"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Nogle funktioner er begrænsede, mens telefonen køler ned.\nTryk for at få flere oplysninger"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Din telefon forsøger automatisk at køle ned. Du kan stadig bruge telefonen, men den kører muligvis langsommere.\n\nNår din telefon er kølet ned, fungerer den normalt igen."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Se håndteringsvejledning"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Træk stikket ud af din enhed"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Din enhed er ved at blive varm i nærheden af opladningsporten. Hvis enheden er tilsluttet en oplader eller USB-enhed, skal du trække stikket ud. Vær opmærksom på, at stikket også kan være varmt."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Se vejledningen i pleje"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Venstre genvej"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Højre genvej"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 5bf907cc5ce6..c0ba5dc3c42b 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"An"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Aus"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nicht verfügbar"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"Weitere Informationen"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigationsleiste"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Zusätzlicher linker Schaltflächentyp"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Einige Funktionen sind während der Abkühlphase des Smartphones eingeschränkt.\nFür mehr Informationen tippen."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Dein Smartphone kühlt sich automatisch ab. Du kannst dein Smartphone weiterhin nutzen, aber es reagiert möglicherweise langsamer.\n\nSobald dein Smartphone abgekühlt ist, funktioniert es wieder normal."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Schritte zur Abkühlung des Geräts ansehen"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Gerät vom Stromnetz trennen"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Dein Gerät erwärmt sich am Ladeanschluss. Trenne das Gerät vom Stromnetz, wenn es an ein Ladegerät oder USB-Zubehör angeschlossen ist. Sei vorsichtig, denn das Kabel könnte ebenfalls heiß sein."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Schritte zur Fehlerbehebung ansehen"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Linke Verknüpfung"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Rechte Verknüpfung"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 2a1403cd293e..9fb2726eeaf6 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ενεργό"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Απενεργοποίηση"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Μη διαθέσιμο"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"μάθετε περισσότερα"</string>
<string name="nav_bar" msgid="4642708685386136807">"Γραμμή πλοήγησης"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Διάταξη"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Επιπλέον τύπος αριστερού κουμπιού"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Ορισμένες λειτουργίες περιορίζονται κατά τη μείωση της θερμοκρασίας.\nΠατήστε για περισσότερες πληροφορίες."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Το τηλέφωνό σας θα προσπαθήσει να μειώσει αυτόματα τη θερμοκρασία. Μπορείτε να εξακολουθήσετε να το χρησιμοποιείτε, αλλά είναι πιθανό να λειτουργεί πιο αργά.\n\nΜόλις μειωθεί η θερμοκρασία του τηλεφώνου σας, θα λειτουργεί ξανά κανονικά."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Δείτε βήματα αντιμετώπισης."</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Αποσυνδέστε τη συσκευή"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Η συσκευή έχει αρχίσει να ζεσταίνεται κοντά στη θύρα φόρτισης. Αν είναι συνδεδεμένη σε φορτιστή ή αξεσουάρ USB, αποσυνδέστε την και προσέξτε γιατί και το καλώδιο μπορεί να έχει ζεσταθεί."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Δείτε βήματα αντιμετώπισης"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Αριστερή συντόμευση"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Δεξιά συντόμευση"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index 9a01ac8e39ab..144777b30506 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desactivado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"No disponible"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"más información"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegación"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Diseño"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botón izquierdo adicional"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Algunas funciones se limitan durante el enfriamiento del teléfono.\nPresiona para obtener más información"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Tu teléfono intentará enfriarse automáticamente. Podrás usarlo, pero es posible que funcione más lento.\n\nUna vez que se haya enfriado, volverá a funcionar correctamente."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver pasos de mantenimiento"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Desenchufa el dispositivo"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"El puerto de carga del dispositivo se está calentando. Si está conectado a un cargador o accesorio USB, desenchúfalo con cuidado, ya que el cable también puede estar caliente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver pasos de mantenimiento"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Acceso directo izquierdo"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Acceso directo derecho"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 038c7b53c099..1ba2dc603d43 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desactivado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"No disponible"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"más información"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegación"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Diseño"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botón a la izquierda extra"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Se han limitado algunas funciones mientras el teléfono se enfría.\nToca para ver más información"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"El teléfono intentará enfriarse. Puedes seguir utilizándolo, pero es posible que funcione con mayor lentitud.\n\nUna vez que se haya enfriado, funcionará con normalidad."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver pasos de mantenimiento"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Desenchufa tu dispositivo"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Tu dispositivo se está calentando cerca del puerto de carga. Si está conectado a un cargador o a un accesorio USB, desenchúfalo con cuidado, ya que el cable también puede estar caliente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver pasos de mantenimiento"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Acceso directo a la izquierda"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Acceso directo a la derecha"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 731740453430..6c277081204d 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Sees"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Väljas"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Pole saadaval"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"lisateave"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigeerimisriba"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Paigutus"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Täiendava vasaku nupu tüüp"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Mõned funktsioonid on piiratud, kuni telefon jahtub.\nPuudutage lisateabe saamiseks."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Teie telefon proovib automaatselt maha jahtuda. Saate telefoni ikka kasutada, kuid see võib olla aeglasem.\n\nKui telefon on jahtunud, töötab see tavapäraselt."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Vaadake hooldusjuhiseid"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Eemaldage seade"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Teie seade läheb laadimispordi juurest soojaks. Kui see on ühendatud laadija või USB-tarvikuga, eemaldage see ja olge ettevaatlik, kuna kaabel võib samuti soe olla."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Vaadake hooldusjuhiseid"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Vasak otsetee"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Parem otsetee"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index a76109854f3a..2e2d6b55c52f 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Eginbide batzuk ezingo dira erabili telefonoa hoztu arte.\nInformazio gehiago lortzeko, sakatu hau."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonoa automatikoki saiatuko da hozten. Hoztu bitartean, telefonoa erabiltzen jarrai dezakezu, baina mantsoago funtziona lezake.\n\nTelefonoaren tenperatura jaitsi bezain laster, ohi bezala funtzionatzen jarraituko du."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ikusi zaintzeko urratsak"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Deskonektatu gailua"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Gailua berotzen ari da kargatzeko atakaren inguruan. Kargagailu edo USB bidezko osagarri batera konektatuta badago, deskonekta ezazu kontuz, kablea ere beroa egongo baita agian."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ikusi zaintzeko urratsak"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Ezkerreko lasterbidea"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Eskuineko lasterbidea"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index e7aefda13a00..d2b3f7c2277a 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"روشن"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"خاموش"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"در دسترس نیست"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"بیشتر بدانید"</string>
<string name="nav_bar" msgid="4642708685386136807">"نوار پیمایش"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"طرح‌بندی"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"نوع دکمه منتهی‌الیه چپ"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"وقتی تلفن درحال خنک شدن است، بعضی از ویژگی‌ها محدود می‌شوند.\nبرای اطلاعات بیشتر ضربه بزنید"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"تلفنتان به‌طور خودکار سعی می‌کند خنک شود. همچنان می‌توانید از تلفنتان استفاده کنید، اما ممکن است کندتر عمل کند.\n\nوقتی تلفن خنک شد، عملکرد عادی‌اش از سرگرفته می‌شود."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"دیدن اقدامات محافظتی"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"دستگاه را جدا کنید"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"‏دستگاهتان کنار درگاه شارژ گرم شده است. اگر دستگاهتان به شارژر یا لوازم جانبی USB متصل است، آن را جدا کنید و مراقب باشید چون ممکن است کابل هم گرم باشد."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"مشاهده مراحل احتیاط"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"میان‌بر چپ"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"میان‌بر راست"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 21c8dfc17cf0..6817033ee8c1 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Päällä"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Pois päältä"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Ei käytettävissä"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"lukeaksesi lisää"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigointipalkki"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Asettelu"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ylimääräinen vasen painiketyyppi"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Joidenkin ominaisuuksien käyttöä on rajoitettu puhelimen jäähtymisen aikana.\nLue lisää napauttamalla"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Puhelimesi yrittää automaattisesti jäähdyttää itsensä. Voit silti käyttää puhelinta, mutta se voi toimia hitaammin.\n\nKun puhelin on jäähtynyt, se toimii normaalisti."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Katso huoltovaiheet"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Irrota laite"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Laite lämpenee latausportin lähellä. Jos laite on yhdistetty laturiin tai USB-lisälaitteeseen, irrota se varoen, sillä johtokin voi olla lämmin."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Katso huoltovaiheet"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Vasen pikakuvake"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Oikea pikakuvake"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index b3439ae969a9..9a3e2d121068 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Certaines fonctionnalités sont limitées pendant que le téléphone refroidit.\nTouchez pour en savoir plus"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Votre téléphone va essayer de se refroidir automatiquement. Vous pouvez toujours l\'utiliser, mais il risque d\'être plus lent.\n\nUne fois refroidi, il fonctionnera normalement."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Afficher les étapes d\'entretien"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Débranchez votre appareil"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Votre appareil chauffe près du port de recharge. S\'il est connecté à un chargeur ou à un accessoire USB, débranchez-le en faisant attention : le câble pourrait également être chaud."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Afficher les étapes d\'entretien"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Raccourci gauche"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Raccourci droit"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 2ffb38934b5b..df5ded35e2fa 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activé"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Désactivé"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponible"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"en savoir plus"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barre de navigation"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Disposition"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Type de bouton gauche supplémentaire"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Fonctionnalités limitées pendant le refroidissement du téléphone.\nAppuyer pour en savoir plus"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Votre téléphone va essayer de se refroidir automatiquement. Vous pouvez toujours l\'utiliser, mais il risque d\'être plus lent.\n\nUne fois refroidi, il fonctionnera normalement."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Afficher les étapes d\'entretien"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Débrancher votre appareil"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Votre appareil se réchauffe près du port de recharge. S\'il est connecté à un chargeur ou un accessoire USB, débranchez-le en faisant attention, car le câble peut lui aussi être chaud."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Afficher les étapes d\'entretien"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Raccourci gauche"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Raccourci droit"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 2da351a0dca2..390742035ba2 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Activado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desactivado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Non dispoñible"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"obter máis información"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegación"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Deseño"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botón adicional á esquerda"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"O uso dalgunhas funcións é limitado mentres o teléfono arrefría.\nToca para obter máis información"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"O teléfono tentará arrefriar automaticamente. Podes utilizalo, pero é probable que funcione máis lento.\n\nUnha vez que arrefríe, funcionará con normalidade."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver pasos de mantemento"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Desconectar o dispositivo"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"O dispositivo estase quentando cerca do porto de carga. Se está conectado a un cargador ou a un accesorio USB, desconéctao con coidado, xa que o cable tamén podería estar quente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver pasos de mantemento"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Atallo á esquerda"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Atallo á dereita"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 97fed2367825..b362597f9133 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ફોન ઠંડો થાય ત્યાં સુધી અમુક સુવિધાઓ મર્યાદિત હોય છે.\nવધુ માહિતી માટે ટૅપ કરો"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"તમારો ફોન ઑટોમૅટિક રીતે ઠંડો થવાનો પ્રયાસ કરશે. તમે હજી પણ તમારા ફોનનો ઉપયોગ કરી શકો છો, પરંતુ તે કદાચ થોડો ધીમો ચાલે.\n\nતમારો ફોન ઠંડો થઈ જવા પર, તે સામાન્ય રીતે ચાલશે."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"સારસંભાળના પગલાં જુઓ"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"તમારા ડિવાઇસને અનપ્લગ કરો"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"તમારું ડિવાઇસ ચાર્જિંગ પોર્ટની પાસે ગરમ થઈ રહ્યું છે. જો તે કોઈ ચાર્જર અથવા USB ઍક્સેસરી સાથે કનેક્ટેડ હોય, તો તેને અનપ્લગ કરો અને ધ્યાન રાખો, કારણ કે કેબલ ગરમ પણ હોઈ શકે છે."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"સારસંભાળના પગલાં જુઓ"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"ડાબો શૉર્ટકટ"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"જમણો શૉર્ટકટ"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 81a11b425fbd..4d9860adb8f9 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"चालू"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"बंद"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"उपलब्ध नहीं है"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ज़्यादा जानें"</string>
<string name="nav_bar" msgid="4642708685386136807">"नेविगेशन बार"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"लेआउट"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"कुछ और बाएं बटन के प्रकार"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"फ़ोन के ठंडा होने तक कुछ सुविधाएं काम नहीं करतीं.\nज़्यादा जानकारी के लिए टैप करें"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"आपका फ़ोन अपने आप ठंडा होने की कोशिश करेगा. आप अब भी अपने फ़ोन का उपयोग कर सकते हैं, लेकिन हो सकता है कि यह धीमी गति से चले.\n\nठंडा हो जाने पर आपका फ़ोन सामान्य रूप से चलेगा."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"डिवाइस के रखरखाव के तरीके देखें"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"डिवाइस को अनप्लग करें"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"आपका डिवाइस चार्जिंग पोर्ट के पास गर्म हो रहा है. अगर डिवाइस चार्जर या यूएसबी ऐक्सेसरी से कनेक्ट है, तो उसे अनप्लग करें. साथ ही, ध्यान रखें कि केबल भी गर्म हो सकती है."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"प्रबंधन से जुड़े चरण देखें"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"बायां शॉर्टकट"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"दायां शॉर्टकट"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 7ef30b6886c7..b5221487da21 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Uključeno"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Isključeno"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nedostupno"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"saznajte više"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigacijska traka"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Izgled"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Vrsta dodatnog lijevog gumba"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Neke su značajke ograničene dok se telefon ne ohladi.\nDodirnite za više informacija"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon će se automatski pokušati ohladiti. Možete ga nastaviti koristiti, no mogao bi raditi sporije.\n\nKad se ohladi, radit će normalno."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Pročitajte upute za održavanje"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Iskopčajte uređaj"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Vaš se uređaj zagrijava u blizini priključka za punjenje. Ako je priključen u punjač ili USB uređaj, iskopčajte ga. Pazite jer se i kabel možda zagrijao."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Pogledajte upute za održavanje"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Lijevi prečac"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Desni prečac"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index b6bfc1b3946e..e7efd9dcb3cd 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Be"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Ki"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nem használható"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"további információ"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigációs sáv"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Elrendezés"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"További bal oldali gombtípus"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Bizonyos funkciók korlátozottan működnek a telefon lehűlése közben.\nKoppintson, ha további információra van szüksége."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"A telefon automatikusan megpróbál lehűlni. Továbbra is tudja használni a telefont, de elképzelhető, hogy működése lelassul.\n\nAmint a telefon lehűl, újra a szokásos módon működik majd."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Olvassa el a kímélő használat lépéseit"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Húzza ki az eszközt"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Eszköze kezd melegedni a töltőport közelében. Ha töltő vagy USB-s kiegészítő van csatlakoztatva hozzá, húzza ki, és legyen óvatos, mert a kábel is meleg lehet."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Olvassa el a megfelelő használat lépéseit"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Bal oldali parancsikon"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Jobb oldali parancsikon"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index 038030350c1b..804f350052fc 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Միացված է"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Անջատված է"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Հասանելի չէ"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"Իմանալ ավելին"</string>
<string name="nav_bar" msgid="4642708685386136807">"Նավիգացիայի գոտի"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Դասավորություն"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Լրացուցիչ ձախ կոճակի տեսակ"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Հովանալու ընթացքում հեռախոսի որոշ գործառույթներ սահմանափակ են։\nՀպեք՝ ավելին իմանալու համար։"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ձեր հեռախոսն ավտոմատ կերպով կփորձի hովանալ: Կարող եք շարունակել օգտագործել հեռախոսը, սակայն հնարավոր է, որ այն ավելի դանդաղ աշխատի:\n\nՀովանալուց հետո հեռախոսը կաշխատի կանոնավոր կերպով:"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Քայլեր գերտաքացման ահազանգի դեպքում"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Անջատեք սարքը"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Լիցքավորման միացքի հատվածում սարքը տաքանում է։ Եթե լիցքավորիչի կամ USB լրասարքի է միացված սարքը, անջատեք այն և զգույշ եղեք, քանի որ մալուխը ևս կարող է տաքացած լինել։"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Քայլեր գերտաքացման ահազանգի դեպքում"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Ձախ դյուրանցում"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Աջ դյուրանցում"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 163dd9f94f2f..da8b3c0c9010 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Aktif"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Nonaktif"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Tidak tersedia"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"pelajari lebih lanjut"</string>
<string name="nav_bar" msgid="4642708685386136807">"Bilah navigasi"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Tata Letak"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Jenis tombol ekstra kiri"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Beberapa fitur dibatasi saat ponsel mendingin.\nKetuk untuk info selengkapnya"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ponsel akan otomatis mencoba mendingin. Anda tetap dapat menggunakan ponsel, tetapi mungkin berjalan lebih lambat.\n\nSetelah dingin, ponsel akan berjalan seperti biasa."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Lihat langkah-langkah perawatan"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Cabut perangkat"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Perangkat menjadi panas saat di dekat port pengisi daya. Jika perangkat terhubung ke pengisi daya atau aksesori USB, cabutlah dan berhati-hatilah karena suhu kabel mungkin juga panas."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Lihat langkah-langkah perawatan"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Pintasan kiri"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Pintasan kanan"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index f2a0304a4844..7f18abfe8594 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Kveikt"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Slökkt"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Ekki í boði"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"nánar"</string>
<string name="nav_bar" msgid="4642708685386136807">"Yfirlitsstika"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Útlit"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Gerð aukahnapps til vinstri"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Sumir eiginleikar eru takmarkaðir meðan síminn kælir sig.\nÝttu til að fá frekari upplýsingar"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Síminn reynir sjálfkrafa að kæla sig. Þú getur enn notað símann en hann gæti verið hægvirkari.\n\nEftir að síminn hefur kælt sig niður virkar hann eðlilega."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Sjá varúðarskref"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Taktu tækið úr sambandi"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Tækið er að hitna nálægt hleðslutenginu. Ef það er tengt við hleðslutæki eða USB-aukahlut skaltu taka það úr sambandi og hafa í huga að snúran gæti einnig verið heit."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Sjá varúðarskref"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Flýtilykill til vinstri"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Flýtilykill til hægri"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 74f3e475555b..83c58bc65dce 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"On"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Off"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Non disponibile"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"scopri di più"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra di navigazione"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo di pulsante extra sinistra"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Alcune funzionalità limitate durante il raffreddamento del telefono.\nTocca per ulteriori informazioni"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Il telefono cercherà automaticamente di raffreddarsi. Puoi comunque usarlo, ma potrebbe essere più lento.\n\nUna volta raffreddato, il telefono funzionerà normalmente."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Leggi le misure da adottare"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Scollega il dispositivo"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Il tuo dispositivo si sta scaldando vicino alla porta di ricarica. Se è collegato a un caricabatterie o a un accessorio USB, scollegalo e fai attenzione perché il cavo potrebbe essere caldo."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Leggi le misure da adottare"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Scorciatoia sinistra"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Scorciatoia destra"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 4e0d5cebc030..09d5d69716ee 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ჩართული"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"გამორთვა"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"მიუწვდომელი"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"შეიტყვეთ მეტი"</string>
<string name="nav_bar" msgid="4642708685386136807">"ნავიგაციის ზოლი"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"განლაგება"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"მარცხენა დამატებითი ღილაკის ტიპი"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ზოგიერთი ფუნქცია შეზღუდული იქნება, სანამ ტელეფონი გაგრილდება.\nშეეხეთ დამატებითი ინფორმაციის მისაღებად"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"თქვენი ტელეფონი გაგრილებას ავტომატურად შეეცდება. შეგიძლიათ გააგრძელოთ მისით სარგებლობა, თუმცა ტელეფონმა შეიძლება უფრო ნელა იმუშაოს.\n\nგაგრილების შემდგომ ის ჩვეულებრივად იმუშავებს."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"მისაღები ზომების გაცნობა"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"გამოაერᲗეᲗ Თქვენი მოწყობილობა"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"თქვენი მოწყობილობა ხურდება დამტენის პორტთან ახლოს. თუ ის დაკავშირებულია დამტენთან ან USB აქსესუართან, გამორთეთ იგი და იზრუნეთ, რადგან შესაძლოა კაბელიც გახურებული იყოს."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"მისაღები ზომების გაცნობა"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"მარცხენა მალსახმობი"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"მარჯვენა მალსახმობი"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 10c47e39164b..1183e381d04e 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -161,10 +161,8 @@
<string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Бет танылмады. Орнына саусақ ізін пайдаланыңыз."</string>
<!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
<skip />
- <!-- no translation found for keyguard_face_failed (9044619102286917151) -->
- <skip />
- <!-- no translation found for keyguard_suggest_fingerprint (8742015961962702960) -->
- <skip />
+ <string name="keyguard_face_failed" msgid="9044619102286917151">"Бет танылмады."</string>
+ <string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"Орнына саусақ ізін пайдаланыңыз."</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"Bluetooth қосылған."</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"Батарея зарядының мөлшері белгісіз."</string>
<string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> қосылған."</string>
@@ -198,9 +196,9 @@
<string name="accessibility_quick_settings_less_time" msgid="9110364286464977870">"Азырақ уақыт."</string>
<string name="accessibility_casting_turned_off" msgid="1387906158563374962">"Экранды трансляциялау тоқтатылды."</string>
<string name="accessibility_brightness" msgid="5391187016177823721">"Дисплей жарықтығы"</string>
- <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобильдік деректер кідіртілді"</string>
+ <string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Мобильдік интернет кідіртілді"</string>
<string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Деректер кідіртілді"</string>
- <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"Белгіленген деректер шегіне жеттіңіз. Мобильдік деректер енді пайдаланылмайды.\n\nЕгер жалғастырсаңыз, деректер трафигі үшін ақы алынуы мүмкін."</string>
+ <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"Белгіленген деректер шегіне жеттіңіз. Мобильдік интернет енді пайдаланылмайды.\n\nЕгер жалғастырсаңыз, деректер трафигі үшін ақы алынуы мүмкін."</string>
<string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"Жалғастыру"</string>
<string name="accessibility_location_active" msgid="2845747916764660369">"Орын өтініштері қосылған"</string>
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Датчиктер өшірулі."</string>
@@ -257,7 +255,7 @@
<string name="quick_settings_hotspot_secondary_label_num_devices" msgid="7536823087501239457">"{count,plural, =1{# құрылғы}other{# құрылғы}}"</string>
<string name="quick_settings_flashlight_label" msgid="4904634272006284185">"Қалта шам"</string>
<string name="quick_settings_flashlight_camera_in_use" msgid="4820591564526512571">"Камера қолданылып жатыр"</string>
- <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобильдік деректер"</string>
+ <string name="quick_settings_cellular_detail_title" msgid="792977203299358893">"Мобильдік интернет"</string>
<string name="quick_settings_cellular_detail_data_usage" msgid="6105969068871138427">"Дерек шығыны"</string>
<string name="quick_settings_cellular_detail_remaining_data" msgid="1136599216568805644">"Қалған деректер"</string>
<string name="quick_settings_cellular_detail_over_limit" msgid="4561921367680636235">"Шектен асу"</string>
@@ -600,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Қосулы"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Өшірулі"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Қолжетімді емес"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"толығырақ"</string>
<string name="nav_bar" msgid="4642708685386136807">"Шарлау тақтасы"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Формат"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Қосымша сол жақ түйме түрі"</string>
@@ -674,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Телефон толық суығанға дейін, кейбір функциялардың жұмысы шектеледі.\nТолығырақ ақпарат үшін түртіңіз."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефон автоматты түрде суи бастайды. Оны пайдалана бере аласыз, бірақ ол баяуырақ жұмыс істеуі мүмкін.\n\nТелефон суығаннан кейін, оның жұмысы қалпына келеді."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Пайдалану нұсқаулығын қараңыз"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Құрылғыны ажыратыңыз"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Құрылғының зарядтау ұяшығы тұрған бөлігі қызып келеді. Зарядтағышқа немесе USB құрылғысына жалғанған болса, оны ажыратыңыз. Абайлаңыз, кабель де ыстық болуы мүмкін."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Пайдалану нұсқаулығын қараңыз"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Сол жақ таңбаша"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Оң жақ таңбаша"</string>
@@ -717,7 +712,7 @@
<string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Мазаламау режимі автоматты ереже немесе қолданба арқылы қосылды."</string>
<string name="running_foreground_services_title" msgid="5137313173431186685">"Фонда жұмыс істеп тұрған қолданбалар"</string>
<string name="running_foreground_services_msg" msgid="3009459259222695385">"Батарея мен деректер трафигі туралы білу үшін түртіңіз"</string>
- <string name="mobile_data_disable_title" msgid="5366476131671617790">"Мобильдік деректер өшірілсін бе?"</string>
+ <string name="mobile_data_disable_title" msgid="5366476131671617790">"Мобильдік интернет өшірілсін бе?"</string>
<string name="mobile_data_disable_message" msgid="8604966027899770415">"<xliff:g id="CARRIER">%s</xliff:g> операторы арқылы деректерге немесе интернетке кіре алмайсыз. Интернетке тек Wi-Fi арқылы кіресіз."</string>
<string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"операторыңыз"</string>
<string name="touch_filtered_warning" msgid="8119511393338714836">"Басқа қолданба рұқсат сұрауын жасырып тұрғандықтан, параметрлер жауабыңызды растай алмайды."</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index 7ee2a9399fb9..c30c2a26c595 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"បើក"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"បិទ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"មិនអាចប្រើបាន"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ស្វែងយល់​បន្ថែម"</string>
<string name="nav_bar" msgid="4642708685386136807">"របាររុករក"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ប្លង់"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ប្រភេទ​ប៊ូតុង​ខាង​ឆ្វេង​បន្ថែម"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"មុខងារ​មួយចំនួន​នឹងមិនអាច​ប្រើបានពេញលេញ​នោះទេ ខណៈពេល​ដែលទូរសព្ទ​កំពុងបញ្ចុះកម្ដៅ។\nសូមចុច​ដើម្បីទទួលបាន​ព័ត៌មានបន្ថែម"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ទូរសព្ទ​របស់អ្នក​នឹង​ព្យាយាម​បញ្ចុះ​កម្តៅ​ដោយ​ស្វ័យប្រវត្តិ។ អ្នក​នៅតែ​អាច​ប្រើ​ទូរសព្ទ​របស់អ្នក​បាន​ដដែល​ ប៉ុន្តែ​វា​នឹង​ដំណើរ​ការ​យឺត​ជាង​មុន។\n\nបន្ទាប់​ពី​ទូរសព្ទ​របស់អ្នក​ត្រជាក់​ជាង​មុន​ហើយ វា​នឹង​ដំណើរការ​ដូច​ធម្មតា។"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"មើលជំហាន​ថែទាំ"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"ដកឧបករណ៍របស់អ្នក"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"ឧបករណ៍របស់អ្នកកំពុងឡើងកម្ដៅនៅជិតរន្ធសាកថ្ម។ ប្រសិនបើឧបករណ៍នេះត្រូវបានភ្ជាប់ទៅឆ្នាំង​សាក ឬគ្រឿងបរិក្ខារ USB សូមដកវា និងមានការប្រុងប្រយ័ត្ន ដោយសារខ្សែក៏អាចក្ដៅផងដែរ។"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"មើលជំហាន​ថែទាំ"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"ផ្លូវកាត់​ខាង​ឆ្វេង"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"ផ្លូវកាត់​ខាង​ស្តាំ"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 2aec2e305fa4..f32d00a6cc6a 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ಆನ್"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ಆಫ್"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ಲಭ್ಯವಿಲ್ಲ"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ಇನ್ನಷ್ಟು ತಿಳಿಯಿರಿ"</string>
<string name="nav_bar" msgid="4642708685386136807">"ನ್ಯಾವಿಗೇಷನ್ ಬಾರ್"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ಲೇಔಟ್"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ಹೆಚ್ಚುವರಿ ಎಡ ಬಟನ್ ವಿಧ"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ಫೋನ್ ತಣ್ಣಗಾಗುವವರೆಗೂ ಕೆಲವು ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಸೀಮಿತಗೊಳಿಸಲಾಗುತ್ತದೆ\nಇನ್ನಷ್ಟು ಮಾಹಿತಿಗಾಗಿ ಟ್ಯಾಪ್ ಮಾಡಿ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ನಿಮ್ಮ ಫೋನ್ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ತಣ್ಣಗಾಗಲು ಪ್ರಯತ್ನಿಸುತ್ತದೆ. ನಿಮ್ಮ ಫೋನ್ ಅನ್ನು ನೀವು ಈಗಲೂ ಬಳಸಬಹುದಾಗಿರುತ್ತದೆ, ಆದರೆ ಇದು ನಿಧಾನವಾಗಿರಬಹುದು.\n\nಒಮ್ಮೆ ನಿಮ್ಮ ಫೋನ್ ತಣ್ಣಗಾದ ನಂತರ ಇದು ಸಾಮಾನ್ಯ ರೀತಿಯಲ್ಲಿ ಕಾರ್ಯನಿರ್ವಹಿಸುತ್ತದೆ."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ಕಾಳಜಿಯ ಹಂತಗಳನ್ನು ವೀಕ್ಷಿಸಿ"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"ನಿಮ್ಮ ಸಾಧನವನ್ನು ಅನ್‌ಪ್ಲಗ್ ಮಾಡಿ"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"ಚಾರ್ಜಿಂಗ್ ಪೋರ್ಟ್ ಸಮೀಪದಲ್ಲಿ ನಿಮ್ಮ ಸಾಧನವು ಬಿಸಿಯಾಗುತ್ತಿದೆ. ಅದನ್ನು ಚಾರ್ಜರ್ ಅಥವಾ USB ಪರಿಕರಕ್ಕೆ ಕನೆಕ್ಟ್ ಮಾಡಿದ್ದರೆ, ಅದನ್ನು ಅನ್‌ಪ್ಲಗ್ ಮಾಡಿ ಹಾಗೂ ಕೇಬಲ್ ಕೂಡ ಬಿಸಿಯಾಗಿರಬಹುದು ಆದ್ದರಿಂದ ಎಚ್ಚರಿಕೆ ವಹಿಸಿ."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ಕಾಳಜಿ ಹಂತಗಳನ್ನು ನೋಡಿ"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"ಎಡ ಶಾರ್ಟ್‌ಕಟ್"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"ಬಲ ಶಾರ್ಟ್‌ಕಟ್"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index b33d766e1986..72e837b39ef2 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"사용"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"사용 안함"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"사용할 수 없음"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"자세히 알아보기"</string>
<string name="nav_bar" msgid="4642708685386136807">"탐색 메뉴"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"레이아웃"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"추가 왼쪽 버튼 유형"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"휴대전화 온도를 낮추는 동안 일부 기능이 제한됩니다.\n자세히 알아보려면 탭하세요."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"휴대전화 온도를 자동으로 낮추려고 시도합니다. 휴대전화를 계속 사용할 수는 있지만 작동이 느려질 수도 있습니다.\n\n휴대전화 온도가 낮아지면 정상적으로 작동됩니다."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"해결 방법 확인하기"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"기기 분리하기"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"기기의 충전 포트 주변 온도가 상승하고 있습니다. 충전기나 USB 액세서리가 연결된 상태라면 분리하세요. 이때 케이블도 뜨거울 수 있으므로 주의하시기 바랍니다."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"취해야 할 조치 확인"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"왼쪽 바로가기"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"오른쪽 바로가기"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index b352277792a9..e6e3818f1c4f 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Күйүк"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Өчүк"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Жеткиликсиз"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"кеңири маалымат"</string>
<string name="nav_bar" msgid="4642708685386136807">"Чабыттоо тилкеси"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Калып"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Сол жактагы кошумча баскычтын түрү"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Телефон сууганча айрым элементтердин иши чектелген.\nКеңири маалымат алуу үчүн таптап коюңуз"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефонуңуз автоматтык түрдө сууйт. Аны колдоно берсеңиз болот, бирок ал жайыраак иштеп калат.\n\nТелефонуңуз суугандан кийин адаттагыдай эле иштеп баштайт."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Тейлөө кадамдарын көрүңүз"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Түзмөктү сууруп коюңуз"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Түзмөгүңүздүн кубаттоо порту жылып баратат. Эгер түзмөгүңүз кубаттагычка же USB кабелине туташып турса, аны сууруп коюңуз. Абайлаңыз, кабель да жылуу болушу мүмкүн."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Тейлөө кадамдарын көрүңүз"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Сол жактагы кыска жол"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Оң жактагы кыска жол"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index f90e869e7b27..5a97ca5dbe83 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ເປີດ"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ປິດ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ບໍ່ສາມາດໃຊ້ໄດ້"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ສຶກສາເພີ່ມເຕີມ"</string>
<string name="nav_bar" msgid="4642708685386136807">"ແຖບນຳທາງ"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ຮູບແບບ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ປະເພດປຸ່ມຊ້າຍພິເສດ"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ຄຸນສົມບັດບາງຢ່າງຖືກຈຳກັດໄວ້ໃນເວລາຫຼຸດອຸນຫະພູມຂອງໂທລະສັບ.\nແຕະເພື່ອເບິ່ງຂໍ້ມູນເພີ່ມເຕີມ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ໂທລະສັບຂອງທ່ານຈະພະຍາຍາມລົດອຸນຫະພູມລົງ. ທ່ານຍັງຄົງສາມາດໃຊ້ໂທລະສັບຂອງທ່ານໄດ້ຢູ່, ແຕ່ມັນຈະເຮັດວຽກຊ້າລົງ.\n\nເມື່ອໂທລະສັບຂອງທ່ານບໍ່ຮ້ອນຫຼາຍແລ້ວ, ມັນຈະກັບມາເຮັດວຽກຕາມປົກກະຕິ."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ເບິ່ງຂັ້ນຕອນການເບິ່ງແຍງ"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"ຖອດອຸປະກອນຂອງທ່ານອອກ"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"ອຸປະກອນຂອງທ່ານຈະອຸ່ນຂຶ້ນເມື່ອຢູ່ໃກ້ຊ່ອງສາກໄຟ. ຫາກມັນເຊື່ອມຕໍ່ຫາສາຍສາກ ຫຼື ອຸປະກອນເສີມ USB ໃດໜຶ່ງຢູ່, ໃຫ້ຖອດມັນອອກ ແລະ ລະວັງເນື່ອງຈາກສາຍກໍອາດຈະອຸ່ນເຊັ່ນກັນ."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ເບິ່ງຂັ້ນຕອນການເບິ່ງແຍງ"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"ປຸ່ມລັດຊ້າຍ"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"ປຸ່ມລັດຂວາ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index fdf45707ca43..0e641c7b3c15 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Įjungta"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Išjungta"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nepasiekiama"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"sužinoti daugiau"</string>
<string name="nav_bar" msgid="4642708685386136807">"Naršymo juosta"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Išdėstymas"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Papildomo mygtuko kairėje tipas"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Kai kurios funkcijos gali neveikti, kol telefonas vėsta.\nPalietę gausite daugiau informacijos"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonas automatiškai bandys atvėsti. Telefoną vis tiek galėsite naudoti, tačiau jis gali veikti lėčiau.\n\nKai telefonas atvės, jis veiks įprastai."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Žr. priežiūros veiksmus"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Atjunkite įrenginį"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Įrenginys kaista šalia įkrovimo prievado. Jei jis prijungtas prie kroviklio ar USB priedo, atjunkite jį ir patikrinkite, nes laidas taip pat gali būti įkaitęs."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Žr. priežiūros veiksmus"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Spartusis klavišas kairėje"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Spartusis klavišas dešinėje"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 9ca7da534145..07ae2c948a97 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ieslēgts"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Izslēgts"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nav pieejams"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"uzzinātu vairāk"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigācijas josla"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Izkārtojums"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Kreisās puses papildu pogas veids"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Dažas funkcijas ir ierobežotas, kamēr notiek tālruņa atdzišana.\nPieskarieties, lai uzzinātu vairāk."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Jūsu tālrunis automātiski mēģinās atdzist. Jūs joprojām varat izmantot tālruni, taču tas, iespējams, darbosies lēnāk.\n\nTiklīdz tālrunis būs atdzisis, tas darbosies normāli."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Skatīt apkopes norādījumus"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Atvienojiet savu ierīci"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Jūsu ierīce uzkarst, atrodoties uzlādes pieslēgvietas tuvumā. Ja ierīce ir pievienota lādētājam vai USB piederumam, uzmanīgi atvienojiet to, jo arī vads var būt uzkarsis."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Skatīt apkopes norādījumus"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Saīsne kreisajā pusē"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Saīsne labajā pusē"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index a241e54cd855..dc12d0905d98 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Некои функции се ограничени додека телефонот се лади.\nДопрете за повеќе информации"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефонот автоматски ќе се обиде да се олади. Вие сепак ќе може да го користите, но тој може да работи побавно.\n\nОткако ќе се олади, ќе работи нормално."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Прикажи ги чекорите за грижа за уредот"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Исклучете го уредот од кабел"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Вашиот уред се загрева во близина на портата за полнење. Ако е поврзан со полнач или USB-додаток, исклучете го од него и внимавајте бидејќи кабелот може да е топол."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Прикажи ги чекорите за грижа за уредот"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Лева кратенка"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Десна кратенка"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 4ff230181581..17fa9ea3536b 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ഓൺ"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ഓഫ്"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ലഭ്യമല്ല"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"കൂടുതലറിയുക"</string>
<string name="nav_bar" msgid="4642708685386136807">"നാവിഗേഷൻ ബാർ"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ലേ‌ഔട്ട്"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"അധിക ഇടത് ബട്ടൺ തരം"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ഫോൺ തണുത്തുകൊണ്ടിരിക്കുമ്പോൾ ചില ഫീച്ചറുകൾ പരിമിതപ്പെടുത്തപ്പെടും.\nകൂടുതൽ വിവരങ്ങൾക്ക് ടാപ്പ് ചെയ്യുക"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"നിങ്ങളുടെ ഫോൺ സ്വയമേവ തണുക്കാൻ ശ്രമിക്കും. നിങ്ങൾക്ക് അപ്പോഴും ഫോൺ ഉപയോഗിക്കാമെങ്കിലും പ്രവർത്തനം മന്ദഗതിയിലായിരിക്കും.\n\nതണുത്തുകഴിഞ്ഞാൽ, ഫോൺ സാധാരണ ഗതിയിൽ പ്രവർത്തിക്കും."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"പരിപാലന നിർദ്ദേശങ്ങൾ കാണുക"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"ഉപകരണം അൺപ്ലഗ് ചെയ്യുക"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"ചാർജിംഗ് പോർട്ടിന് സമീപം നിങ്ങളുടെ ഉപകരണം ചൂടാകുന്നുണ്ട്. ഇത് ചാർജറിലേക്കോ USB ആക്‌സസറിയിലേക്കോ കണക്‌റ്റ് ചെയ്‌തിട്ടുണ്ടെങ്കിൽ അൺപ്ലഗ് ചെയ്യുക, കേബിളും ചൂടായിരിക്കാമെന്നതിനാൽ ശ്രദ്ധിക്കണം."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"മുൻകരുതൽ നടപടികൾ കാണുക"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"ഇടത് കുറുക്കുവഴി"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"വലത് കുറുക്കുവഴി"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index a3d13bd447de..751ef19222f4 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Утсыг хөрөх үед зарим онцлогийг хязгаарлана.\nДэлгэрэнгүй мэдээлэл авах бол товшино уу"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Таны утас автоматаар хөрөх болно. Та утсаа ашиглаж болох хэдий ч удаан ажиллаж болзошгүй.\n\nТаны утас хөрсний дараагаар хэвийн ажиллана."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Хянамж болгоомжийн алхмыг харах"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Төхөөрөмжөө салгана уу"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Таны төхөөрөмж цэнэглэх портын ойролцоо халж байна. Хэрэв төхөөрөмжийг цэнэглэгч эсвэл USB дагалдах хэрэгсэлд холбосон бол төхөөрөмжийг салгаж, кабель нь халуун байж болзошгүй тул болгоомжтой байгаарай."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Хянамж болгоомжийн алхмыг харна уу"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Зүүн товчлол"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Баруун товчлол"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index c9f3bc27cdc2..ab8d9527b883 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -327,9 +327,9 @@
<string name="do_disclosure_generic" msgid="4896482821974707167">"हे डिव्हाइस तुमच्या संस्थेचे आहे"</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> चे आहे"</string>
<string name="do_financed_disclosure_with_name" msgid="6723004643314467864">"हे डिव्हाइस <xliff:g id="ORGANIZATION_NAME">%s</xliff:g> द्वारे पुरवले गेले आहे"</string>
- <string name="phone_hint" msgid="6682125338461375925">"फोनसाठी चिन्हावरून स्वाइप करा"</string>
- <string name="voice_hint" msgid="7476017460191291417">"व्हॉइस सहाय्यासाठी चिन्हावरून स्वाइप करा"</string>
- <string name="camera_hint" msgid="4519495795000658637">"कॅमेर्‍यासाठी चिन्हावरून स्वाइप करा"</string>
+ <string name="phone_hint" msgid="6682125338461375925">"फोनसाठी आयकनवरून स्वाइप करा"</string>
+ <string name="voice_hint" msgid="7476017460191291417">"व्हॉइस सहाय्यासाठी आयकनवरून स्वाइप करा"</string>
+ <string name="camera_hint" msgid="4519495795000658637">"कॅमेर्‍यासाठी आयकनवरून स्वाइप करा"</string>
<string name="interruption_level_none_with_warning" msgid="8394434073508145437">"संपूर्ण शांतता. हे स्क्रीन रीडर ना देखील शांत करेल."</string>
<string name="interruption_level_none" msgid="219484038314193379">"संपूर्ण शांतता"</string>
<string name="interruption_level_priority" msgid="661294280016622209">"केवळ प्राधान्य"</string>
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"सुरू"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"बंद"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"उपलब्ध नाही"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"अधिक जाणून घ्या"</string>
<string name="nav_bar" msgid="4642708685386136807">"नॅव्हिगेशन बार"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"लेआउट"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"अतिरिक्त डाव्या बटणाचा प्रकार"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"फोन थंड होईपर्यंत काही वैशिष्ट्ये मर्यादित केली.\nअधिक माहितीसाठी टॅप करा"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"तुमचा फोन स्वयंचलितपणे थंड होईल. तुम्ही अद्यापही तुमचा फोन वापरू शकता परंतु तो कदाचित धीमेपणे कार्य करेल.\n\nतुमचा फोन एकदा थंड झाला की, तो सामान्यपणे कार्य करेल."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"काय काळजी घ्यावी ते पहा"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"तुमचे डिव्हाइस अनप्लग करा"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"तुमचे डिव्हाइस हे चार्जिंग पोर्टच्या जवळ गरम होत आहे. हे चार्जर किंवा USB अ‍ॅक्सेसरी यांच्याशी कनेक्ट केलेले असल्यास, ते अनप्लग करा आणि काळजी घ्या कारण केबलदेखील गरम असू शकते."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"काय काळजी घ्यावी ते पहा"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"डावा शॉर्टकट"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"उजवा शॉर्टकट"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index b161345a3d9c..11b788c1ae6e 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Hidup"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Mati"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Tidak tersedia"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"mengetahui lebih lanjut"</string>
<string name="nav_bar" msgid="4642708685386136807">"Bar navigasi"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Reka letak"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Jenis butang kiri tambahan"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Sesetengah ciri adalah terhad semasa telefon menyejuk.\nKetik untuk mendapatkan maklumat lanjut"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon anda akan cuba menyejuk secara automatik. Anda masih dapat menggunakan telefon itu tetapi telefon tersebut mungkin berjalan lebih perlahan.\n\nSetelah telefon anda sejuk, telefon itu akan berjalan seperti biasa."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Lihat langkah penjagaan"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Cabut palam peranti anda"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Peranti anda menjadi panas berdekatan port pengecasan. Jika peranti anda disambungkan ke pengecas atau aksesori USB, cabut palam peranti dan berhati-hati kerana kabel juga mungkin panas."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Lihat langkah penjagaan"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Pintasan kiri"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Pintasan kanan"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 3f201ac37ded..e87be582afaf 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ဖွင့်"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ပိတ်"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"မရနိုင်ပါ"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ပိုမိုလေ့လာရန်"</string>
<string name="nav_bar" msgid="4642708685386136807">"ရွှေ့လျားရန်ဘားတန်း"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"အပြင်အဆင်"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"လက်ဝဲခလုတ် အမျိုးအစားအပို"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ဖုန်းကို အေးအောင်ပြုလုပ်နေစဉ်တွင် အချို့ဝန်ဆောင်မှုများကို ကန့်သတ်ထားပါသည်။\nနောက်ထပ်အချက်အလက်များအတွက် တို့ပါ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"သင့်ဖုန်းသည် အလိုအလျောက် ပြန်အေးသွားပါလိမ့်မည်။ ဖုန်းကို အသုံးပြုနိုင်ပါသေးသည် သို့သော် ပိုနှေးနိုင်ပါသည်။\n\nသင့်ဖုန်း အေးသွားသည်နှင့် ပုံမှန်အတိုင်း ပြန်အလုပ်လုပ်ပါလိမ့်မည်။"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ဂရုပြုစရာ အဆင့်များ ကြည့်ရန်"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"သင့်စက်ကို ပလတ်ဖြုတ်ပါ"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"သင့်စက်သည် အားသွင်းပို့တ်အနီးတွင် ပူနွေးလာသည်။ ၎င်းကို အားသွင်းကိရိယာ (သို့) USB ဆက်စပ်ပစ္စည်းနှင့် ချိတ်ဆက်ထားပါက ပလတ်ဖြုတ်ပါ။ ကြိုးကလည်း ပူနွေးနေနိုင်သဖြင့် ဂရုပြုပါ။"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ဂရုပြုစရာ အဆင့်များ ကြည့်ရန်"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"လက်ဝဲ ဖြတ်လမ်းလင့်ခ်"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"လက်ယာ ဖြတ်လမ်းလင့်ခ်"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index 24dcef7fdf76..22f3836fb2b4 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"På"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Av"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Utilgjengelig"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"finne ut mer"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigasjonsrad"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Oppsett"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra venstre-knapptype"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Enkelte funksjoner er begrenset mens telefonen kjøles ned.\nTrykk for å se mer informasjon"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonen din kommer til å prøve å kjøle seg ned automatisk. Du kan fremdeles bruke telefonen, men den kjører muligens saktere.\n\nTelefonen kommer til å kjøre som normalt, når den har kjølt seg ned."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Se vedlikeholdstrinnene"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Koble fra enheten"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Enheten begynner å bli varm nær ladeporten. Hvis den er koblet til en lader eller et USB-tilbehør, må du koble den fra. Vær forsiktig da kabelen også kan være varm."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Se vedlikeholdstrinnene"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Venstre hurtigtast"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Høyre hurtigtast"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index 4f85e3e9c0d0..eb7876589c56 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"अन छ"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"अफ छ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"उपलब्ध छैन"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"थप जान्नुहोस्"</string>
<string name="nav_bar" msgid="4642708685386136807">"नेभिगेशन पट्टी"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"लेआउट"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"अतिरिक्त बायाँतिरको बटनको प्रकार"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"फोन नचिस्सिँदासम्म केही सुविधाहरू उपलब्ध हुने छैनन्।\nथप जानकारीका लागि ट्याप गर्नुहोस्"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"तपाईंको फोन स्वतः चिसो हुने प्रयास गर्ने छ। तपाईं अझै पनि आफ्नो फोनको प्रयोग गर्न सक्नुहुन्छ तर त्यो अझ ढिलो चल्न सक्छ।\n\nचिसो भएपछि तपाईंको फोन सामान्य गतिमा चल्नेछ।"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"डिभाइसको हेरचाह गर्ने तरिका हेर्नुहोस्"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"डिभाइस बिजुलीको स्रोतबाट निकाल्नुहोस्"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"तपाईंको डिभाइसको चार्जिङ पोर्टतिरको भाग तातीरहेको छ। तपाईंको डिभाइस चार्जर वा USB एक्सेसरीमा जोडिएको गरिएको छ भने त्यसलाई निकाल्नुहोस्। यसका साथै सो केबल तातो हुन सक्ने भएकाले ख्याल गर्नुहोला।"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"हेरचाहसम्बन्धी चरणहरू हेर्नुहोस्‌"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"बायाँतिरको सर्टकट"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"दायाँतिरको सर्टकट"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index ee08b0146965..c6c63b566412 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Bepaalde functies zijn beperkt terwijl de telefoon afkoelt.\nTik voor meer informatie"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Je telefoon probeert automatisch af te koelen. Je kunt je telefoon nog steeds gebruiken, maar deze kan langzamer werken.\n\nZodra de telefoon is afgekoeld, werkt deze weer normaal."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Onderhoudsstappen bekijken"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Je apparaat loskoppelen"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Je apparaat wordt warm in de buurt van de oplaadpoort. Als het apparaat is aangesloten op een oplader of USB-poort, koppel je het los. Wees voorzichtig: de kabel kan warm zijn."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Onderhoudsstappen bekijken"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Snelkoppeling links"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Snelkoppeling rechts"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 6923145f93b5..d6f742c001e3 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -178,7 +178,7 @@
<string name="accessibility_overflow_action" msgid="8555835828182509104">"ସମସ୍ତ ବିଜ୍ଞପ୍ତି ଦେଖନ୍ତୁ"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"ଟେଲି-ଟାଇପରାଇଟର୍ ସକ୍ଷମ ଅଛି।"</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"ରିଙ୍ଗର୍‌ କମ୍ପନରେ ଅଛି।"</string>
- <string name="accessibility_ringer_silent" msgid="8994620163934249882">"ରିଙ୍ଗର୍‌ ସାଇଲେଣ୍ଟରେ ଅଛି।"</string>
+ <string name="accessibility_ringer_silent" msgid="8994620163934249882">"ରିଂଗର ସାଇଲେଣ୍ଟରେ ଅଛି।"</string>
<!-- no translation found for accessibility_casting (8708751252897282313) -->
<skip />
<string name="accessibility_desc_notification_shade" msgid="5355229129428759989">"ବିଜ୍ଞପ୍ତି ଶେଡ୍‍।"</string>
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ଚାଲୁ ଅଛି"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ବନ୍ଦ ଅଛି"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ଅନୁପଲବ୍ଧ"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ଅଧିକ ଜାଣନ୍ତୁ"</string>
<string name="nav_bar" msgid="4642708685386136807">"ନାଭିଗେଶନ୍ ବାର୍‍"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ଲେଆଉଟ୍"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ସମ୍ପୂର୍ଣ୍ଣ ବାମ ବଟନ୍‍ ପ୍ରକାର"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ଫୋନ୍ ଥଣ୍ଡା ହେବା ସମୟରେ କିଛି ଫିଚର୍ ଠିକ ଭାବେ କାମ କରିନଥାଏ।\nଅଧିକ ସୂଚନା ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ଆପଣଙ୍କ ଫୋନ୍‍ ସ୍ୱଚାଳିତ ଭାବେ ଥଣ୍ଡା ହେବାକୁ ଚେଷ୍ଟା କରିବ। ଆପଣ ତଥାପି ନିଜ ଫୋନ୍‍ ବ୍ୟବହାର କରିପାରିବେ, କିନ୍ତୁ ଏହା ଧୀରେ ଚାଲିପାରେ।\n\nଆପଣଙ୍କ ଫୋନ୍‍ ଥଣ୍ଡା ହୋଇଯିବାପରେ, ଏହା ସାମାନ୍ୟ ଭାବେ ଚାଲିବ।"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ଯତ୍ନ ନେବା ପାଇଁ ଷ୍ଟେପଗୁଡ଼ିକ ଦେଖନ୍ତୁ"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"ଆପଣଙ୍କ ଡିଭାଇସକୁ ଅନପ୍ଲଗ କରନ୍ତୁ"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"ଚାର୍ଜିଂ ପୋର୍ଟ ନିକଟରେ ଆପଣଙ୍କ ଡିଭାଇସ ଗରମ ହୋଇଯାଉଛି। ଯଦି ଏହା ଏକ ଚାର୍ଜର କିମ୍ବା USB ଆକସେସୋରୀ ସହ କନେକ୍ଟ କରାଯାଇଥାଏ ତେବେ ଏହାକୁ ଅନପ୍ଲଗ କରନ୍ତୁ ଏବଂ ଧ୍ୟାନ ରଖନ୍ତୁ କାରଣ କେବୁଲ ମଧ୍ୟ ଗରମ ହୋଇପାରେ।"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ସେବା ସମ୍ବନ୍ଧିତ ଷ୍ଟେପ୍‌ଗୁଡ଼ିକ ଦେଖନ୍ତୁ"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"ବାମ ଶର୍ଟକଟ୍‍"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"ଡାହାଣ ଶର୍ଟକଟ୍‍"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 2beeb5969b1b..df36b540f003 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ਚਾਲੂ"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ਬੰਦ"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ਅਣਉਪਲਬਧ"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ਹੋਰ ਜਾਣੋ"</string>
<string name="nav_bar" msgid="4642708685386136807">"ਨੈਵੀਗੇਸ਼ਨ ਵਾਲੀ ਪੱਟੀ"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"ਖਾਕਾ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ਵਧੇਰੇ ਖੱਬੇ ਬਟਨ ਕਿਸਮ"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ਫ਼ੋਨ ਦੇ ਠੰਡਾ ਹੋਣ ਦੇ ਦੌਰਾਨ ਕੁਝ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਸੀਮਤ ਹੁੰਦੀਆਂ ਹਨ।\nਵਧੇਰੇ ਜਾਣਕਾਰੀ ਲਈ ਟੈਪ ਕਰੋ"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ਤੁਹਾਡਾ ਫ਼ੋਨ ਸਵੈਚਲਿਤ ਰੂਪ ਵਿੱਚ ਠੰਡਾ ਹੋਣ ਦੀ ਕੋਸ਼ਿਸ਼ ਕਰੇਗਾ। ਤੁਸੀਂ ਹਾਲੇ ਵੀ ਆਪਣੇ ਫ਼ੋਨ ਨੂੰ ਵਰਤ ਸਕਦੇ ਹੋ, ਪਰੰਤੂ ਹੋ ਸਕਦਾ ਹੈ ਕਿ ਇਹ ਵਧੇਰੇ ਹੌਲੀ ਚੱਲੇ।\n\nਇੱਕ ਵਾਰ ਠੰਡਾ ਹੋਣ ਤੋਂ ਬਾਅਦ ਤੁਹਾਡਾ ਫ਼ੋਨ ਸਧਾਰਨ ਤੌਰ \'ਤੇ ਚੱਲੇਗਾ।"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ਦੇਖਭਾਲ ਦੇ ਪੜਾਅ ਦੇਖੋ"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"ਆਪਣਾ ਡੀਵਾਈਸ ਅਣਪਲੱਗ ਕਰੋ"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"ਤੁਹਾਡਾ ਡੀਵਾਈਸ ਚਾਰਜਿੰਗ ਪੋਰਟ ਦੇ ਨੇੜੇ ਗਰਮ ਹੋ ਰਿਹਾ ਹੈ। ਜੇ ਇਹ ਕਿਸੇ ਚਾਰਜਰ ਜਾਂ USB ਐਕਸੈਸਰੀ ਨਾਲ ਕਨੈਕਟ ਹੈ, ਤਾਂ ਇਸਨੂੰ ਅਣਪਲੱਗ ਕਰੋ ਅਤੇ ਸਾਵਧਾਨ ਰਹੋ, ਕਿਉਂਕਿ ਕੇਬਲ ਵੀ ਗਰਮ ਹੋ ਸਕਦੀ ਹੈ।"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ਦੇਖਭਾਲ ਦੇ ਪੜਾਅ ਦੇਖੋ"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"ਖੱਬਾ ਸ਼ਾਰਟਕੱਟ"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"ਸੱਜਾ ਸ਼ਾਰਟਕੱਟ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 9449f267a2e0..f66eff3098db 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Włączono"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Wyłączono"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Niedostępne"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"Więcej informacji"</string>
<string name="nav_bar" msgid="4642708685386136807">"Pasek nawigacji"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Układ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Typ dodatkowego lewego przycisku"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Podczas obniżania temperatury telefonu niektóre funkcje są ograniczone\nKliknij, by dowiedzieć się więcej"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon automatycznie podejmie próbę obniżenia temperatury. Możesz go wciąż używać, ale telefon może działać wolniej.\n\nGdy temperatura się obniży, telefon będzie działał normalnie."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Zobacz instrukcję postępowania"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Odłącz urządzenie"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Urządzenie za bardzo się nagrzewa w okolicy gniazda ładowania. Jeśli jest podłączone do ładowarki albo akcesorium USB, odłącz je. Uważaj, bo kabel również może być nagrzany."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Zobacz instrukcję postępowania"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Lewy skrót"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Prawy skrót"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 2f9a36e64f29..6ce4ccb37870 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponível"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"saber mais"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegação"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botão esquerdo extra"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Alguns recursos ficam limitados enquanto o smartphone é resfriado.\nToque para saber mais"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Seu smartphone tentará se resfriar automaticamente. Você ainda poderá usá-lo, mas talvez ele fique mais lento.\n\nQuando o smartphone estiver resfriado, ele voltará ao normal."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver etapas de cuidado"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Desconecte seu dispositivo"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Seu dispositivo está ficando quente perto da porta de carregamento. Desconecte qualquer carregador ou acessório USB que esteja conectado, mas tome cuidado, porque o cabo também pode estar quente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver etapas de cuidado"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Atalho à esquerda"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Atalho à direita"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 3251eecb1b47..37918d4da69d 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponível"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"saber mais"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegação"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Esquema"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botão esquerdo adicional"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 2f9a36e64f29..6ce4ccb37870 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Ativado"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Desativado"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponível"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"saber mais"</string>
<string name="nav_bar" msgid="4642708685386136807">"Barra de navegação"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tipo de botão esquerdo extra"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Alguns recursos ficam limitados enquanto o smartphone é resfriado.\nToque para saber mais"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Seu smartphone tentará se resfriar automaticamente. Você ainda poderá usá-lo, mas talvez ele fique mais lento.\n\nQuando o smartphone estiver resfriado, ele voltará ao normal."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Ver etapas de cuidado"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Desconecte seu dispositivo"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Seu dispositivo está ficando quente perto da porta de carregamento. Desconecte qualquer carregador ou acessório USB que esteja conectado, mas tome cuidado, porque o cabo também pode estar quente."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Ver etapas de cuidado"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Atalho à esquerda"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Atalho à direita"</string>
diff --git a/packages/SystemUI/res/values-ro-ldrtl/strings.xml b/packages/SystemUI/res/values-ro-ldrtl/strings.xml
index e167b41c680c..a7cd33cd21da 100644
--- a/packages/SystemUI/res/values-ro-ldrtl/strings.xml
+++ b/packages/SystemUI/res/values-ro-ldrtl/strings.xml
@@ -19,5 +19,5 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Trageți spre stânga pentru a comuta rapid între aplicații"</string>
+ <string name="recents_quick_scrub_onboarding" msgid="2452671841151577157">"Trage spre stânga pentru a comuta rapid între aplicații"</string>
</resources>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 683774c18f8e..01f7e184ed01 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -20,53 +20,53 @@
<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="4811759950673118541">"UI sistem"</string>
- <string name="battery_low_title" msgid="5319680173344341779">"Activați Economisirea bateriei?"</string>
+ <string name="battery_low_title" msgid="5319680173344341779">"Activezi Economisirea bateriei?"</string>
<string name="battery_low_description" msgid="3282977755476423966">"Mai aveți <xliff:g id="PERCENTAGE">%s</xliff:g> din baterie. Economisirea bateriei activează Tema întunecată, restricționează activitatea în fundal și amână notificările."</string>
<string name="battery_low_intro" msgid="5148725009653088790">"Economisirea bateriei activează Tema întunecată, restricționează activitatea în fundal și amână notificările."</string>
<string name="battery_low_percent_format" msgid="4276661262843170964">"Procent rămas din baterie: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="invalid_charger_title" msgid="938685362320735167">"Nu se poate realiza încărcarea prin USB"</string>
- <string name="invalid_charger_text" msgid="2339310107232691577">"Folosiți încărcătorul livrat împreună cu dispozitivul"</string>
- <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Activați economisirea bateriei?"</string>
+ <string name="invalid_charger_text" msgid="2339310107232691577">"Folosește încărcătorul livrat împreună cu dispozitivul"</string>
+ <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"Activezi economisirea bateriei?"</string>
<string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"Despre Economisirea bateriei"</string>
- <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Activați"</string>
- <string name="battery_saver_start_action" msgid="8353766979886287140">"Activați"</string>
+ <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"Activează"</string>
+ <string name="battery_saver_start_action" msgid="8353766979886287140">"Activează"</string>
<string name="battery_saver_dismiss_action" msgid="7199342621040014738">"Nu, mulțumesc"</string>
<string name="status_bar_settings_auto_rotation" msgid="8329080442278431708">"Rotire automată a ecranului"</string>
- <string name="usb_device_permission_prompt" msgid="4414719028369181772">"Permiteți <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
- <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Permiteți accesul aplicației <xliff:g id="APPLICATION">%1$s</xliff:g> la <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nPermisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string>
- <string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"Permiteți ca <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
- <string name="usb_audio_device_confirm_prompt_title" msgid="8828406516732985696">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt" msgid="4414719028369181772">"Permiți ca <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_permission_prompt_warn" msgid="2309129784984063656">"Permiți accesul aplicației <xliff:g id="APPLICATION">%1$s</xliff:g> la <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nPermisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string>
+ <string name="usb_audio_device_permission_prompt_title" msgid="4221351137250093451">"Permiți ca <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_audio_device_confirm_prompt_title" msgid="8828406516732985696">"Deschizi <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
<string name="usb_audio_device_prompt_warn" msgid="2504972133361130335">"Permisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB. Dacă folosiți <xliff:g id="APPLICATION">%1$s</xliff:g> cu acest dispozitiv, acest lucru vă poate împiedica să auziți apeluri, notificări și alarme."</string>
<string name="usb_audio_device_prompt" msgid="7944987408206252949">"Dacă folosiți <xliff:g id="APPLICATION">%1$s</xliff:g> cu acest dispozitiv, acest lucru vă poate împiedica să auziți apeluri, notificări și alarme."</string>
- <string name="usb_accessory_permission_prompt" msgid="717963550388312123">"Permiteți <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
- <string name="usb_device_confirm_prompt" msgid="4091711472439910809">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
- <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> pentru a gestiona <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nPermisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string>
- <string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"Deschideți <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
- <string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"Aplic. instal. nu funcț. cu acest acces. USB. Aflați despre acest accesoriu la <xliff:g id="URL">%1$s</xliff:g>"</string>
+ <string name="usb_accessory_permission_prompt" msgid="717963550388312123">"Permiți ca <xliff:g id="APPLICATION">%1$s</xliff:g> să acceseze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt" msgid="4091711472439910809">"Deschizi <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_DEVICE">%2$s</xliff:g>?"</string>
+ <string name="usb_device_confirm_prompt_warn" msgid="990208659736311769">"Deschizi <xliff:g id="APPLICATION">%1$s</xliff:g> pentru a gestiona <xliff:g id="USB_DEVICE">%2$s</xliff:g>?\nPermisiunea de înregistrare nu a fost acordată aplicației, dar aceasta poate să înregistreze conținut audio prin intermediul acestui dispozitiv USB."</string>
+ <string name="usb_accessory_confirm_prompt" msgid="5728408382798643421">"Deschizi <xliff:g id="APPLICATION">%1$s</xliff:g> ca să gestioneze <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>?"</string>
+ <string name="usb_accessory_uri_prompt" msgid="6756649383432542382">"Aplic. instal. nu funcț. cu acest acces. USB. Află despre acest accesoriu la <xliff:g id="URL">%1$s</xliff:g>"</string>
<string name="title_usb_accessory" msgid="1236358027511638648">"Accesoriu USB"</string>
- <string name="label_view" msgid="6815442985276363364">"Afișați"</string>
- <string name="always_use_device" msgid="210535878779644679">"Deschideți întotdeauna <xliff:g id="APPLICATION">%1$s</xliff:g> când este conectat <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
- <string name="always_use_accessory" msgid="1977225429341838444">"Deschideți întotdeauna <xliff:g id="APPLICATION">%1$s</xliff:g> când este conectat <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
- <string name="usb_debugging_title" msgid="8274884945238642726">"Permiteți remedierea erorilor prin USB?"</string>
+ <string name="label_view" msgid="6815442985276363364">"Afișează"</string>
+ <string name="always_use_device" msgid="210535878779644679">"Deschide întotdeauna <xliff:g id="APPLICATION">%1$s</xliff:g> când este conectat <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
+ <string name="always_use_accessory" msgid="1977225429341838444">"Deschide întotdeauna <xliff:g id="APPLICATION">%1$s</xliff:g> când este conectat <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
+ <string name="usb_debugging_title" msgid="8274884945238642726">"Permiți remedierea erorilor prin USB?"</string>
<string name="usb_debugging_message" msgid="5794616114463921773">"Amprenta din cheia RSA a computerului este:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
- <string name="usb_debugging_always" msgid="4003121804294739548">"Permiteți întotdeauna de pe acest computer"</string>
- <string name="usb_debugging_allow" msgid="1722643858015321328">"Permiteți"</string>
+ <string name="usb_debugging_always" msgid="4003121804294739548">"Permite întotdeauna de pe acest computer"</string>
+ <string name="usb_debugging_allow" msgid="1722643858015321328">"Permite"</string>
<string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Remedierea erorilor prin USB nu este permisă"</string>
- <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"Utilizatorul conectat momentan pe acest dispozitiv nu poate activa remedierea erorilor prin USB. Pentru a folosi această funcție, comutați la utilizatorul principal."</string>
+ <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"Utilizatorul conectat momentan pe acest dispozitiv nu poate activa remedierea erorilor prin USB. Pentru a folosi această funcție, comută la utilizatorul principal."</string>
<string name="hdmi_cec_set_menu_language_title" msgid="1259765420091503742">"Schimbați limba de sistem la <xliff:g id="LANGUAGE">%1$s</xliff:g>?"</string>
<string name="hdmi_cec_set_menu_language_description" msgid="8176716678074126619">"Alt dispozitiv solicită schimbarea limbii de sistem"</string>
- <string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Schimbați limba"</string>
- <string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Păstrați limba actuală"</string>
- <string name="wifi_debugging_title" msgid="7300007687492186076">"Permiteți remedierea erorilor wireless în această rețea?"</string>
+ <string name="hdmi_cec_set_menu_language_accept" msgid="2513689457281009578">"Schimbă limba"</string>
+ <string name="hdmi_cec_set_menu_language_decline" msgid="7650721096558646011">"Păstrează limba actuală"</string>
+ <string name="wifi_debugging_title" msgid="7300007687492186076">"Permiți remedierea erorilor wireless în această rețea?"</string>
<string name="wifi_debugging_message" msgid="5461204211731802995">"Numele rețelei (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresa Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
- <string name="wifi_debugging_always" msgid="2968383799517975155">"Permiteți întotdeauna în această rețea"</string>
- <string name="wifi_debugging_allow" msgid="4573224609684957886">"Permiteți"</string>
+ <string name="wifi_debugging_always" msgid="2968383799517975155">"Permite întotdeauna în această rețea"</string>
+ <string name="wifi_debugging_allow" msgid="4573224609684957886">"Permite"</string>
<string name="wifi_debugging_secondary_user_title" msgid="2493201475880517725">"Remedierea erorilor wireless nu este permisă"</string>
<string name="wifi_debugging_secondary_user_message" msgid="4492383073970079751">"Utilizatorul conectat momentan pe acest dispozitiv nu poate activa remedierea erorilor wireless. Pentru a folosi această funcție, comutați la utilizatorul principal."</string>
<string name="usb_contaminant_title" msgid="894052515034594113">"Portul USB a fost dezactivat"</string>
- <string name="usb_contaminant_message" msgid="7730476585174719805">"Pentru a vă proteja dispozitivul de lichide sau reziduuri, portul USB este dezactivat și nu va detecta niciun accesoriu.\n\nVeți primi o notificare când puteți folosi din nou portul USB."</string>
+ <string name="usb_contaminant_message" msgid="7730476585174719805">"Pentru a proteja dispozitivul de lichide sau reziduuri, portul USB este dezactivat și nu va detecta niciun accesoriu.\n\nVei primi o notificare când poți folosi din nou portul USB."</string>
<string name="usb_port_enabled" msgid="531823867664717018">"Portul USB a fost activat pentru a detecta încărcătoarele și accesoriile"</string>
- <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"Activați USB"</string>
+ <string name="usb_disable_contaminant_detection" msgid="3827082183595978641">"Activează USB"</string>
<string name="learn_more" msgid="4690632085667273811">"Mai multe"</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>
@@ -75,15 +75,15 @@
<string name="screenshot_saved_title" msgid="8893267638659083153">"Captură de ecran salvată"</string>
<string name="screenshot_failed_title" msgid="3259148215671936891">"Nu s-a putut salva captura de ecran"</string>
<string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"Pentru a salva captura de ecran, trebuie să deblocați dispozitivul"</string>
- <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Încercați să faceți din nou o captură de ecran"</string>
+ <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Încearcă să faci din nou o captură de ecran"</string>
<string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"Nu se poate salva captura de ecran"</string>
- <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Crearea capturilor de ecran nu este permisă de aplicație sau de organizația dvs."</string>
+ <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"Crearea capturilor de ecran nu e permisă de aplicație sau de organizația ta"</string>
<string name="screenshot_blocked_by_admin" msgid="5486757604822795797">"Administratorul IT a blocat crearea capturilor de ecran"</string>
- <string name="screenshot_edit_label" msgid="8754981973544133050">"Editați"</string>
- <string name="screenshot_edit_description" msgid="3333092254706788906">"Editați captura de ecran"</string>
- <string name="screenshot_share_description" msgid="2861628935812656612">"Trimiteți captura de ecran"</string>
+ <string name="screenshot_edit_label" msgid="8754981973544133050">"Editează"</string>
+ <string name="screenshot_edit_description" msgid="3333092254706788906">"Editează captura de ecran"</string>
+ <string name="screenshot_share_description" msgid="2861628935812656612">"Trimite captura de ecran"</string>
<string name="screenshot_scroll_label" msgid="2930198809899329367">"Surprindeți mai mult"</string>
- <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Închideți captura de ecran"</string>
+ <string name="screenshot_dismiss_description" msgid="4702341245899508786">"Închide captura de ecran"</string>
<string name="screenshot_preview_description" msgid="7606510140714080474">"Previzualizare a capturii de ecran"</string>
<string name="screenshot_top_boundary_pct" msgid="2520148599096479332">"Marginea de sus la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
<string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"Marginea de jos la <xliff:g id="PERCENT">%1$d</xliff:g> %%"</string>
@@ -102,50 +102,50 @@
<string name="screenrecord_start" msgid="330991441575775004">"Începeți"</string>
<string name="screenrecord_ongoing_screen_only" msgid="4459670242451527727">"Se înregistrează ecranul"</string>
<string name="screenrecord_ongoing_screen_and_audio" msgid="5351133763125180920">"Se înregistrează ecranul și conținutul audio"</string>
- <string name="screenrecord_taps_label" msgid="1595690528298857649">"Afișați atingerile de pe ecran"</string>
- <string name="screenrecord_stop_label" msgid="72699670052087989">"Opriți"</string>
- <string name="screenrecord_share_label" msgid="5025590804030086930">"Trimiteți"</string>
+ <string name="screenrecord_taps_label" msgid="1595690528298857649">"Afișează atingerile de pe ecran"</string>
+ <string name="screenrecord_stop_label" msgid="72699670052087989">"Oprește"</string>
+ <string name="screenrecord_share_label" msgid="5025590804030086930">"Trimite"</string>
<string name="screenrecord_save_title" msgid="1886652605520893850">"Înregistrarea ecranului a fost salvată"</string>
- <string name="screenrecord_save_text" msgid="3008973099800840163">"Atingeți pentru a afișa"</string>
+ <string name="screenrecord_save_text" msgid="3008973099800840163">"Atinge pentru a afișa"</string>
<string name="screenrecord_delete_error" msgid="2870506119743013588">"Eroare la ștergerea înregistrării ecranului"</string>
<string name="screenrecord_start_error" msgid="2200660692479682368">"Eroare la începerea înregistrării ecranului"</string>
<string name="accessibility_back" msgid="6530104400086152611">"Înapoi"</string>
<string name="accessibility_home" msgid="5430449841237966217">"Ecranul de pornire"</string>
<string name="accessibility_menu" msgid="2701163794470513040">"Meniu"</string>
<string name="accessibility_accessibility_button" msgid="4089042473497107709">"Accesibilitate"</string>
- <string name="accessibility_rotate_button" msgid="1238584767612362586">"Rotiți ecranul"</string>
+ <string name="accessibility_rotate_button" msgid="1238584767612362586">"Rotește ecranul"</string>
<string name="accessibility_recent" msgid="901641734769533575">"Recente"</string>
<string name="accessibility_camera_button" msgid="2938898391716647247">"Cameră foto"</string>
<string name="accessibility_phone_button" msgid="4256353121703100427">"Telefon"</string>
<string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Asistent vocal"</string>
<string name="accessibility_wallet_button" msgid="1458258783460555507">"Portofel"</string>
<string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Scanner de coduri QR"</string>
- <string name="accessibility_unlock_button" msgid="122785427241471085">"Deblocați"</string>
+ <string name="accessibility_unlock_button" msgid="122785427241471085">"Deblochează"</string>
<string name="accessibility_lock_icon" msgid="661492842417875775">"Dispozitiv blocat"</string>
<string name="accessibility_scanning_face" msgid="3093828357921541387">"Scanarea chipului"</string>
- <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Trimiteți"</string>
- <string name="cancel" msgid="1089011503403416730">"Anulați"</string>
- <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmați"</string>
- <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Încercați din nou"</string>
- <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Atingeți pentru a anula autentificarea"</string>
- <string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"Încercați din nou"</string>
+ <string name="accessibility_send_smart_reply" msgid="8885032190442015141">"Trimite"</string>
+ <string name="cancel" msgid="1089011503403416730">"Anulează"</string>
+ <string name="biometric_dialog_confirm" msgid="2005978443007344895">"Confirmă"</string>
+ <string name="biometric_dialog_try_again" msgid="8575345628117768844">"Încearcă din nou"</string>
+ <string name="biometric_dialog_empty_space_description" msgid="3330555462071453396">"Atinge pentru a anula autentificarea"</string>
+ <string name="biometric_dialog_face_icon_description_idle" msgid="4351777022315116816">"Încearcă din nou"</string>
<string name="biometric_dialog_face_icon_description_authenticating" msgid="3401633342366146535">"Se caută chipul"</string>
<string name="biometric_dialog_face_icon_description_authenticated" msgid="2242167416140740920">"Chip autentificat"</string>
<string name="biometric_dialog_face_icon_description_confirmed" msgid="7918067993953940778">"Confirmat"</string>
- <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Atingeți Confirmați pentru a finaliza"</string>
- <string name="biometric_dialog_tap_confirm_with_face" msgid="1092050545851021991">"S-a deblocat cu ajutorul feței. Apăsați pictograma de deblocare pentru a continua"</string>
- <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"S-a deblocat cu ajutorul feței. Apăsați pentru a continua."</string>
- <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Chipul a fost recunoscut. Apăsați pentru a continua."</string>
+ <string name="biometric_dialog_tap_confirm" msgid="9166350738859143358">"Atinge Confirm pentru a finaliza"</string>
+ <string name="biometric_dialog_tap_confirm_with_face" msgid="1092050545851021991">"S-a deblocat cu ajutorul feței. Apasă pictograma de deblocare pentru a continua"</string>
+ <string name="biometric_dialog_tap_confirm_with_face_alt_1" msgid="439152621640507113">"S-a deblocat cu ajutorul feței. Apasă pentru a continua."</string>
+ <string name="biometric_dialog_tap_confirm_with_face_alt_2" msgid="8586608186457385108">"Chipul a fost recunoscut. Apasă pentru a continua."</string>
<string name="biometric_dialog_tap_confirm_with_face_alt_3" msgid="2192670471930606539">"Chip recunoscut. Apăsați pictograma de deblocare să continuați."</string>
<string name="biometric_dialog_authenticated" msgid="7337147327545272484">"Autentificat"</string>
- <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Folosiți PIN-ul"</string>
- <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"Folosiți modelul"</string>
- <string name="biometric_dialog_use_password" msgid="3445033859393474779">"Folosiți parola"</string>
+ <string name="biometric_dialog_use_pin" msgid="8385294115283000709">"Folosește PIN-ul"</string>
+ <string name="biometric_dialog_use_pattern" msgid="2315593393167211194">"Folosește modelul"</string>
+ <string name="biometric_dialog_use_password" msgid="3445033859393474779">"Folosește parola"</string>
<string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN greșit"</string>
<string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"Model greșit"</string>
<string name="biometric_dialog_wrong_password" msgid="69477929306843790">"Parolă greșită"</string>
- <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Prea multe încercări incorecte.\nÎncercați din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string>
- <string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Încercați din nou. Încercarea <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> din <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
+ <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"Prea multe încercări incorecte.\nÎncearcă din nou peste <xliff:g id="NUMBER">%d</xliff:g> secunde."</string>
+ <string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"Încearcă din nou. Încercarea <xliff:g id="ATTEMPTS_0">%1$d</xliff:g> din <xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g>."</string>
<string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"Datele dvs. vor fi șterse"</string>
<string name="biometric_dialog_last_pattern_attempt_before_wipe_device" msgid="6562299244825817598">"Dacă la următoarea încercare introduceți un model incorect, datele de pe acest dispozitiv vor fi șterse."</string>
<string name="biometric_dialog_last_pin_attempt_before_wipe_device" msgid="9151756675698215723">"Dacă la următoarea încercare introduceți un cod PIN incorect, datele de pe acest dispozitiv vor fi șterse."</string>
@@ -156,9 +156,9 @@
<string name="biometric_dialog_last_pattern_attempt_before_wipe_profile" msgid="6045224069529284686">"Dacă la următoarea încercare introduceți un model incorect, profilul de serviciu și datele sale vor fi șterse."</string>
<string name="biometric_dialog_last_pin_attempt_before_wipe_profile" msgid="545567685899091757">"Dacă la următoarea încercare introduceți un cod PIN incorect, profilul de serviciu și datele sale vor fi șterse."</string>
<string name="biometric_dialog_last_password_attempt_before_wipe_profile" msgid="8538032972389729253">"Dacă la următoarea încercare introduceți o parolă incorectă, profilul de serviciu și datele sale vor fi șterse."</string>
- <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Atingeți senzorul de amprente"</string>
+ <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Atinge senzorul de amprente"</string>
<string name="accessibility_fingerprint_dialog_fingerprint_icon" msgid="4465698996175640549">"Pictograma amprentă"</string>
- <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Chipul nu a fost recunoscut. Folosiți amprenta."</string>
+ <string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"Chipul nu a fost recunoscut. Folosește amprenta."</string>
<!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
<skip />
<string name="keyguard_face_failed" msgid="9044619102286917151">"Chip nerecunoscut"</string>
@@ -175,7 +175,7 @@
<string name="accessibility_battery_level" msgid="5143715405241138822">"Baterie: <xliff:g id="NUMBER">%d</xliff:g> la sută."</string>
<string name="accessibility_battery_level_with_estimate" msgid="4843119982547599452">"Procentul rămas din baterie este <xliff:g id="PERCENTAGE">%1$s</xliff:g>. În baza utilizării, timpul rămas este de aproximativ <xliff:g id="TIME">%2$s</xliff:g>"</string>
<string name="accessibility_battery_level_charging" msgid="8892191177774027364">"Bateria se încarcă, <xliff:g id="BATTERY_PERCENTAGE">%d</xliff:g> la sută."</string>
- <string name="accessibility_overflow_action" msgid="8555835828182509104">"Vedeți toate notificările"</string>
+ <string name="accessibility_overflow_action" msgid="8555835828182509104">"Vezi toate notificările"</string>
<string name="accessibility_tty_enabled" msgid="1123180388823381118">"TeleTypewriter activat."</string>
<string name="accessibility_ringer_vibrate" msgid="6261841170896561364">"Vibrare sonerie."</string>
<string name="accessibility_ringer_silent" msgid="8994620163934249882">"Sonerie silențioasă."</string>
@@ -185,7 +185,7 @@
<string name="accessibility_desc_quick_settings" msgid="4374766941484719179">"Setări rapide."</string>
<string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"Ecranul de blocare."</string>
<string name="accessibility_desc_work_lock" msgid="4355620395354680575">"Ecran de blocare pentru serviciu"</string>
- <string name="accessibility_desc_close" msgid="8293708213442107755">"Închideți"</string>
+ <string name="accessibility_desc_close" msgid="8293708213442107755">"Închide"</string>
<string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"niciun sunet"</string>
<string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"numai alarme"</string>
<string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"Nu deranja."</string>
@@ -198,11 +198,11 @@
<string name="accessibility_brightness" msgid="5391187016177823721">"Luminozitatea ecranului"</string>
<string name="data_usage_disabled_dialog_mobile_title" msgid="2286843518689837719">"Datele mobile sunt întrerupte"</string>
<string name="data_usage_disabled_dialog_title" msgid="9131615296036724838">"Conexiunea de date este întreruptă"</string>
- <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"A fost atinsă limita de date setată. Datele mobile nu mai sunt folosite.\n\nDacă reluați, este posibil să se aplice taxe pentru utilizarea datelor."</string>
- <string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"Reluați"</string>
+ <string name="data_usage_disabled_dialog" msgid="7933201635215099780">"A fost atinsă limita de date setată. Datele mobile nu mai sunt folosite.\n\nDacă reiei, se pot aplica taxe pentru utilizarea datelor."</string>
+ <string name="data_usage_disabled_dialog_enable" msgid="2796648546086408937">"Reia"</string>
<string name="accessibility_location_active" msgid="2845747916764660369">"Solicitări locație active"</string>
<string name="accessibility_sensors_off_active" msgid="2619725434618911551">"Dezactivarea senzorilor este activă"</string>
- <string name="accessibility_clear_all" msgid="970525598287244592">"Ștergeți toate notificările."</string>
+ <string name="accessibility_clear_all" msgid="970525598287244592">"Șterge toate notificările."</string>
<string name="notification_group_overflow_indicator" msgid="7605120293801012648">"+ <xliff:g id="NUMBER">%s</xliff:g>"</string>
<string name="notification_group_overflow_description" msgid="7176322877233433278">"{count,plural, =1{Încă # notificare în grup.}few{Încă # notificări în grup.}other{Încă # de notificări în grup.}}"</string>
<string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"Ecranul este blocat în orientarea de tip peisaj."</string>
@@ -245,7 +245,7 @@
<string name="quick_settings_color_correction_label" msgid="5636617913560474664">"Corecția culorii"</string>
<string name="quick_settings_more_user_settings" msgid="1064187451100861954">"Setări de utilizator"</string>
<string name="quick_settings_done" msgid="2163641301648855793">"Terminat"</string>
- <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Închideți"</string>
+ <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"Închide"</string>
<string name="quick_settings_connected" msgid="3873605509184830379">"Conectat"</string>
<string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"Conectat, bateria la <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
<string name="quick_settings_connecting" msgid="2381969772953268809">"Se conectează..."</string>
@@ -281,7 +281,7 @@
<string name="quick_settings_nfc_on" msgid="1004976611203202230">"Serviciul NFC este activat"</string>
<string name="quick_settings_screen_record_label" msgid="8650355346742003694">"Înregistrarea ecranului"</string>
<string name="quick_settings_screen_record_start" msgid="1574725369331638985">"Începeți"</string>
- <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Opriți"</string>
+ <string name="quick_settings_screen_record_stop" msgid="8087348522976412119">"Oprește"</string>
<string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string>
<string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"Deblocați microfonul dispozitivului?"</string>
<string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"Deblocați camera dispozitivului?"</string>
@@ -299,21 +299,21 @@
<string name="sensor_privacy_camera_unblocked_toast_content" msgid="7843105715964332311">"Cameră foto disponibilă"</string>
<string name="sensor_privacy_mic_camera_unblocked_toast_content" msgid="7339355093282661115">"Microfon și cameră disponibile"</string>
<string name="media_seamless_other_device" msgid="4654849800789196737">"Alt dispozitiv"</string>
- <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comutați secțiunea Recente"</string>
- <string name="zen_priority_introduction" msgid="3159291973383796646">"Se vor anunța prin sunete și vibrații numai alarmele, mementourile, evenimentele și apelanții specificați de dvs. Totuși, veți auzi tot ce alegeți să redați, inclusiv muzică, videoclipuri și jocuri."</string>
- <string name="zen_alarms_introduction" msgid="3987266042682300470">"Se vor anunța prin sunete și vibrații numai alarmele. Totuși, veți auzi tot ce alegeți să redați, inclusiv muzică, videoclipuri și jocuri."</string>
- <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizați"</string>
- <string name="zen_silence_introduction_voice" msgid="853573681302712348">"Această opțiune blochează TOATE sunetele și vibrațiile, inclusiv cele ale alarmelor, muzicii, videoclipurilor și jocurilor. Totuși, veți putea iniția apeluri."</string>
+ <string name="quick_step_accessibility_toggle_overview" msgid="7908949976727578403">"Comută secțiunea Recente"</string>
+ <string name="zen_priority_introduction" msgid="3159291973383796646">"Se vor anunța prin sunete și vibrații numai alarmele, mementourile, evenimentele și apelanții specificați de tine. Totuși, vei auzi tot ce alegi să redai, inclusiv muzică, videoclipuri și jocuri."</string>
+ <string name="zen_alarms_introduction" msgid="3987266042682300470">"Se vor anunța prin sunete și vibrații numai alarmele. Totuși, vei auzi tot ce alegi să redai, inclusiv muzică, videoclipuri și jocuri."</string>
+ <string name="zen_priority_customize_button" msgid="4119213187257195047">"Personalizează"</string>
+ <string name="zen_silence_introduction_voice" msgid="853573681302712348">"Această opțiune blochează TOATE sunetele și vibrațiile, inclusiv cele ale alarmelor, muzicii, videoclipurilor și jocurilor. Totuși, vei putea iniția apeluri."</string>
<string name="zen_silence_introduction" msgid="6117517737057344014">"Această opțiune blochează TOATE sunetele și vibrațiile, inclusiv cele ale alarmelor, muzicii, videoclipurilor și jocurilor."</string>
- <string name="notification_tap_again" msgid="4477318164947497249">"Atingeți din nou pentru a deschide"</string>
- <string name="tap_again" msgid="1315420114387908655">"Atingeți din nou"</string>
- <string name="keyguard_unlock" msgid="8031975796351361601">"Glisați în sus pentru a deschide"</string>
- <string name="keyguard_unlock_press" msgid="9140109453735019209">"Apăsați pictograma de deblocare pentru a deschide"</string>
+ <string name="notification_tap_again" msgid="4477318164947497249">"Atinge din nou pentru a deschide"</string>
+ <string name="tap_again" msgid="1315420114387908655">"Atinge din nou"</string>
+ <string name="keyguard_unlock" msgid="8031975796351361601">"Glisează în sus pentru a deschide"</string>
+ <string name="keyguard_unlock_press" msgid="9140109453735019209">"Apasă pictograma de deblocare pentru a deschide"</string>
<string name="keyguard_face_successful_unlock_swipe" msgid="6180997591385846073">"S-a deblocat folosind fața. Glisați în sus și deschideți."</string>
- <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"S-a deblocat cu ajutorul feței. Apăsați pictograma de deblocare pentru a deschide"</string>
- <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"S-a deblocat cu ajutorul feței. Apăsați pentru a deschide."</string>
- <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Chipul a fost recunoscut. Apăsați pentru a deschide."</string>
- <string name="keyguard_face_successful_unlock_press_alt_3" msgid="7219030481255573962">"Chip recunoscut. Apăsați pictograma de deblocare pentru a deschide"</string>
+ <string name="keyguard_face_successful_unlock_press" msgid="25520941264602588">"S-a deblocat cu ajutorul feței. Apasă pictograma de deblocare pentru a deschide"</string>
+ <string name="keyguard_face_successful_unlock_press_alt_1" msgid="5715461103913071474">"S-a deblocat cu ajutorul feței. Apasă pentru a deschide."</string>
+ <string name="keyguard_face_successful_unlock_press_alt_2" msgid="8310787946357120406">"Chipul a fost recunoscut. Apasă pentru a deschide."</string>
+ <string name="keyguard_face_successful_unlock_press_alt_3" msgid="7219030481255573962">"Chip recunoscut. Apasă pictograma de deblocare pentru a deschide"</string>
<string name="keyguard_face_successful_unlock" msgid="4203999851465708287">"S-a deblocat folosind fața"</string>
<string name="keyguard_face_successful_unlock_alt1" msgid="5853906076353839628">"Chipul a fost recunoscut"</string>
<string-array name="udfps_accessibility_touch_hints">
@@ -322,14 +322,14 @@
<item msgid="4844142668312841831">"Deplasați spre dreapta"</item>
<item msgid="5640521437931460125">"Deplasați în sus"</item>
</string-array>
- <string name="keyguard_retry" msgid="886802522584053523">"Glisați pentru a încerca din nou"</string>
- <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Deblocați pentru a folosi NFC"</string>
+ <string name="keyguard_retry" msgid="886802522584053523">"Glisează pentru a încerca din nou"</string>
+ <string name="require_unlock_for_nfc" msgid="1305686454823018831">"Deblochează pentru a folosi NFC"</string>
<string name="do_disclosure_generic" msgid="4896482821974707167">"Dispozitivul aparține organizației dvs."</string>
<string name="do_disclosure_with_name" msgid="2091641464065004091">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
<string name="do_financed_disclosure_with_name" msgid="6723004643314467864">"Acest dispozitiv este oferit de <xliff:g id="ORGANIZATION_NAME">%s</xliff:g>"</string>
- <string name="phone_hint" msgid="6682125338461375925">"Glisați dinspre telefon"</string>
- <string name="voice_hint" msgid="7476017460191291417">"Glisați dinspre pictogramă pentru asistentul vocal"</string>
- <string name="camera_hint" msgid="4519495795000658637">"Glisați pentru a fotografia"</string>
+ <string name="phone_hint" msgid="6682125338461375925">"Glisează dinspre telefon"</string>
+ <string name="voice_hint" msgid="7476017460191291417">"Glisează dinspre pictogramă pentru asistentul vocal"</string>
+ <string name="camera_hint" msgid="4519495795000658637">"Glisează pentru a fotografia"</string>
<string name="interruption_level_none_with_warning" msgid="8394434073508145437">"Liniște absolută. Se va opri sunetul și pentru cititoarele de ecran."</string>
<string name="interruption_level_none" msgid="219484038314193379">"Niciun sunet"</string>
<string name="interruption_level_priority" msgid="661294280016622209">"Numai cu prioritate"</string>
@@ -342,35 +342,35 @@
<string name="keyguard_indication_charging_time_fast" msgid="8390311020603859480">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă rapid • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
<string name="keyguard_indication_charging_time_slowly" msgid="301936949731705417">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă lent • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
<string name="keyguard_indication_charging_time_dock" msgid="3149328898931741271">"<xliff:g id="PERCENTAGE">%2$s</xliff:g> • Se încarcă • <xliff:g id="CHARGING_TIME_LEFT">%1$s</xliff:g> până la încărcarea completă"</string>
- <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Comutați între utilizatori"</string>
+ <string name="accessibility_multi_user_switch_switcher" msgid="5330448341251092660">"Schimbă utilizatorul"</string>
<string name="accessibility_multi_user_list_switcher" msgid="8574105376229857407">"meniu vertical"</string>
<string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
- <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ați revenit în sesiunea pentru invitați!"</string>
- <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string>
- <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string>
- <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, continuați"</string>
+ <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ai revenit în sesiunea pentru invitați!"</string>
+ <string name="guest_wipe_session_message" msgid="3393823610257065457">"Continui sesiunea?"</string>
+ <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începe din nou"</string>
+ <string name="guest_wipe_session_dontwipe" msgid="3211052048269304205">"Da, continuă"</string>
<string name="guest_notification_app_name" msgid="2110425506754205509">"Modul pentru invitați"</string>
- <string name="guest_notification_session_active" msgid="5567273684713471450">"Folosiți modul pentru invitați"</string>
+ <string name="guest_notification_session_active" msgid="5567273684713471450">"Folosește modul pentru invitați"</string>
<string name="user_add_user_message_guest_remove" msgid="5589286604543355007">\n\n"Dacă adăugați un utilizator nou, veți ieși din modul pentru invitați și se vor șterge toate aplicațiile și datele din sesiunea pentru invitați actuală."</string>
- <string name="user_limit_reached_title" msgid="2429229448830346057">"Ați atins limita de utilizatori"</string>
+ <string name="user_limit_reached_title" msgid="2429229448830346057">"Ai atins limita de utilizatori"</string>
<string name="user_limit_reached_message" msgid="1070703858915935796">"{count,plural, =1{Se poate crea doar un utilizator.}few{Puteți adăuga până la # utilizatori.}other{Puteți adăuga până la # de utilizatori.}}"</string>
- <string name="user_remove_user_title" msgid="9124124694835811874">"Eliminați utilizatorul?"</string>
+ <string name="user_remove_user_title" msgid="9124124694835811874">"Elimini utilizatorul?"</string>
<string name="user_remove_user_message" msgid="6702834122128031833">"Toate aplicațiile și datele acestui utilizator vor fi șterse."</string>
- <string name="user_remove_user_remove" msgid="8387386066949061256">"Eliminați"</string>
+ <string name="user_remove_user_remove" msgid="8387386066949061256">"Elimină"</string>
<string name="media_projection_dialog_text" msgid="1755705274910034772">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> va avea acces la toate informațiile vizibile pe ecran sau redate pe dispozitiv în timp ce înregistrați sau proiectați. Între aceste informații se numără parole, detalii de plată, fotografii, mesaje și conținutul audio pe care îl redați."</string>
<string name="media_projection_dialog_service_text" msgid="958000992162214611">"Serviciul care oferă această funcție va avea acces la toate informațiile vizibile pe ecran sau redate pe dispozitiv în timp ce înregistrați sau proiectați. Între aceste informații se numără parole, detalii de plată, fotografii, mesaje și conținutul audio pe care îl redați."</string>
<string name="media_projection_dialog_service_title" msgid="2888507074107884040">"Începeți să înregistrați sau să proiectați?"</string>
<string name="media_projection_dialog_title" msgid="3316063622495360646">"Începeți să înregistrați sau să proiectați cu <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>?"</string>
- <string name="clear_all_notifications_text" msgid="348312370303046130">"Ștergeți toate notificările"</string>
- <string name="manage_notifications_text" msgid="6885645344647733116">"Gestionați"</string>
+ <string name="clear_all_notifications_text" msgid="348312370303046130">"Șterge toate notificările"</string>
+ <string name="manage_notifications_text" msgid="6885645344647733116">"Gestionează"</string>
<string name="manage_notifications_history_text" msgid="57055985396576230">"Istoric"</string>
<string name="notification_section_header_incoming" msgid="850925217908095197">"Noi"</string>
<string name="notification_section_header_gentle" msgid="6804099527336337197">"Silențioase"</string>
<string name="notification_section_header_alerting" msgid="5581175033680477651">"Notificări"</string>
<string name="notification_section_header_conversations" msgid="821834744538345661">"Conversații"</string>
- <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Ștergeți toate notificările silențioase"</string>
+ <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Șterge toate notificările silențioase"</string>
<string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Notificări întrerupte prin „Nu deranja”"</string>
- <string name="media_projection_action_text" msgid="3634906766918186440">"Începeți acum"</string>
+ <string name="media_projection_action_text" msgid="3634906766918186440">"Începe acum"</string>
<string name="empty_shade_text" msgid="8935967157319717412">"Nicio notificare"</string>
<string name="quick_settings_disclosure_parental_controls" msgid="2114102871438223600">"Dispozitivul este gestionat de unul dintre părinți"</string>
<string name="quick_settings_disclosure_management_monitoring" msgid="8231336875820702180">"Organizația dvs. deține acest dispozitiv și poate monitoriza traficul de rețea"</string>
@@ -382,8 +382,8 @@
<string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>"</string>
<string name="quick_settings_disclosure_management_vpns" msgid="929181757984262902">"Acest dispozitiv aparține organizației dvs. și este conectat la internet prin rețele VPN."</string>
<string name="quick_settings_disclosure_named_management_vpns" msgid="3312645578322079185">"Acest dispozitiv aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> și este conectat la internet prin rețele VPN."</string>
- <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"Este posibil ca organizația dvs. să monitorizeze traficul de rețea în profilul dvs. de serviciu"</string>
- <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"Este posibil ca <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> să monitorizeze traficul de rețea din profilul dvs. de serviciu"</string>
+ <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"E posibil ca organizația ta să monitorizeze traficul de rețea în profilul de serviciu"</string>
+ <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"E posibil ca <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> să monitorizeze traficul de rețea din profilul tău de serviciu"</string>
<string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"Adminul IT poate vedea profilul de serviciu"</string>
<string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"Este posibil ca rețeaua să fie monitorizată"</string>
<string name="quick_settings_disclosure_vpns" msgid="3586175303518266301">"Acest dispozitiv este conectat la internet prin rețele VPN."</string>
@@ -395,40 +395,40 @@
<string name="monitoring_subtitle_vpn" msgid="800485258004629079">"VPN"</string>
<string name="monitoring_subtitle_network_logging" msgid="2444199331891219596">"Înregistrare în jurnal pentru rețea"</string>
<string name="monitoring_subtitle_ca_certificate" msgid="8588092029755175800">"Certificate CA"</string>
- <string name="monitoring_button_view_policies" msgid="3869724835853502410">"Afișați politicile"</string>
- <string name="monitoring_button_view_controls" msgid="8316440345340701117">"Vedeți opțiunile"</string>
+ <string name="monitoring_button_view_policies" msgid="3869724835853502410">"Afișează politicile"</string>
+ <string name="monitoring_button_view_controls" msgid="8316440345340701117">"Vezi opțiunile"</string>
<string name="monitoring_description_named_management" msgid="505833016545056036">"Dispozitivul aparține organizației <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>.\n\nAdministratorul dvs. IT poate să monitorizeze și să gestioneze setările, accesul la nivelul companiei, aplicațiile, datele asociate dispozitivului și informațiile despre locația dispozitivului.\n\nPentru mai multe informații, contactați administratorul IT."</string>
<string name="monitoring_financed_description_named_management" msgid="6108439201399938668">"Este posibil ca <xliff:g id="ORGANIZATION_NAME_0">%1$s</xliff:g> să acceseze date asociate dispozitivului, să gestioneze aplicații și să modifice setările acestuia.\n\nDacă aveți întrebări, luați legătura cu <xliff:g id="ORGANIZATION_NAME_1">%2$s</xliff:g>."</string>
<string name="monitoring_description_management" msgid="4308879039175729014">"Dispozitivul aparține organizației dvs.\n\nAdministratorul dvs. IT poate să monitorizeze și să gestioneze setările, accesul la nivelul companiei, aplicațiile, datele asociate dispozitivului și informațiile despre locația dispozitivului.\n\nPentru mai multe informații, contactați administratorul IT."</string>
- <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizația dvs. a instalat un certificat CA pe acest dispozitiv. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string>
- <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizația dvs. a instalat un certificat CA în profilul dvs. de serviciu. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string>
- <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Pe acest dispozitiv este instalat un certificat CA. Traficul dvs. sigur de rețea poate fi monitorizat sau modificat."</string>
- <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratorul dvs. a activat înregistrarea în jurnal pentru rețea, funcție ce monitorizează traficul de pe dispozitivul dvs."</string>
+ <string name="monitoring_description_management_ca_certificate" msgid="7785013130658110130">"Organizația ta a instalat un certificat CA pe acest dispozitiv. Traficul de rețea securizat poate fi monitorizat sau modificat."</string>
+ <string name="monitoring_description_managed_profile_ca_certificate" msgid="7904323416598435647">"Organizația ta a instalat un certificat CA în profilul tău de serviciu. Traficul de rețea securizat poate fi monitorizat sau modificat."</string>
+ <string name="monitoring_description_ca_certificate" msgid="448923057059097497">"Pe acest dispozitiv este instalat un certificat CA. Traficul de rețea securizat poate fi monitorizat sau modificat."</string>
+ <string name="monitoring_description_management_network_logging" msgid="216983105036994771">"Administratorul tău a activat înregistrarea în jurnal pentru rețea, funcție care monitorizează traficul de pe dispozitivul tău."</string>
<string name="monitoring_description_managed_profile_network_logging" msgid="6932303843097006037">"Administratorul a activat înregistrarea în jurnal pentru rețea, funcție ce monitorizează traficul în profilul dvs. de serviciu, dar nu și în profilul personal."</string>
<string name="monitoring_description_named_vpn" msgid="7502657784155456414">"Acest dispozitiv este conectat la internet prin aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>. Activitatea în rețea, inclusiv e-mailurile și datele de navigare, sunt vizibile pentru administratorul IT."</string>
<string name="monitoring_description_two_named_vpns" msgid="6726394451199620634">"Acest dispozitiv este conectat la internet prin aplicațiile <xliff:g id="VPN_APP_0">%1$s</xliff:g> și <xliff:g id="VPN_APP_1">%2$s</xliff:g>. Activitatea în rețea, inclusiv e-mailurile și datele de navigare, sunt vizibile pentru administratorul IT."</string>
<string name="monitoring_description_managed_profile_named_vpn" msgid="7254359257263069766">"Aplicațiile dvs. pentru lucru sunt conectate la internet prin aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>. Activitatea în rețea cu aplicațiile pentru lucru, inclusiv e-mailurile și datele de navigare, sunt vizibile pentru administratorul IT și pentru furnizorul de servicii VPN."</string>
<string name="monitoring_description_personal_profile_named_vpn" msgid="5083909710727365452">"Aplicațiile dvs. personale sunt conectate la internet prin aplicația <xliff:g id="VPN_APP">%1$s</xliff:g>. Activitatea în rețea, inclusiv e-mailurile și datele de navigare, sunt vizibile pentru furnizorul de servicii VPN."</string>
<string name="monitoring_description_vpn_settings_separator" msgid="8292589617720435430">" "</string>
- <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"Deschideți Setări VPN"</string>
+ <string name="monitoring_description_vpn_settings" msgid="5264167033247632071">"Deschide Setări VPN"</string>
<string name="monitoring_description_parental_controls" msgid="8184693528917051626">"Dispozitivul este gestionat de unul dintre părinți. Părintele poate să vadă și să gestioneze informații cum ar fi aplicațiile pe care le folosești, locația ta și durata de folosire a dispozitivului."</string>
<string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string>
<string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"Deblocat de TrustAgent"</string>
<string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>. <xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string>
<string name="accessibility_volume_settings" msgid="1458961116951564784">"Setări de sunet"</string>
- <string name="volume_odi_captions_tip" msgid="8825655463280990941">"Adăugați subtitrări automate la fișierele media"</string>
+ <string name="volume_odi_captions_tip" msgid="8825655463280990941">"Adaugă subtitrări automate la fișierele media"</string>
<string name="accessibility_volume_close_odi_captions_tip" msgid="8924753283621160480">"Sfat pentru subtitrări"</string>
<string name="volume_odi_captions_content_description" msgid="4172765742046013630">"Suprapunere pe subtitrări"</string>
- <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"activați"</string>
- <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"dezactivați"</string>
+ <string name="volume_odi_captions_hint_enable" msgid="2073091194012843195">"activează"</string>
+ <string name="volume_odi_captions_hint_disable" msgid="2518846326748183407">"dezactivează"</string>
<string name="sound_settings" msgid="8874581353127418308">"Sunete și vibrații"</string>
<string name="volume_panel_dialog_settings_button" msgid="2513228491513390310">"Setări"</string>
<string name="screen_pinning_title" msgid="9058007390337841305">"Aplicația este fixată"</string>
- <string name="screen_pinning_description" msgid="8699395373875667743">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunile Înapoi și Recente pentru a anula fixarea."</string>
- <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunile Înapoi și Acasă pentru a anula fixarea."</string>
- <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Astfel rămâne afișată până anulați fixarea. Glisați în sus și țineți apăsat pentru a anula fixarea."</string>
- <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunea Recente pentru a anula fixarea."</string>
- <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Astfel rămâne afișat până anulați fixarea. Atingeți lung opțiunea Acasă pentru a anula fixarea."</string>
+ <string name="screen_pinning_description" msgid="8699395373875667743">"Astfel rămâne afișat până anulezi fixarea. Atinge lung opțiunile Înapoi și Recente pentru a anula fixarea."</string>
+ <string name="screen_pinning_description_recents_invisible" msgid="4564466648700390037">"Astfel rămâne afișat până anulezi fixarea. Atinge lung opțiunile Înapoi și Acasă pentru a anula fixarea."</string>
+ <string name="screen_pinning_description_gestural" msgid="7246323931831232068">"Astfel rămâne afișată până anulați fixarea. Glisează în sus și ține apăsat pentru a anula fixarea."</string>
+ <string name="screen_pinning_description_accessible" msgid="7386449191953535332">"Astfel rămâne afișat până anulezi fixarea. Atinge lung opțiunea Recente pentru a anula fixarea."</string>
+ <string name="screen_pinning_description_recents_invisible_accessible" msgid="2857071808674481986">"Astfel rămâne afișat până anulezi fixarea. Atinge lung opțiunea Acasă pentru a anula fixarea."</string>
<string name="screen_pinning_exposes_personal_data" msgid="8189852022981524789">"Pot fi accesate date cu caracter personal (cum ar fi agenda și conținutul e-mailurilor)."</string>
<string name="screen_pinning_can_open_other_apps" msgid="7529756813231421455">"Aplicațiile fixate pot deschide alte aplicații."</string>
<string name="screen_pinning_toast" msgid="8177286912533744328">"Pentru a anula fixarea acestei aplicații, atingeți lung butoanele Înapoi și Recente"</string>
@@ -449,57 +449,57 @@
<string name="stream_accessibility" msgid="3873610336741987152">"Accesibilitate"</string>
<string name="volume_ringer_status_normal" msgid="1339039682222461143">"Sonerie"</string>
<string name="volume_ringer_status_vibrate" msgid="6970078708957857825">"Vibrații"</string>
- <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Blocați"</string>
- <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Atingeți pentru a activa sunetul."</string>
- <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Atingeți pentru a seta vibrarea. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
- <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Atingeți pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
- <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Atingeți pentru a seta pe vibrații."</string>
- <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Atingeți pentru a dezactiva sunetul."</string>
- <string name="volume_ringer_change" msgid="3574969197796055532">"Atingeți pentru a schimba modul soneriei"</string>
- <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivați sunetul"</string>
- <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activați sunetul"</string>
+ <string name="volume_ringer_status_silent" msgid="3691324657849880883">"Blochează"</string>
+ <string name="volume_stream_content_description_unmute" msgid="7729576371406792977">"%1$s. Atinge pentru a activa sunetul."</string>
+ <string name="volume_stream_content_description_vibrate" msgid="4858111994183089761">"%1$s. Atinge pentru a seta vibrarea. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
+ <string name="volume_stream_content_description_mute" msgid="4079046784917920984">"%1$s. Atinge pentru a dezactiva sunetul. Sunetul se poate dezactiva pentru serviciile de accesibilitate."</string>
+ <string name="volume_stream_content_description_vibrate_a11y" msgid="2742330052979397471">"%1$s. Atinge pentru a seta pe vibrații."</string>
+ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Atinge pentru a dezactiva sunetul."</string>
+ <string name="volume_ringer_change" msgid="3574969197796055532">"Atinge pentru a schimba modul soneriei"</string>
+ <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"dezactivează sunetul"</string>
+ <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"activează sunetul"</string>
<string name="volume_ringer_hint_vibrate" msgid="6211609047099337509">"vibrații"</string>
<string name="volume_dialog_title" msgid="6502703403483577940">"Comenzi de volum pentru %s"</string>
<string name="volume_dialog_ringer_guidance_ring" msgid="9143194270463146858">"Apelurile și notificările vor suna (<xliff:g id="VOLUME_LEVEL">%1$s</xliff:g>)"</string>
<string name="system_ui_tuner" msgid="1471348823289954729">"System UI Tuner"</string>
<string name="status_bar" msgid="4357390266055077437">"Bară de stare"</string>
<string name="demo_mode" msgid="263484519766901593">"Mod demonstrativ pentru IU sistem"</string>
- <string name="enable_demo_mode" msgid="3180345364745966431">"Activați modul demonstrativ"</string>
- <string name="show_demo_mode" msgid="3677956462273059726">"Afișați modul demonstrativ"</string>
+ <string name="enable_demo_mode" msgid="3180345364745966431">"Activează modul demonstrativ"</string>
+ <string name="show_demo_mode" msgid="3677956462273059726">"Afișează modul demonstrativ"</string>
<string name="status_bar_ethernet" msgid="5690979758988647484">"Ethernet"</string>
<string name="status_bar_alarm" msgid="87160847643623352">"Alarmă"</string>
<string name="wallet_title" msgid="5369767670735827105">"Portofel"</string>
<string name="wallet_empty_state_label" msgid="7776761245237530394">"Configurați pentru a face achiziții mai rapide și mai sigure cu telefonul dvs."</string>
- <string name="wallet_app_button_label" msgid="7123784239111190992">"Afișați-le pe toate"</string>
- <string name="wallet_secondary_label_no_card" msgid="8488069304491125713">"Atingeți pentru a deschide"</string>
+ <string name="wallet_app_button_label" msgid="7123784239111190992">"Afișează-le pe toate"</string>
+ <string name="wallet_secondary_label_no_card" msgid="8488069304491125713">"Atinge pentru a deschide"</string>
<string name="wallet_secondary_label_updating" msgid="5726130686114928551">"Se actualizează"</string>
- <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Deblocați pentru a folosi"</string>
- <string name="wallet_error_generic" msgid="257704570182963611">"A apărut o problemă la preluarea cardurilor. Încercați din nou mai târziu"</string>
+ <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Deblochează pentru a folosi"</string>
+ <string name="wallet_error_generic" msgid="257704570182963611">"A apărut o problemă la preluarea cardurilor. Încearcă din nou mai târziu"</string>
<string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Setările ecranului de blocare"</string>
<string name="qr_code_scanner_title" msgid="5290201053875420785">"Scanați codul QR"</string>
<string name="status_bar_work" msgid="5238641949837091056">"Profil de serviciu"</string>
<string name="status_bar_airplane" msgid="4848702508684541009">"Mod Avion"</string>
- <string name="zen_alarm_warning" msgid="7844303238486849503">"Nu veți auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
+ <string name="zen_alarm_warning" msgid="7844303238486849503">"Nu vei auzi următoarea alarmă <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template" msgid="2234991538018805736">"la <xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="alarm_template_far" msgid="3561752195856839456">"<xliff:g id="WHEN">%1$s</xliff:g>"</string>
<string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"Hotspot"</string>
<string name="accessibility_managed_profile" msgid="4703836746209377356">"Profil de serviciu"</string>
<string name="tuner_warning_title" msgid="7721976098452135267">"Distractiv pentru unii, dar nu pentru toată lumea"</string>
- <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner vă oferă modalități suplimentare de a ajusta și a personaliza interfața de utilizare Android. Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuați cu prudență."</string>
- <string name="tuner_persistent_warning" msgid="230466285569307806">"Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuați cu prudență."</string>
+ <string name="tuner_warning" msgid="1861736288458481650">"System UI Tuner oferă modalități suplimentare de a ajusta și a personaliza interfața de utilizare Android. Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuă cu prudență."</string>
+ <string name="tuner_persistent_warning" msgid="230466285569307806">"Aceste funcții experimentale pot să se schimbe, să se blocheze sau să dispară din versiunile viitoare. Continuă cu prudență."</string>
<string name="got_it" msgid="477119182261892069">"Am înțeles"</string>
<string name="tuner_toast" msgid="3812684836514766951">"Felicitări! System UI Tuner a fost adăugat în Setări"</string>
- <string name="remove_from_settings" msgid="633775561782209994">"Eliminați din Setări"</string>
- <string name="remove_from_settings_prompt" msgid="551565437265615426">"Eliminați System UI Tuner din Setări și încetați utilizarea tuturor funcțiilor sale?"</string>
- <string name="enable_bluetooth_title" msgid="866883307336662596">"Activați Bluetooth?"</string>
- <string name="enable_bluetooth_message" msgid="6740938333772779717">"Pentru a conecta tastatura la tabletă, mai întâi trebuie să activați Bluetooth."</string>
- <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activați"</string>
+ <string name="remove_from_settings" msgid="633775561782209994">"Elimină din Setări"</string>
+ <string name="remove_from_settings_prompt" msgid="551565437265615426">"Elimini System UI Tuner din Setări și încetezi utilizarea tuturor funcțiilor sale?"</string>
+ <string name="enable_bluetooth_title" msgid="866883307336662596">"Activezi Bluetooth?"</string>
+ <string name="enable_bluetooth_message" msgid="6740938333772779717">"Pentru a conecta tastatura la tabletă, mai întâi trebuie să activezi Bluetooth."</string>
+ <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"Activează"</string>
<string name="tuner_full_importance_settings" msgid="1388025816553459059">"Comenzi de gestionare a notificărilor"</string>
<string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Activată – În funcție de chip"</string>
- <string name="power_notification_controls_description" msgid="1334963837572708952">"Folosind comenzile de gestionare a notificărilor, puteți să setați un nivel de importanță de la 0 la 5 pentru notificările unei aplicații. \n\n"<b>"Nivelul 5"</b>" \n– Se afișează la începutul listei de notificări \n– Se permite întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 4"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 3"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n\n"<b>"Nivelul 2"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n\n"<b>"Nivelul 1"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n– Se ascunde în ecranul de blocare și în bara de stare \n– Se afișează la finalul listei de notificări \n\n"<b>"Nivelul 0"</b>" \n– Se blochează toate notificările din aplicație"</string>
+ <string name="power_notification_controls_description" msgid="1334963837572708952">"Folosind comenzile de gestionare a notificărilor, poți seta un nivel de importanță de la 0 la 5 pentru notificările unei aplicații. \n\n"<b>"Nivelul 5"</b>" \n– Se afișează la începutul listei de notificări \n– Se permite întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 4"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Se afișează întotdeauna scurt \n\n"<b>"Nivelul 3"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n\n"<b>"Nivelul 2"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n\n"<b>"Nivelul 1"</b>" \n– Se împiedică întreruperea pe ecranul complet \n– Nu se afișează niciodată scurt \n– Nu se emit sunete și nu vibrează niciodată \n– Se ascunde în ecranul de blocare și în bara de stare \n– Se afișează la finalul listei de notificări \n\n"<b>"Nivelul 0"</b>" \n– Se blochează toate notificările din aplicație"</string>
<string name="inline_done_button" msgid="6043094985588909584">"Gata"</string>
- <string name="inline_ok_button" msgid="603075490581280343">"Aplicați"</string>
- <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Dezactivați notificările"</string>
+ <string name="inline_ok_button" msgid="603075490581280343">"Aplică"</string>
+ <string name="inline_turn_off_notifications" msgid="8543989584403106071">"Dezactivează notificările"</string>
<string name="notification_silence_title" msgid="8608090968400832335">"Silențios"</string>
<string name="notification_alert_title" msgid="3656229781017543655">"Prestabilite"</string>
<string name="notification_automatic_title" msgid="3745465364578762652">"Automat"</string>
@@ -523,23 +523,23 @@
<string name="notification_multichannel_desc" msgid="7414593090056236179">"Acest grup de notificări nu poate fi configurat aici"</string>
<string name="notification_delegate_header" msgid="1264510071031479920">"Notificare prin proxy"</string>
<string name="notification_channel_dialog_title" msgid="6856514143093200019">"Toate notificările din <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
- <string name="see_more_title" msgid="7409317011708185729">"Vedeți mai multe"</string>
+ <string name="see_more_title" msgid="7409317011708185729">"Vezi mai multe"</string>
<string name="feedback_alerted" msgid="5192459808484271208">"Notificarea a fost &lt;b&gt;promovată automat la Prestabilită&lt;/b&gt; de sistem."</string>
<string name="feedback_silenced" msgid="9116540317466126457">"Notificarea a fost &lt;b&gt;setată automat ca Silențioasă&lt;/b&gt; de sistem."</string>
<string name="feedback_promoted" msgid="2125562787759780807">"Notificarea a fost &lt;b&gt;clasificată automat mai sus&lt;/b&gt; în umbră."</string>
<string name="feedback_demoted" msgid="951884763467110604">"Notificarea a fost &lt;b&gt;clasificată automat mai jos&lt;/b&gt; în umbră."</string>
- <string name="feedback_prompt" msgid="3656728972307896379">"Trimiteți feedback dezvoltatorului. Este corect?"</string>
+ <string name="feedback_prompt" msgid="3656728972307896379">"Trimite feedback dezvoltatorului. Este corect?"</string>
<string name="notification_channel_controls_opened_accessibility" msgid="6111817750774381094">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> sunt afișate"</string>
<string name="notification_channel_controls_closed_accessibility" msgid="1561909368876911701">"Opțiunile privind notificările pentru <xliff:g id="APP_NAME">%1$s</xliff:g> nu sunt afișate"</string>
<string name="notification_more_settings" msgid="4936228656989201793">"Mai multe setări"</string>
- <string name="notification_app_settings" msgid="8963648463858039377">"Personalizați"</string>
- <string name="notification_conversation_bubble" msgid="2242180995373949022">"Afișați balonul"</string>
- <string name="notification_conversation_unbubble" msgid="6908427185031099868">"Eliminați baloanele"</string>
+ <string name="notification_app_settings" msgid="8963648463858039377">"Personalizează"</string>
+ <string name="notification_conversation_bubble" msgid="2242180995373949022">"Afișează balonul"</string>
+ <string name="notification_conversation_unbubble" msgid="6908427185031099868">"Elimină baloanele"</string>
<string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
<string name="notification_menu_gear_description" msgid="6429668976593634862">"comenzile notificării"</string>
<string name="notification_menu_snooze_description" msgid="4740133348901973244">"opțiuni de amânare a notificării"</string>
<string name="notification_menu_snooze_action" msgid="5415729610393475019">"Reamintește-mi"</string>
- <string name="snooze_undo" msgid="2738844148845992103">"Anulați"</string>
+ <string name="snooze_undo" msgid="2738844148845992103">"Anulează"</string>
<string name="snoozed_for_time" msgid="7586689374860469469">"Amânată <xliff:g id="TIME_AMOUNT">%1$s</xliff:g>"</string>
<string name="snoozeHourOptions" msgid="2332819756222425558">"{count,plural, =1{# oră}=2{# ore}few{# ore}other{# de ore}}"</string>
<string name="snoozeMinuteOptions" msgid="2222082405822030979">"{count,plural, =1{# minut}few{# minute}other{# de minute}}"</string>
@@ -556,28 +556,28 @@
<string name="keyboard_key_space" msgid="6980847564173394012">"Spațiu"</string>
<string name="keyboard_key_enter" msgid="8633362970109751646">"Enter"</string>
<string name="keyboard_key_backspace" msgid="4095278312039628074">"Backspace"</string>
- <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Redați/Întrerupeți"</string>
- <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Opriți"</string>
+ <string name="keyboard_key_media_play_pause" msgid="8389984232732277478">"Redă/Întrerupe"</string>
+ <string name="keyboard_key_media_stop" msgid="1509943745250377699">"Oprește"</string>
<string name="keyboard_key_media_next" msgid="8502476691227914952">"Înainte"</string>
<string name="keyboard_key_media_previous" msgid="5637875709190955351">"Înapoi"</string>
- <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Derulați înapoi"</string>
- <string name="keyboard_key_media_fast_forward" msgid="3572444327046911822">"Derulați rapid înainte"</string>
+ <string name="keyboard_key_media_rewind" msgid="3450387734224327577">"Derulează înapoi"</string>
+ <string name="keyboard_key_media_fast_forward" msgid="3572444327046911822">"Derulează rapid înainte"</string>
<string name="keyboard_key_page_up" msgid="173914303254199845">"O pagină mai sus"</string>
<string name="keyboard_key_page_down" msgid="9035902490071829731">"O pagină mai jos"</string>
- <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Ștergeți"</string>
+ <string name="keyboard_key_forward_del" msgid="5325501825762733459">"Șterge"</string>
<string name="keyboard_key_move_home" msgid="3496502501803911971">"La început"</string>
<string name="keyboard_key_move_end" msgid="99190401463834854">"La final"</string>
- <string name="keyboard_key_insert" msgid="4621692715704410493">"Inserați"</string>
+ <string name="keyboard_key_insert" msgid="4621692715704410493">"Inserează"</string>
<string name="keyboard_key_num_lock" msgid="7209960042043090548">"Num Lock"</string>
<string name="keyboard_key_numpad_template" msgid="7316338238459991821">"Tasta numerică <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="notif_inline_reply_remove_attachment_description" msgid="7954075334095405429">"Eliminați atașamentul"</string>
+ <string name="notif_inline_reply_remove_attachment_description" msgid="7954075334095405429">"Elimină atașamentul"</string>
<string name="keyboard_shortcut_group_system" msgid="1583416273777875970">"Sistem"</string>
<string name="keyboard_shortcut_group_system_home" msgid="7465138628692109907">"Ecran de pornire"</string>
<string name="keyboard_shortcut_group_system_recents" msgid="8628108256824616927">"Recente"</string>
<string name="keyboard_shortcut_group_system_back" msgid="1055709713218453863">"Înapoi"</string>
<string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Notificări"</string>
<string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Comenzi rapide de la tastatură"</string>
- <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Schimbați aspectul tastaturii"</string>
+ <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Schimbă aspectul tastaturii"</string>
<string name="keyboard_shortcut_group_applications" msgid="7386239431100651266">"Aplicații"</string>
<string name="keyboard_shortcut_group_applications_assist" msgid="771606231466098742">"Asistent"</string>
<string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"Browser"</string>
@@ -590,16 +590,15 @@
<string name="volume_dnd_silent" msgid="4154597281458298093">"Comandă rapidă din butoanele de volum"</string>
<string name="battery" msgid="769686279459897127">"Baterie"</string>
<string name="headset" msgid="4485892374984466437">"Set căști-microfon"</string>
- <string name="accessibility_long_click_tile" msgid="210472753156768705">"Deschideți setările"</string>
+ <string name="accessibility_long_click_tile" msgid="210472753156768705">"Deschide setările"</string>
<string name="accessibility_status_bar_headphones" msgid="1304082414912647414">"Căștile sunt conectate"</string>
<string name="accessibility_status_bar_headset" msgid="2699275863720926104">"Setul căști-microfon este conectat"</string>
<string name="data_saver" msgid="3484013368530820763">"Economizor de date"</string>
<string name="accessibility_data_saver_on" msgid="5394743820189757731">"Economizorul de date este activat"</string>
<string name="switch_bar_on" msgid="1770868129120096114">"Activat"</string>
- <string name="switch_bar_off" msgid="5669805115416379556">"Dezactivați"</string>
+ <string name="switch_bar_off" msgid="5669805115416379556">"Dezactivează"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Indisponibil"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"a afla mai multe"</string>
<string name="nav_bar" msgid="4642708685386136807">"Bară de navigare"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Aspect"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Tip de buton din extrema stângă"</string>
@@ -607,7 +606,7 @@
<string-array name="nav_bar_buttons">
<item msgid="2681220472659720036">"Clipboard"</item>
<item msgid="4795049793625565683">"Cod de tastă"</item>
- <item msgid="80697951177515644">"Confirmați rotirea, comutator de la tastatură"</item>
+ <item msgid="80697951177515644">"Confirmă rotirea, comutator de la tastatură"</item>
<item msgid="7626977989589303588">"Niciunul"</item>
</string-array>
<string-array name="nav_bar_layouts">
@@ -616,19 +615,19 @@
<item msgid="7453955063378349599">"Înclinat spre stânga"</item>
<item msgid="5874146774389433072">"Înclinat spre dreapta"</item>
</string-array>
- <string name="save" msgid="3392754183673848006">"Salvați"</string>
- <string name="reset" msgid="8715144064608810383">"Resetați"</string>
+ <string name="save" msgid="3392754183673848006">"Salvează"</string>
+ <string name="reset" msgid="8715144064608810383">"Resetează"</string>
<string name="clipboard" msgid="8517342737534284617">"Clipboard"</string>
<string name="accessibility_key" msgid="3471162841552818281">"Buton personalizat pentru navigare"</string>
<string name="left_keycode" msgid="8211040899126637342">"Codul de taste din stânga"</string>
<string name="right_keycode" msgid="2480715509844798438">"Codul de taste din dreapta"</string>
<string name="left_icon" msgid="5036278531966897006">"Pictograma din stânga"</string>
<string name="right_icon" msgid="1103955040645237425">"Pictograma din dreapta"</string>
- <string name="drag_to_add_tiles" msgid="8933270127508303672">"Țineți apăsat și trageți pentru a adăuga piese"</string>
- <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Țineți apăsat și trageți pentru a rearanja piesele"</string>
- <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Trageți aici pentru a elimina"</string>
- <string name="drag_to_remove_disabled" msgid="933046987838658850">"Aveți nevoie de cel puțin <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> carduri"</string>
- <string name="qs_edit" msgid="5583565172803472437">"Editați"</string>
+ <string name="drag_to_add_tiles" msgid="8933270127508303672">"Ține apăsat și trage pentru a adăuga carduri"</string>
+ <string name="drag_to_rearrange_tiles" msgid="2143204300089638620">"Ține apăsat și trage pentru a rearanja cardurile"</string>
+ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Trage aici pentru a elimina"</string>
+ <string name="drag_to_remove_disabled" msgid="933046987838658850">"Ai nevoie de cel puțin <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> carduri"</string>
+ <string name="qs_edit" msgid="5583565172803472437">"Editează"</string>
<string name="tuner_time" msgid="2450785840990529997">"Oră"</string>
<string-array name="clock_options">
<item msgid="3986445361435142273">"Afișează orele, minutele și secundele"</item>
@@ -640,49 +639,47 @@
<item msgid="3805744470661798712">"Afișează procentajul când se încarcă (prestabilit)"</item>
<item msgid="8619482474544321778">"Nu afișa această pictogramă"</item>
</string-array>
- <string name="tuner_low_priority" msgid="8412666814123009820">"Afișați pictogramele de notificare cu prioritate redusă"</string>
+ <string name="tuner_low_priority" msgid="8412666814123009820">"Afișează pictogramele de notificare cu prioritate redusă"</string>
<string name="other" msgid="429768510980739978">"Altele"</string>
<string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"eliminați cardul"</string>
<string name="accessibility_qs_edit_tile_add_action" msgid="5051211910345301833">"adăugați cardul la sfârșit"</string>
- <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mutați cardul"</string>
- <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adăugați un card"</string>
- <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mutați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
- <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adăugați pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mută cardul"</string>
+ <string name="accessibility_qs_edit_tile_start_add" msgid="7560798153975555772">"Adaugă un card"</string>
+ <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mută pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
+ <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adaugă pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_position" msgid="4509277359815711830">"Poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
<string name="accessibility_qs_edit_tile_added" msgid="9067146040380836334">"Cardul a fost adăugat"</string>
<string name="accessibility_qs_edit_tile_removed" msgid="1175925632436612036">"Cardul a fost eliminat"</string>
<string name="accessibility_desc_quick_settings_edit" msgid="741658939453595297">"Editorul pentru setări rapide."</string>
<string name="accessibility_desc_notification_icon" msgid="7331265967584178674">"Notificare <xliff:g id="ID_1">%1$s</xliff:g>: <xliff:g id="ID_2">%2$s</xliff:g>"</string>
- <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Deschideți setările."</string>
- <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Deschideți setările rapide."</string>
- <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Închideți setările rapide."</string>
+ <string name="accessibility_quick_settings_settings" msgid="7098489591715844713">"Deschide setările."</string>
+ <string name="accessibility_quick_settings_expand" msgid="2609275052412521467">"Deschide setările rapide."</string>
+ <string name="accessibility_quick_settings_collapse" msgid="4674876336725041982">"Închide setările rapide."</string>
<string name="accessibility_quick_settings_user" msgid="505821942882668619">"Conectat(ă) ca <xliff:g id="ID_1">%s</xliff:g>"</string>
<string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"alege utilizatorul"</string>
<string name="data_connection_no_internet" msgid="691058178914184544">"Fără conexiune la internet"</string>
- <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Deschideți setările <xliff:g id="ID_1">%s</xliff:g>."</string>
- <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editați ordinea setărilor."</string>
+ <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Deschide setările <xliff:g id="ID_1">%s</xliff:g>."</string>
+ <string name="accessibility_quick_settings_edit" msgid="1523745183383815910">"Editează ordinea setărilor."</string>
<string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Meniul de pornire"</string>
<string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> din <xliff:g id="ID_2">%2$d</xliff:g>"</string>
<string name="tuner_lock_screen" msgid="2267383813241144544">"Ecran de blocare"</string>
<string name="thermal_shutdown_title" msgid="2702966892682930264">"Telefonul s-a oprit din cauza încălzirii"</string>
- <string name="thermal_shutdown_message" msgid="6142269839066172984">"Acum telefonul funcționează normal.\nAtingeți pentru mai multe informații"</string>
- <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonul se încălzise prea mult și s-a oprit pentru a se răci. Acum telefonul funcționează normal.\n\nTelefonul s-ar putea încălzi prea mult dacă:\n • folosiți aplicații care consumă multe resurse (de ex., jocuri, aplicații video/de navigare);\n • descărcați/încărcați fișiere mari;\n • folosiți telefonul la temperaturi ridicate."</string>
- <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Vedeți pașii pentru îngrijire"</string>
+ <string name="thermal_shutdown_message" msgid="6142269839066172984">"Acum telefonul funcționează normal.\nAtinge pentru mai multe informații"</string>
+ <string name="thermal_shutdown_dialog_message" msgid="6745684238183492031">"Telefonul se încălzise prea mult și s-a oprit pentru a se răci. Acum telefonul funcționează normal.\n\nTelefonul s-ar putea încălzi prea mult dacă:\n • folosești aplicații care consumă multe resurse (de ex., jocuri, aplicații video/de navigare);\n • descarci/încarci fișiere mari;\n • folosești telefonul la temperaturi ridicate."</string>
+ <string name="thermal_shutdown_dialog_help_text" msgid="6413474593462902901">"Vezi pașii pentru îngrijire"</string>
<string name="high_temp_title" msgid="2218333576838496100">"Telefonul se încălzește"</string>
- <string name="high_temp_notif_message" msgid="1277346543068257549">"Anumite funcții sunt limitate în timp ce telefonul se răcește.\nAtingeți pentru mai multe informații"</string>
- <string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonul va încerca automat să se răcească. Puteți folosi telefonul în continuare, dar este posibil să funcționeze mai lent.\n\nDupă ce se răcește, telefonul va funcționa normal."</string>
- <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Vedeți pașii pentru îngrijire"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
- <string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Vedeți pașii pentru îngrijire"</string>
+ <string name="high_temp_notif_message" msgid="1277346543068257549">"Anumite funcții sunt limitate în timp ce telefonul se răcește.\nAtinge pentru mai multe informații"</string>
+ <string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonul va încerca automat să se răcească. Îl poți folosi în continuare, dar e posibil să funcționeze mai lent.\n\nDupă ce se răcește, telefonul va funcționa normal."</string>
+ <string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Vezi pașii pentru îngrijire"</string>
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Deconectează dispozitivul"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Dispozitivul se încălzește lângă portul de încărcare. Dacă este conectat la un încărcător sau accesoriu USB, deconectează-l și ai grijă, deoarece și cablul poate fi cald."</string>
+ <string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Vezi pașii pentru îngrijire"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Comanda rapidă din stânga"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Comanda rapidă din dreapta"</string>
<string name="lockscreen_unlock_left" msgid="1417801334370269374">"Comanda rapidă din stânga și deblochează"</string>
<string name="lockscreen_unlock_right" msgid="4658008735541075346">"Comanda rapidă din dreapta și deblochează"</string>
<string name="lockscreen_none" msgid="4710862479308909198">"Niciuna"</string>
- <string name="tuner_launch_app" msgid="3906265365971743305">"Lansați <xliff:g id="APP">%1$s</xliff:g>"</string>
+ <string name="tuner_launch_app" msgid="3906265365971743305">"Lansează <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="tuner_other_apps" msgid="7767462881742291204">"Alte aplicații"</string>
<string name="tuner_circle" msgid="5270591778160525693">"Cerc"</string>
<string name="tuner_plus" msgid="4130366441154416484">"Plus"</string>
@@ -701,9 +698,9 @@
<string name="instant_apps" msgid="8337185853050247304">"Aplicații instantanee"</string>
<string name="instant_apps_title" msgid="8942706782103036910">"<xliff:g id="APP">%1$s</xliff:g> rulează"</string>
<string name="instant_apps_message" msgid="6112428971833011754">"Aplicația a fost deschisă fără a fi instalată."</string>
- <string name="instant_apps_message_with_help" msgid="1816952263531203932">"Aplicația a fost deschisă fără a fi instalată. Atingeți pentru a afla mai multe."</string>
+ <string name="instant_apps_message_with_help" msgid="1816952263531203932">"Aplicația a fost deschisă fără a fi instalată. Atinge pentru a afla mai multe."</string>
<string name="app_info" msgid="5153758994129963243">"Informații aplicație"</string>
- <string name="go_to_web" msgid="636673528981366511">"Accesați browserul"</string>
+ <string name="go_to_web" msgid="636673528981366511">"Accesează browserul"</string>
<string name="mobile_data" msgid="4564407557775397216">"Date mobile"</string>
<string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string>
<string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string>
@@ -714,21 +711,21 @@
<string name="qs_dnd_prompt_app" msgid="4027984447935396820">"Funcția Nu deranja a fost activată de o aplicație (<xliff:g id="ID_1">%s</xliff:g>)."</string>
<string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"Funcția Nu deranja a fost activată de o regulă automată sau de o aplicație."</string>
<string name="running_foreground_services_title" msgid="5137313173431186685">"Aplicațiile rulează în fundal"</string>
- <string name="running_foreground_services_msg" msgid="3009459259222695385">"Atingeți pentru mai multe detalii privind bateria și utilizarea datelor"</string>
- <string name="mobile_data_disable_title" msgid="5366476131671617790">"Dezactivați datele mobile?"</string>
- <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nu veți avea acces la date sau la internet prin intermediul <xliff:g id="CARRIER">%s</xliff:g>. Internetul va fi disponibil numai prin Wi-Fi."</string>
- <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatorul dvs."</string>
- <string name="touch_filtered_warning" msgid="8119511393338714836">"Deoarece o aplicație acoperă o solicitare de permisiune, Setările nu vă pot verifica răspunsul."</string>
- <string name="slice_permission_title" msgid="3262615140094151017">"Permiteți <xliff:g id="APP_0">%1$s</xliff:g> să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
+ <string name="running_foreground_services_msg" msgid="3009459259222695385">"Atinge pentru mai multe detalii privind bateria și utilizarea datelor"</string>
+ <string name="mobile_data_disable_title" msgid="5366476131671617790">"Dezactivezi datele mobile?"</string>
+ <string name="mobile_data_disable_message" msgid="8604966027899770415">"Nu vei avea acces la date sau la internet prin intermediul <xliff:g id="CARRIER">%s</xliff:g>. Internetul va fi disponibil numai prin Wi-Fi."</string>
+ <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"operatorul tău"</string>
+ <string name="touch_filtered_warning" msgid="8119511393338714836">"Deoarece o aplicație acoperă o solicitare de permisiune, Setările nu îți pot verifica răspunsul."</string>
+ <string name="slice_permission_title" msgid="3262615140094151017">"Permiți ca <xliff:g id="APP_0">%1$s</xliff:g> să afișeze porțiuni din <xliff:g id="APP_2">%2$s</xliff:g>?"</string>
<string name="slice_permission_text_1" msgid="6675965177075443714">"- Poate citi informații din <xliff:g id="APP">%1$s</xliff:g>"</string>
<string name="slice_permission_text_2" msgid="6758906940360746983">"- Poate efectua acțiuni în <xliff:g id="APP">%1$s</xliff:g>"</string>
- <string name="slice_permission_checkbox" msgid="4242888137592298523">"Permiteți <xliff:g id="APP">%1$s</xliff:g> să afișeze porțiuni din orice aplicație"</string>
- <string name="slice_permission_allow" msgid="6340449521277951123">"Permiteți"</string>
- <string name="slice_permission_deny" msgid="6870256451658176895">"Refuzați"</string>
- <string name="auto_saver_title" msgid="6873691178754086596">"Atingeți pentru a programa Economisirea energiei"</string>
- <string name="auto_saver_text" msgid="3214960308353838764">"Porniți dacă este probabil ca bateria să se descarce"</string>
+ <string name="slice_permission_checkbox" msgid="4242888137592298523">"Permite <xliff:g id="APP">%1$s</xliff:g> să afișeze porțiuni din orice aplicație"</string>
+ <string name="slice_permission_allow" msgid="6340449521277951123">"Permite"</string>
+ <string name="slice_permission_deny" msgid="6870256451658176895">"Refuz"</string>
+ <string name="auto_saver_title" msgid="6873691178754086596">"Atinge pentru a programa Economisirea energiei"</string>
+ <string name="auto_saver_text" msgid="3214960308353838764">"Pornește dacă e probabil ca bateria să se descarce"</string>
<string name="no_auto_saver_action" msgid="7467924389609773835">"Nu, mulțumesc"</string>
- <string name="heap_dump_tile_name" msgid="2464189856478823046">"Extrageți memoria SysUI"</string>
+ <string name="heap_dump_tile_name" msgid="2464189856478823046">"Extrage memoria SysUI"</string>
<string name="ongoing_privacy_dialog_a11y_title" msgid="2205794093673327974">"În uz"</string>
<string name="ongoing_privacy_chip_content_multiple_apps" msgid="8341216022442383954">"Aplicațiile folosesc <xliff:g id="TYPES_LIST">%s</xliff:g>."</string>
<string name="ongoing_privacy_dialog_separator" msgid="1866222499727706187">", "</string>
@@ -775,16 +772,16 @@
<string name="accessibility_magnifier_edit" msgid="1522877239671820636">"Editează"</string>
<string name="accessibility_magnification_magnifier_window_settings" msgid="2834685072221468434">"Setările ferestrei de mărire"</string>
<string name="accessibility_floating_button_migration_tooltip" msgid="5217151214439341902">"Atingeți pentru a deschide funcțiile de accesibilitate. Personalizați sau înlocuiți butonul în Setări.\n\n"<annotation id="link">"Afișați setările"</annotation></string>
- <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mutați butonul spre margine pentru a-l ascunde temporar"</string>
- <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mutați în stânga sus"</string>
- <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mutați în dreapta sus"</string>
- <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mutați în stânga jos"</string>
- <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mutați în dreapta jos"</string>
+ <string name="accessibility_floating_button_docking_tooltip" msgid="6814897496767461517">"Mută butonul spre margine pentru a-l ascunde temporar"</string>
+ <string name="accessibility_floating_button_action_move_top_left" msgid="6253520703618545705">"Mută în stânga sus"</string>
+ <string name="accessibility_floating_button_action_move_top_right" msgid="6106225581993479711">"Mută în dreapta sus"</string>
+ <string name="accessibility_floating_button_action_move_bottom_left" msgid="8063394111137429725">"Mută în stânga jos"</string>
+ <string name="accessibility_floating_button_action_move_bottom_right" msgid="6196904373227440500">"Mută în dreapta jos"</string>
<string name="accessibility_floating_button_action_move_to_edge_and_hide_to_half" msgid="662401168245782658">"Mutați în afară și ascundeți"</string>
<string name="accessibility_floating_button_action_move_out_edge_and_show" msgid="8354760891651663326">"Mutați în afară și afișați"</string>
<string name="accessibility_floating_button_action_double_tap_to_toggle" msgid="7976492639670692037">"Activați / dezactivați"</string>
<string name="quick_controls_title" msgid="6839108006171302273">"Comenzile dispozitivelor"</string>
- <string name="controls_providers_title" msgid="6879775889857085056">"Alegeți aplicația pentru a adăuga comenzi"</string>
+ <string name="controls_providers_title" msgid="6879775889857085056">"Alege aplicația pentru a adăuga comenzi"</string>
<string name="controls_number_of_favorites" msgid="4481806788981836355">"{count,plural, =1{S-a adăugat # comandă.}few{S-au adăugat # comenzi.}other{S-au adăugat # de comenzi.}}"</string>
<string name="controls_removed" msgid="3731789252222856959">"Eliminată"</string>
<string name="accessibility_control_favorite" msgid="8694362691985545985">"Marcată ca preferată"</string>
@@ -792,18 +789,18 @@
<string name="accessibility_control_not_favorite" msgid="1291760269563092359">"S-a anulat marcarea ca preferată"</string>
<string name="accessibility_control_change_favorite" msgid="2943178027582253261">"marcați ca preferată"</string>
<string name="accessibility_control_change_unfavorite" msgid="6997408061750740327">"anulați marcarea ca preferată"</string>
- <string name="accessibility_control_move" msgid="8980344493796647792">"Mutați pe poziția <xliff:g id="NUMBER">%d</xliff:g>"</string>
+ <string name="accessibility_control_move" msgid="8980344493796647792">"Mută pe poziția <xliff:g id="NUMBER">%d</xliff:g>"</string>
<string name="controls_favorite_default_title" msgid="967742178688938137">"Comenzi"</string>
- <string name="controls_favorite_subtitle" msgid="6481675111056961083">"Alegeți comenzile de accesat din Setările rapide"</string>
+ <string name="controls_favorite_subtitle" msgid="6481675111056961083">"Alege comenzile de accesat din Setările rapide"</string>
<string name="controls_favorite_rearrange" msgid="5616952398043063519">"Țineți apăsat și trageți pentru a rearanja comenzile"</string>
<string name="controls_favorite_removed" msgid="5276978408529217272">"Au fost șterse toate comenzile"</string>
<string name="controls_favorite_toast_no_changes" msgid="7094494210840877931">"Modificările nu au fost salvate"</string>
- <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Vedeți alte aplicații"</string>
+ <string name="controls_favorite_see_other_apps" msgid="7709087332255283460">"Vezi alte aplicații"</string>
<string name="controls_favorite_load_error" msgid="5126216176144877419">"Comenzile nu au putut fi încărcate. Accesați aplicația <xliff:g id="APP">%s</xliff:g> pentru a vă asigura că setările aplicației nu s-au schimbat."</string>
<string name="controls_favorite_load_none" msgid="7687593026725357775">"Nu sunt disponibile comenzi compatibile"</string>
<string name="controls_favorite_other_zone_header" msgid="9089613266575525252">"Altul"</string>
- <string name="controls_dialog_title" msgid="2343565267424406202">"Adăugați la comenzile dispozitivelor"</string>
- <string name="controls_dialog_ok" msgid="2770230012857881822">"Adăugați"</string>
+ <string name="controls_dialog_title" msgid="2343565267424406202">"Adaugă la comenzile dispozitivelor"</string>
+ <string name="controls_dialog_ok" msgid="2770230012857881822">"Adaugă"</string>
<string name="controls_dialog_message" msgid="342066938390663844">"Sugerat de <xliff:g id="APP">%s</xliff:g>"</string>
<string name="controls_tile_locked" msgid="731547768182831938">"Dispozitiv blocat"</string>
<string name="controls_settings_show_controls_dialog_title" msgid="3357852503553809554">"Vedeți și controlați dispozitivele de pe ecranul de blocare?"</string>
@@ -815,10 +812,10 @@
<string name="controls_pin_use_alphanumeric" msgid="8478371861023048414">"Codul PIN conține litere sau simboluri"</string>
<string name="controls_pin_verify" msgid="3452778292918877662">"Verificați <xliff:g id="DEVICE">%s</xliff:g>"</string>
<string name="controls_pin_wrong" msgid="6162694056042164211">"Cod PIN greșit"</string>
- <string name="controls_pin_instructions" msgid="6363309783822475238">"Introduceți codul PIN"</string>
- <string name="controls_pin_instructions_retry" msgid="1566667581012131046">"Încercați alt cod PIN"</string>
- <string name="controls_confirmation_message" msgid="7744104992609594859">"Confirmați schimbarea pentru <xliff:g id="DEVICE">%s</xliff:g>"</string>
- <string name="controls_structure_tooltip" msgid="4355922222944447867">"Glisați pentru a vedea mai multe"</string>
+ <string name="controls_pin_instructions" msgid="6363309783822475238">"Introdu codul PIN"</string>
+ <string name="controls_pin_instructions_retry" msgid="1566667581012131046">"Încearcă alt cod PIN"</string>
+ <string name="controls_confirmation_message" msgid="7744104992609594859">"Confirmă schimbarea pentru <xliff:g id="DEVICE">%s</xliff:g>"</string>
+ <string name="controls_structure_tooltip" msgid="4355922222944447867">"Glisează pentru a vedea mai multe"</string>
<string name="controls_seeding_in_progress" msgid="3033855341410264148">"Se încarcă recomandările"</string>
<string name="controls_media_title" msgid="1746947284862928133">"Media"</string>
<string name="controls_media_close_session" msgid="4780485355795635052">"Ascundeți comanda media pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
@@ -828,36 +825,36 @@
<string name="controls_media_settings_button" msgid="5815790345117172504">"Setări"</string>
<string name="controls_media_playing_item_description" msgid="4531853311504359098">"<xliff:g id="SONG_NAME">%1$s</xliff:g> de la <xliff:g id="ARTIST_NAME">%2$s</xliff:g> se redă în <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
<string name="controls_media_seekbar_description" msgid="4389621713616214611">"<xliff:g id="ELAPSED_TIME">%1$s</xliff:g> din <xliff:g id="TOTAL_TIME">%2$s</xliff:g>"</string>
- <string name="controls_media_button_play" msgid="2705068099607410633">"Redați"</string>
- <string name="controls_media_button_pause" msgid="8614887780950376258">"Întrerupeți"</string>
+ <string name="controls_media_button_play" msgid="2705068099607410633">"Redă"</string>
+ <string name="controls_media_button_pause" msgid="8614887780950376258">"Întrerupe"</string>
<string name="controls_media_button_prev" msgid="8126822360056482970">"Melodia anterioară"</string>
<string name="controls_media_button_next" msgid="6662636627525947610">"Melodia următoare"</string>
<string name="controls_media_button_connecting" msgid="3138354625847598095">"Se conectează"</string>
- <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"Redați"</string>
- <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Deschideți <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
- <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Redați <xliff:g id="SONG_NAME">%1$s</xliff:g> de la <xliff:g id="ARTIST_NAME">%2$s</xliff:g> în <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
- <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Redați <xliff:g id="SONG_NAME">%1$s</xliff:g> în <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
- <string name="media_transfer_undo" msgid="1895606387620728736">"Anulați"</string>
+ <string name="controls_media_smartspace_rec_title" msgid="1699818353932537407">"Redă"</string>
+ <string name="controls_media_smartspace_rec_description" msgid="4136242327044070732">"Deschide <xliff:g id="APP_LABEL">%1$s</xliff:g>"</string>
+ <string name="controls_media_smartspace_rec_item_description" msgid="2189271793070870883">"Redă <xliff:g id="SONG_NAME">%1$s</xliff:g> de la <xliff:g id="ARTIST_NAME">%2$s</xliff:g> în <xliff:g id="APP_LABEL">%3$s</xliff:g>"</string>
+ <string name="controls_media_smartspace_rec_item_no_artist_description" msgid="8703614798636591077">"Redă <xliff:g id="SONG_NAME">%1$s</xliff:g> în <xliff:g id="APP_LABEL">%2$s</xliff:g>"</string>
+ <string name="media_transfer_undo" msgid="1895606387620728736">"Anulează"</string>
<string name="media_move_closer_to_start_cast" msgid="2673104707465013176">"Apropiați-vă pentru a reda pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
<string name="media_move_closer_to_end_cast" msgid="6495907340926563656">"Mergeți mai aproape de <xliff:g id="DEVICENAME">%1$s</xliff:g> ca să redați acolo"</string>
<string name="media_transfer_playing_different_device" msgid="7186806382609785610">"Se redă pe <xliff:g id="DEVICENAME">%1$s</xliff:g>"</string>
- <string name="media_transfer_failed" msgid="7955354964610603723">"A apărut o eroare. Încercați din nou."</string>
+ <string name="media_transfer_failed" msgid="7955354964610603723">"A apărut o eroare. Încearcă din nou."</string>
<string name="controls_error_timeout" msgid="794197289772728958">"Inactiv, verificați aplicația"</string>
<string name="controls_error_removed" msgid="6675638069846014366">"Nu s-a găsit"</string>
<string name="controls_error_removed_title" msgid="1207794911208047818">"Comanda este indisponibilă"</string>
<string name="controls_error_removed_message" msgid="2885911717034750542">"Nu s-a putut accesa <xliff:g id="DEVICE">%1$s</xliff:g>. Accesați aplicația <xliff:g id="APPLICATION">%2$s</xliff:g> pentru a vă asigura de disponibilitatea comenzii și că setările aplicației nu s-au schimbat."</string>
- <string name="controls_open_app" msgid="483650971094300141">"Deschideți aplicația"</string>
+ <string name="controls_open_app" msgid="483650971094300141">"Deschide aplicația"</string>
<string name="controls_error_generic" msgid="352500456918362905">"Starea nu se poate încărca"</string>
<string name="controls_error_failed" msgid="960228639198558525">"Eroare, încercați din nou"</string>
- <string name="controls_menu_add" msgid="4447246119229920050">"Adăugați comenzi"</string>
- <string name="controls_menu_edit" msgid="890623986951347062">"Editați comenzile"</string>
- <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adăugați ieșiri"</string>
+ <string name="controls_menu_add" msgid="4447246119229920050">"Adaugă comenzi"</string>
+ <string name="controls_menu_edit" msgid="890623986951347062">"Editează comenzile"</string>
+ <string name="media_output_dialog_add_output" msgid="5642703238877329518">"Adaugă ieșiri"</string>
<string name="media_output_dialog_group" msgid="5571251347877452212">"Grup"</string>
<string name="media_output_dialog_single_device" msgid="3102758980643351058">"S-a selectat un dispozitiv"</string>
<string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"S-au selectat <xliff:g id="COUNT">%1$d</xliff:g> dispozitive"</string>
<string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(deconectat)"</string>
- <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nu se poate comuta. Atingeți pentru a încerca din nou."</string>
- <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Conectați un dispozitiv"</string>
+ <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"Nu se poate comuta. Atinge pentru a încerca din nou."</string>
+ <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"Conectează un dispozitiv"</string>
<string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"Pentru a proiecta această sesiune, deschideți aplicația."</string>
<string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"Aplicație necunoscută"</string>
<string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"Nu mai proiectați"</string>
@@ -869,14 +866,14 @@
<string name="media_output_broadcasting_message" msgid="4150299923404886073">"Ca să asculte transmisia dvs., persoanele din apropiere cu dispozitive Bluetooth compatibile vă pot scana codul QR sau pot folosi numele și parola transmisiei."</string>
<string name="media_output_broadcast_name" msgid="8786127091542624618">"Numele transmisiei"</string>
<string name="media_output_broadcast_code" msgid="870795639644728542">"Parolă"</string>
- <string name="media_output_broadcast_dialog_save" msgid="7910865591430010198">"Salvați"</string>
+ <string name="media_output_broadcast_dialog_save" msgid="7910865591430010198">"Salvează"</string>
<string name="media_output_broadcast_starting" msgid="8130153654166235557">"Începe…"</string>
<string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Nu se poate transmite"</string>
- <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nu se poate salva. Încercați din nou."</string>
+ <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Nu se poate salva. Încearcă din nou."</string>
<string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Nu se poate salva."</string>
<string name="build_number_clip_data_label" msgid="3623176728412560914">"Numărul versiunii"</string>
<string name="build_number_copy_toast" msgid="877720921605503046">"Numărul versiunii s-a copiat în clipboard."</string>
- <string name="basic_status" msgid="2315371112182658176">"Deschideți conversația"</string>
+ <string name="basic_status" msgid="2315371112182658176">"Deschide conversația"</string>
<string name="select_conversation_title" msgid="6716364118095089519">"Widgeturi pentru conversație"</string>
<string name="select_conversation_text" msgid="3376048251434956013">"Atingeți o conversație ca să o adăugați pe ecranul de pornire"</string>
<string name="no_conversations_text" msgid="5354115541282395015">"Conversațiile dvs. recente se vor afișa aici"</string>
@@ -905,7 +902,7 @@
<string name="status_before_loading" msgid="1500477307859631381">"Conținutul va apărea în curând"</string>
<string name="missed_call" msgid="4228016077700161689">"Apel nepreluat"</string>
<string name="messages_count_overflow_indicator" msgid="7850934067082006043">"<xliff:g id="NUMBER">%d</xliff:g>+"</string>
- <string name="people_tile_description" msgid="8154966188085545556">"Vedeți mesaje recente, apeluri pierdute și actualizări de stare"</string>
+ <string name="people_tile_description" msgid="8154966188085545556">"Vezi mesaje recente, apeluri pierdute și actualizări de stare"</string>
<string name="people_tile_title" msgid="6589377493334871272">"Conversație"</string>
<string name="paused_by_dnd" msgid="7856941866433556428">"Întrerupt de Nu deranja"</string>
<string name="new_notification_text_content_description" msgid="2915029960094389291">"<xliff:g id="NAME">%1$s</xliff:g> a trimis un mesaj: <xliff:g id="NOTIFICATION">%2$s</xliff:g>"</string>
@@ -913,11 +910,11 @@
<string name="new_status_content_description" msgid="6046637888641308327">"<xliff:g id="NAME">%1$s</xliff:g> are o nouă stare: <xliff:g id="STATUS">%2$s</xliff:g>"</string>
<string name="person_available" msgid="2318599327472755472">"Disponibil"</string>
<string name="battery_state_unknown_notification_title" msgid="8464703640483773454">"Problemă la citirea măsurării bateriei"</string>
- <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Atingeți pentru mai multe informații"</string>
+ <string name="battery_state_unknown_notification_text" msgid="13720937839460899">"Atinge pentru mai multe informații"</string>
<string name="qs_alarm_tile_no_alarm" msgid="4826472008616807923">"Nicio alarmă setată"</string>
<string name="accessibility_fingerprint_label" msgid="5255731221854153660">"Senzor de amprentă"</string>
<string name="accessibility_authenticate_hint" msgid="798914151813205721">"Autentificați-vă"</string>
- <string name="accessibility_enter_hint" msgid="2617864063504824834">"Accesați dispozitivul"</string>
+ <string name="accessibility_enter_hint" msgid="2617864063504824834">"Accesează dispozitivul"</string>
<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>
@@ -930,32 +927,32 @@
<string name="all_network_unavailable" msgid="4112774339909373349">"Nicio rețea disponibilă"</string>
<string name="turn_on_wifi" msgid="1308379840799281023">"Wi-Fi"</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="unlock_to_view_networks" msgid="5072880496312015676">"Deblochează 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="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Deocamdată, Wi-Fi nu se poate conecta automat"</string>
<string name="see_all_networks" msgid="3773666844913168122">"Afișează-le pe toate"</string>
<string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Pentru a schimba rețeaua, deconectați ethernet"</string>
<string name="wifi_scan_notify_message" msgid="3753839537448621794">"Pentru a îmbunătăți experiența cu dispozitivul, aplicațiile și serviciile pot să caute în continuare rețele Wi‑Fi chiar și atunci când conexiunea Wi-Fi este dezactivată. Puteți să schimbați acest aspect din setările pentru căutarea de rețele Wi-Fi. "<annotation id="link">"Schimbați"</annotation></string>
- <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Dezactivați modul Avion"</string>
+ <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Dezactivează modul Avion"</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_add" msgid="4888460910694986304">"Adaugă un card"</string>
<string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"Nu adăugați un card"</string>
- <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Alegeți utilizatorul"</string>
+ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Alege utilizatorul"</string>
<string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplicație este activă}few{# aplicații sunt active}other{# de aplicații sunt active}}"</string>
<string name="fgs_dot_content_description" msgid="2865071539464777240">"Informații noi"</string>
<string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aplicații active"</string>
<string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Aceste aplicații sunt active și rulează, chiar dacă nu le folosiți. Astfel, funcțiile lor sunt îmbunătățite, dar autonomia bateriei poate fi afectată."</string>
- <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Opriți"</string>
+ <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Oprește"</string>
<string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Oprită"</string>
<string name="clipboard_edit_text_done" msgid="4551887727694022409">"Gata"</string>
<string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"S-a copiat"</string>
<string name="clipboard_edit_source" msgid="9156488177277788029">"Din <xliff:g id="APPNAME">%1$s</xliff:g>"</string>
- <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Închideți textul copiat"</string>
- <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editați textul copiat"</string>
- <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editați imaginea copiată"</string>
- <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Trimiteți către un dispozitiv din apropiere"</string>
- <string name="clipboard_text_hidden" msgid="7926899867471812305">"Atingeți pentru a afișa"</string>
+ <string name="clipboard_dismiss_description" msgid="3335990369850165486">"Închide textul copiat"</string>
+ <string name="clipboard_edit_text_description" msgid="805254383912962103">"Editează textul copiat"</string>
+ <string name="clipboard_edit_image_description" msgid="8904857948976041306">"Editează imaginea copiată"</string>
+ <string name="clipboard_send_nearby_description" msgid="4629769637846717650">"Trimite către un dispozitiv din apropiere"</string>
+ <string name="clipboard_text_hidden" msgid="7926899867471812305">"Atinge pentru a afișa"</string>
<string name="clipboard_text_copied" msgid="5100836834278976679">"Textul a fost copiat"</string>
<string name="clipboard_image_copied" msgid="3793365360174328722">"Imaginea a fost copiată"</string>
<string name="clipboard_content_copied" msgid="144452398567828145">"Conținutul a fost copiat"</string>
@@ -963,8 +960,8 @@
<string name="clipboard_overlay_window_name" msgid="6450043652167357664">"Clipboard"</string>
<string name="clipboard_image_preview" msgid="2156475174343538128">"Previzualizarea imaginii"</string>
<string name="clipboard_edit" msgid="4500155216174011640">"editați"</string>
- <string name="add" msgid="81036585205287996">"Adăugați"</string>
- <string name="manage_users" msgid="1823875311934643849">"Gestionați utilizatorii"</string>
+ <string name="add" msgid="81036585205287996">"Adaugă"</string>
+ <string name="manage_users" msgid="1823875311934643849">"Gestionează utilizatorii"</string>
<string name="drag_split_not_supported" msgid="4326847447699729722">"Notificarea nu acceptă tragerea pe ecranul împărțit."</string>
<string name="dream_overlay_status_bar_wifi_off" msgid="4497069245055003582">"Wi‑Fi indisponibil"</string>
<string name="dream_overlay_status_bar_priority_mode" msgid="5428462123314728739">"Modul Prioritate"</string>
@@ -978,7 +975,7 @@
<string name="bt_le_audio_broadcast_dialog_title" msgid="3605428497924077811">"Opriți difuzarea <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string>
<string name="bt_le_audio_broadcast_dialog_sub_title" msgid="7889684551194225793">"Dacă difuzați <xliff:g id="SWITCHAPP">%1$s</xliff:g> sau schimbați rezultatul, difuzarea actuală se va opri"</string>
<string name="bt_le_audio_broadcast_dialog_switch_app" msgid="6098768269397105733">"Difuzați <xliff:g id="SWITCHAPP">%1$s</xliff:g>"</string>
- <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Schimbați rezultatul"</string>
+ <string name="bt_le_audio_broadcast_dialog_different_output" msgid="7885102097302562674">"Schimbă rezultatul"</string>
<string name="bt_le_audio_broadcast_dialog_unknown_name" msgid="3791472237793443044">"Necunoscută"</string>
<string name="dream_date_complication_date_format" msgid="8191225366513860104">"EE, z LLL"</string>
<string name="dream_time_complication_12_hr_time_format" msgid="4691197486690291529">"h:mm"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index e848857f1ecc..08eee34c0c80 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Вкл."</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Откл."</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Недоступно"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"узнать больше"</string>
<string name="nav_bar" msgid="4642708685386136807">"Панель навигации"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Расположение кнопок"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Дополнительный тип кнопки \"Влево\""</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Пока телефон не остынет, некоторые функции могут быть недоступны.\nНажмите, чтобы получить дополнительную информацию"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ваш телефон остынет автоматически.\n\nОбратите внимание, что до тех пор он может работать медленнее, чем обычно."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Подробнее о действиях при перегреве…"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Отключите устройство"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Устройство нагревается в районе зарядного порта. Если оно подключено к зарядному или USB-устройству, отключите его. Будьте осторожны: кабель тоже мог нагреться."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Подробнее о действиях при перегреве…"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Ярлык слева"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Ярлык справа"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index fcff90dac09f..c329c22071e1 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ක්‍රියාත්මකයි"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ක්‍රියාවිරහිතයි"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ලබා ගත නොහැකිය"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"තව දැන ගන්න"</string>
<string name="nav_bar" msgid="4642708685386136807">"සංචලන තීරුව"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"පිරිසැලසුම"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"අමතර වම් බොත්තම් වර්ගය"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"දුරකථනය සිසිල් වන අතරතුර සමහර විශේෂාංග සීමිත විය හැකිය.\nතව තතු සඳහා තට්ටු කරන්න"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"ඔබගේ දුරකථනය ස්වයංක්‍රියව සිසිල් වීමට උත්සාහ කරනු ඇත. ඔබට තවම ඔබේ දුරකථනය භාවිත කළ හැකිය, නමුත් එය සෙමින් ධාවනය විය හැකිය.\n\nඔබේ දුරකථනය සිසිල් වූ පසු, එය සාමාන්‍ය ලෙස ධාවනය වනු ඇත."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"රැකවරණ පියවර බලන්න"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"ඔබේ උපාංගය ගලවන්න"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"ඔබේ උපාංගය ආරෝපණ කවුළුව අවට උණුසුම් වෙමින් පවතී. එය චාජරයකට හෝ USB උපාංගයකට සම්බන්ධ කර ඇත්නම්, එය ගලවා, කේබලය උණුසුම් විය හැකි බැවින් ප්‍රවේශම් වන්න."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"රැකවරණ පියවර බලන්න"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"වම් කෙටි මග"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"දකුණු කෙටි මග"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index eefb1de38701..9f9efc409c07 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Niektoré funkcie budú obmedzené, dokým neklesne teplota telefónu.\nViac sa dozviete po klepnutí."</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Váš telefón sa automaticky pokúsi schladiť. Môžete ho naďalej používať, ale môže fungovať pomalšie.\n\nPo poklese teploty bude telefón fungovať ako normálne."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Zobraziť opatrenia"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Odpojte zariadenie"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Zariadenie sa zahrieva pri nabíjacom porte. Ak je pripojené k nabíjačke alebo príslušenstvu USB, odpojte ho a dajte pozor, lebo môže byť horúci aj kábel."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Zobraziť opatrenia"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Ľavá skratka"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Pravá skratka"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 914036166684..397659d7a90b 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Vklopljeno"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Izklopljeno"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Ni na voljo"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"za več informacij"</string>
<string name="nav_bar" msgid="4642708685386136807">"Vrstica za krmarjenje"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Postavitev"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Vrsta dodatnega levega gumba"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Nekatere funkcije bodo med ohlajanjem telefona omejene.\nDotaknite se za več informacij"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefon se bo samodejno poskusil ohladiti. Še naprej ga lahko uporabljate, vendar bo morda deloval počasneje.\n\nKo se telefon ohladi, bo zopet deloval kot običajno."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Oglejte si navodila za ukrepanje"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Odklopite napravo"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Naprava se segreva pri vratih za polnjenje. Če je priključena na polnilnik ali dodatek USB, ga odklopite in bodite tem previdni, saj je tudi kabel lahko topel."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Oglejte si navodila za ukrepanje"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Leva bližnjica"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Desna bližnjica"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 99536cb2e2bc..f7144115e6d2 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Aktiv"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Joaktiv"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Nuk ofrohet"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"mëso më shumë"</string>
<string name="nav_bar" msgid="4642708685386136807">"Shiriti i navigimit"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Struktura"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Lloji i butonit shtesë majtas"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Disa veçori janë të kufizuara kur telefoni është duke u ftohur.\nTrokit për më shumë informacione"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefoni yt do të përpiqet automatikisht që të ftohet. Mund ta përdorësh përsëri telefonin, por ai mund të punojë më ngadalë.\n\nPasi telefoni të jetë ftohur, ai do të punojë si normalisht."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Shiko hapat për kujdesin"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Shkëpute pajisjen"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Pajisja jote po nxehet pranë portës së karikimit. Nëse është lidhur me një karikues ose një aksesor USB, shkëpute dhe trego kujdes pasi kablloja mund të jetë e nxehtë po ashtu."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Shiko hapat për kujdesin"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Shkurtorja majtas"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Shkurtorja djathtas"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 8b9822d7b7c6..9eabe28b26b1 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Укључено"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Искључено"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Недоступно"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"сазнајте више"</string>
<string name="nav_bar" msgid="4642708685386136807">"Трака за навигацију"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Распоред"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Додатни тип левог дугмета"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Неке функције су ограничене док се телефон не охлади.\nДодирните за више информација"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Телефон ће аутоматски покушати да се охлади. И даље ћете моћи да користите телефон, али ће спорије реаговати.\n\nКада се телефон охлади, нормално ће радити."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Погледајте упозорења"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Искључите уређај"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Уређај се загрева у близини порта за пуњење. Ако је повезан са пуњачем или USB опремом, искључите је и будите пажљиви јер и кабл може да буде врућ."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Погледајте упозорења"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Лева пречица"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Десна пречица"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 374d5521fa83..c7446fa46aab 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"På"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Av"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Inte tillgängligt"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"läs mer"</string>
<string name="nav_bar" msgid="4642708685386136807">"Navigeringsfält"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Layout"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Knapptyp för extra vänster"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Vissa funktioner är begränsade medan telefonen svalnar.\nTryck för mer information"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Mobilen försöker svalna automatiskt. Du kan fortfarande använda mobilen, men den kan vara långsammare än vanligt.\n\nMobilen fungerar som vanligt när den har svalnat."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Visa alla skötselråd"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Koppla ur enheten"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Enheten börjar bli varm vid laddningsporten. Om den är ansluten till en laddare eller ett USB-tillbehör kopplar du ur den. Var försiktigt eftersom kabeln också kan vara varm."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Visa alla skötselråd"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Vänster genväg"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Höger genväg"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 79e542ff626f..c02ea6cf1859 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Imewashwa"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Imezimwa"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Hakipatikani"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"pata maelezo zaidi"</string>
<string name="nav_bar" msgid="4642708685386136807">"Sehemu ya viungo muhimu"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Mpangilio"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Aina ya kitufe cha kushoto cha ziada"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Baadhi ya vipengele havitatumika kwenye simu wakati inapoa.\nGusa ili upate maelezo zaidi"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Simu yako itajaribu kupoa kiotomatiki. Bado unaweza kutumia simu yako, lakini huenda ikafanya kazi polepole. \n\nPindi simu yako itakapopoa, itaendelea kufanya kazi kama kawaida."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Angalia hatua za utunzaji"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Chomoa kifaa chako"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Kifaa chako kinapata joto karibu na mlango wa kuchaji. Ikiwa kimeunganishwa kwenye chaja au kifuasi cha USB, kichomoe na uwe makini kwani kebo inaweza kuwa imepata joto."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Angalia hatua za ulinzi"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Njia ya mkato ya kushoto"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Njia ya mkato ya kulia"</string>
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index f4d482406fa0..b24ce122208f 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -66,8 +66,6 @@
<dimen name="lockscreen_shade_qs_transition_distance">@dimen/lockscreen_shade_notifications_scrim_transition_distance</dimen>
<dimen name="lockscreen_shade_qs_transition_delay">@dimen/lockscreen_shade_notifications_scrim_transition_delay</dimen>
<dimen name="lockscreen_shade_qs_squish_transition_distance">@dimen/lockscreen_shade_qs_transition_distance</dimen>
- <!-- On split-shade, the QS squish transition should start from half height. -->
- <item name="lockscreen_shade_qs_squish_start_fraction" type="dimen" format="float" >0.5</item>
<!-- On split-shade, there should be no depth effect, so setting the value to 0. -->
<dimen name="lockscreen_shade_depth_controller_transition_distance">0dp</dimen>
<dimen name="lockscreen_shade_udfps_keyguard_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml
index a587e5a806ca..5dcbeb5c85cf 100644
--- a/packages/SystemUI/res/values-sw600dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp/dimens.xml
@@ -86,8 +86,6 @@
<dimen name="lockscreen_shade_qs_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
<dimen name="lockscreen_shade_qs_transition_delay">@dimen/lockscreen_shade_scrim_transition_distance</dimen>
<dimen name="lockscreen_shade_qs_squish_transition_distance">@dimen/lockscreen_shade_qs_transition_distance</dimen>
- <!-- On large screen portrait, the QS squish transition should start from half height. -->
- <item name="lockscreen_shade_qs_squish_start_fraction" type="dimen" format="float" >0.5</item>
<dimen name="lockscreen_shade_depth_controller_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
<dimen name="lockscreen_shade_udfps_keyguard_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
<dimen name="lockscreen_shade_status_bar_transition_distance">@dimen/lockscreen_shade_full_transition_distance</dimen>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index 1c795bf6b6d8..303e43588536 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"ஆன்"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ஆஃப்"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"இல்லை"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"மேலும் அறிக"</string>
<string name="nav_bar" msgid="4642708685386136807">"வழிசெலுத்தல் பட்டி"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"தளவமைப்பு"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"கூடுதல் இடப்புற பட்டன் வகை"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"மொபைலின் வெப்ப அளவு குறையும் வரை சில அம்சங்களைப் பயன்படுத்த முடியாது.\nமேலும் தகவலுக்கு தட்டவும்"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"உங்கள் மொபைலின் வெப்ப அளவு தானாகவே குறையும். தொடர்ந்து நீங்கள் மொபைலைப் பயன்படுத்தலாம், ஆனால் அதன் வேகம் குறைவாக இருக்கக்கூடும்.\n\nமொபைலின் வெப்ப அளவு குறைந்தவுடன், அது இயல்பு நிலையில் இயங்கும்."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"மேலும் விவரங்களுக்கு இதைப் பார்க்கவும்"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"சாதன இணைப்பைத் துண்டித்தல்"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"சார்ஜிங் போர்ட்டிற்கு அருகே உங்கள் சாதனம் சூடாகிறது. சார்ஜருடனோ USB உபகரணத்துடனோ சாதனம் இணைக்கப்பட்டிருந்தால் அதன் இணைப்பைத் துண்டிக்கவும். கேபிளும் சூடாக இருக்கக்கூடும் என்பதால் கவனத்துடன் கையாளவும்."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"மேலும் விவரங்களுக்கு இதைப் பார்க்கவும்"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"இடப்புற ஷார்ட்கட்"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"வலப்புற ஷார்ட்கட்"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index b278f22cefcf..643fe1ec262f 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ఫోన్‌ను చల్లబరిచే క్రమంలో కొన్ని ఫీచర్లు పరిమితం చేయబడ్డాయి.\nమరింత సమాచారం కోసం ట్యాప్ చేయండి"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"మీ ఫోన్ ఆటోమేటిక్‌గా చల్లబడటానికి ప్రయత్నిస్తుంది. మీరు ఇప్పటికీ మీ ఫోన్‌ను ఉపయోగించవచ్చు, కానీ దాని పనితీరు నెమ్మదిగా ఉండవచ్చు.\n\nమీ ఫోన్ చల్లబడిన తర్వాత, అది సాధారణ రీతిలో పని చేస్తుంది."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"తీసుకోవాల్సిన జాగ్రత్తలు ఏమిటో చూడండి"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"మీ పరికరాన్ని అన్‌ప్లగ్ చేయండి"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"ఛార్జింగ్ పోర్ట్ దగ్గర ఉంచినప్పుడు మీ పరికరం వేడెక్కుతోంది. ఇది ఛార్జర్ లేదా USB యాక్సెసరీకి కనెక్ట్ చేసి ఉంటే, దాన్ని అన్‌ప్లగ్ చేసి, కేబుల్ వేడెక్కే అవకాశం కూడా ఉన్నందున జాగ్రత్త వహించండి."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"తీసుకోవాల్సిన జాగ్రత్తలు ఏమిటో చూడండి"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"ఎడమవైపు షార్ట్‌కట్"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"కుడివైపు షార్ట్‌కట్"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index ec0e36595137..0c20911c4d42 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"เปิด"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"ปิด"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"ไม่พร้อมใช้งาน"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"ดูข้อมูลเพิ่มเติม"</string>
<string name="nav_bar" msgid="4642708685386136807">"แถบนำทาง"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"การจัดวาง"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"ประเภทปุ่มทางซ้ายเพิ่มเติม"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"ฟีเจอร์บางอย่างจะใช้งานได้จำกัดขณะโทรศัพท์เย็นลง\nแตะเพื่อดูข้อมูลเพิ่มเติม"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"โทรศัพท์จะพยายามลดอุณหภูมิลงโดยอัตโนมัติ คุณยังสามารถใช้โทรศัพท์ได้ แต่โทรศัพท์อาจทำงานช้าลง\n\nโทรศัพท์จะทำงานตามปกติเมื่อเย็นลงแล้ว"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"ดูขั้นตอนในการดูแลรักษา"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"ถอดปลั๊กอุปกรณ์"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"บริเวณพอร์ตชาร์จของอุปกรณ์เริ่มจะร้อนแล้ว หากมีที่ชาร์จหรืออุปกรณ์เสริม USB เสียบอยู่ ให้ถอดออกอย่างระมัดระวังเพราะสายเส้นนั้นก็อาจจะร้อนด้วยเช่นกัน"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"ดูขั้นตอนในการดูแลรักษา"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"ทางลัดทางซ้าย"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"ทางลัดทางขวา"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index c0b358860022..4653b79336c3 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Limitado ang ilang feature habang nagku-cool down ang telepono.\nMag-tap para sa higit pang impormasyon"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Awtomatikong susubukan ng iyong telepono na mag-cool down. Magagamit mo pa rin ang iyong telepono, ngunit maaaring mas mabagal ang paggana nito.\n\nKapag nakapag-cool down na ang iyong telepono, gagana na ito nang normal."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Tingnan ang mga hakbang sa pangangalaga"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Bunutin sa saksakan ang device"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Umiinit ang iyong device malapit sa charging port. Kung nakakonekta ito sa charger o USB accessory, bunutin ito sa saksakan, at mag-ingat dahil posibleng mainit din ang cable."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Tingnan ang mga hakbang sa pangangalaga"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Kaliwang shortcut"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Kanang shortcut"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 389178a15322..2629f10a4504 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Açık"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Kapalı"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Kullanılamıyor"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"daha fazla bilgi"</string>
<string name="nav_bar" msgid="4642708685386136807">"Gezinme çubuğu"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Düzen"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Ekstra sol düğme türü"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Telefon soğurken bazı özellikler sınırlı olarak kullanılabilir.\nDaha fazla bilgi için dokunun"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Telefonunuz otomatik olarak soğumaya çalışacak. Bu sırada telefonunuzu kullanmaya devam edebilirsiniz ancak uygulamalar daha yavaş çalışabilir.\n\nTelefonunuz soğuduktan sonra normal şekilde çalışacaktır."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Bakımla ilgili adımlara bakın"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Cihazınızın fişini çekin"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Cihazınız, şarj yuvasının yakınındayken ısınıyor. Şarj cihazına veya USB aksesuarına bağlıysa cihazı çıkarın. Ayrıca, kablo sıcak olabileceği için dikkatli olun."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Bakımla ilgili adımlara bakın"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Sol kısayol"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Sağ kısayol"</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index 77966e8a7bb1..aea9d4ee0a8e 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Увімкнено"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Вимкнено"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Недоступно"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"дізнатися більше"</string>
<string name="nav_bar" msgid="4642708685386136807">"Панель навігації"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Макет"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Додатковий тип кнопки ліворуч"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Під час охолодження деякі функції обмежуються.\nНатисніть, щоб дізнатися більше"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ваш телефон охолоджуватиметься автоматично. Ви можете далі користуватися телефоном, але він може працювати повільніше.\n\nКоли телефон охолоне, він працюватиме належним чином."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Переглянути запобіжні заходи"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Від’єднайте пристрій"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Пристрій нагрівається біля зарядного порту. Якщо він під’єднаний до зарядного пристрою або USB-аксесуара, від’єднайте його, однак будьте обережні, оскільки кабель також може бути гарячий."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Переглянути застереження"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Комбінація клавіш ліворуч"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Комбінація клавіш праворуч"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 4ebbc705d607..e3d2d531a6be 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"آن"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"آف"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"غیر دستیاب ہے"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"مزید جانیں"</string>
<string name="nav_bar" msgid="4642708685386136807">"نیویگیشن بار"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"لے آؤٹ"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"بائيں جانب کی اضافی بٹن کی قسم"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"فون کے ٹھنڈے ہو جانے تک کچھ خصوصیات محدود ہیں۔\nمزید معلومات کیلئے تھپتھپائیں"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"آپ کا فون خودکار طور پر ٹھنڈا ہونے کی کوشش کرے گا۔ آپ ابھی بھی اپنا فون استعمال کر سکتے ہیں، مگر ہو سکتا ہے یہ سست چلے۔\n\nایک بار آپ کا فون ٹھنڈا ہوجائے تو یہ معمول کے مطابق چلے گا۔"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"نگہداشت کے اقدامات ملاحظہ کریں"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"اپنے آلہ کو ان پلگ کریں"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"‏آپ کا آلہ چارجنگ پورٹ کے قریب گرم ہو رہا ہے۔ اگر یہ چارجر یا USB لوازمات سے منسلک ہے تو اسے ان پلگ کریں اور خیال رکھیں کہ کیبل بھی گرم ہو سکتی ہے۔"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"نگہداشت کے اقدامات ملاحظہ کریں"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"بائيں جانب کا شارٹ کٹ"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"دائیں جانب کا شارٹ کٹ"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index c0e7b2f82ea3..68be635ace1d 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"Đang bật"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"Đang tắt"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"Không có sẵn"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"tìm hiểu thêm"</string>
<string name="nav_bar" msgid="4642708685386136807">"Thanh điều hướng"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"Bố cục"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"Loại nút bổ sung bên trái"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Một số tính năng bị hạn chế trong khi điện thoại nguội dần.\nHãy nhấn để biết thêm thông tin"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Điện thoại của bạn sẽ tự động nguội dần. Bạn vẫn có thể sử dụng điện thoại, nhưng điện thoại có thể chạy chậm hơn. \n\nSau khi đã nguội, điện thoại sẽ chạy bình thường."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Xem các bước chăm sóc"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Rút thiết bị ra"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Phần gần cổng sạc của thiết bị đang nóng lên. Nếu thiết bị kết nối với bộ sạc hoặc phụ kiện USB, hãy rút ra một cách thận trọng vì cáp có thể cũng đang nóng."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Xem các bước chăm sóc"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Lối tắt bên trái"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Lối tắt bên phải"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index df0b0e056cf0..04df91b94aa8 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"开启"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"关闭"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"不可用"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"了解详情"</string>
<string name="nav_bar" msgid="4642708685386136807">"导航栏"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"布局"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"其他向左按钮类型"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"手机降温时,部分功能的使用会受限制。\n点按即可了解详情"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"您的手机将自动尝试降温。您依然可以使用您的手机,但是手机运行速度可能会更慢。\n\n手机降温后,就会恢复正常的运行速度。"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"查看处理步骤"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"拔出设备"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"设备的充电接口附近在发热。如果该设备已连接到充电器或 USB 配件,请立即拔掉,并注意充电线也可能会发热。"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"查看处理步骤"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"向左快捷方式"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"向右快捷方式"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
index b476255bc669..6ce948def69e 100644
--- a/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/tiles_states_strings.xml
@@ -89,7 +89,7 @@
<string-array name="tile_states_color_correction">
<item msgid="2840507878437297682">"不可用"</item>
<item msgid="1909756493418256167">"关闭"</item>
- <item msgid="4531508423703413340">"开启"</item>
+ <item msgid="4531508423703413340">"已开启"</item>
</string-array>
<string-array name="tile_states_inversion">
<item msgid="3638187931191394628">"不可用"</item>
@@ -174,6 +174,6 @@
<string-array name="tile_states_dream">
<item msgid="6184819793571079513">"不可用"</item>
<item msgid="8014986104355098744">"关闭"</item>
- <item msgid="5966994759929723339">"开启"</item>
+ <item msgid="5966994759929723339">"已开启"</item>
</string-array>
</resources>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 67f94b766153..aedaec6ed9d8 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -161,7 +161,7 @@
<string name="fingerprint_dialog_use_fingerprint_instead" msgid="6178228876763024452">"無法辨識面孔,請改用指紋完成驗證。"</string>
<!-- no translation found for keyguard_face_failed_use_fp (7140293906176164263) -->
<skip />
- <string name="keyguard_face_failed" msgid="9044619102286917151">"無法辨識臉孔"</string>
+ <string name="keyguard_face_failed" msgid="9044619102286917151">"無法辨識面孔"</string>
<string name="keyguard_suggest_fingerprint" msgid="8742015961962702960">"請改用指紋"</string>
<string name="accessibility_bluetooth_connected" msgid="4745196874551115205">"藍牙連線已建立。"</string>
<string name="accessibility_battery_unknown" msgid="1807789554617976440">"電量百分比不明。"</string>
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"開啟"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"關閉"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"無法使用"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"瞭解詳情"</string>
<string name="nav_bar" msgid="4642708685386136807">"導覽列"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"配置"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"其他向左按鈕類型"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"手機降溫時,部分功能會受限制。\n輕按即可瞭解詳情"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"手機會自動嘗試降溫。您仍可以使用手機,但手機的運作速度可能較慢。\n\n手機降溫後便會恢復正常。"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"查看保養步驟"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"拔除裝置"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"充電埠附近的裝置溫度正在上升。如裝置正連接充電器或 USB 配件,請拔除裝置並小心安全,因為電線的溫度可能也偏高。"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"查看保養步驟"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"向左捷徑"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"向右捷徑"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 23189d09cdfb..8151cc4a6c0b 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -598,8 +598,7 @@
<string name="switch_bar_on" msgid="1770868129120096114">"開啟"</string>
<string name="switch_bar_off" msgid="5669805115416379556">"關閉"</string>
<string name="tile_unavailable" msgid="3095879009136616920">"無法使用"</string>
- <!-- no translation found for accessibility_tile_disabled_by_policy_action_description (6958422730461646926) -->
- <skip />
+ <string name="accessibility_tile_disabled_by_policy_action_description" msgid="6958422730461646926">"瞭解詳情"</string>
<string name="nav_bar" msgid="4642708685386136807">"導覽列"</string>
<string name="nav_bar_layout" msgid="4716392484772899544">"配置"</string>
<string name="left_nav_bar_button_type" msgid="2634852842345192790">"其他向左按鈕類型"</string>
@@ -672,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"手機降溫時,某些功能會受限。\n輕觸即可瞭解詳情"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"手機會自動嘗試降溫。你仍可繼續使用手機,但是手機的運作速度可能會較慢。\n\n手機降溫完畢後,就會恢復正常的運作速度。"</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"查看處理步驟"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"拔除裝置"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"裝置的充電埠附近越來越熱。如果裝置已連接充電器或 USB 配件,請立即拔除。此外,電線也可能會變熱,請特別留意。"</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"查看處理步驟"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"向左快速鍵"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"向右快速鍵"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 08cd7fcde557..b2937f8a7cd2 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -671,10 +671,8 @@
<string name="high_temp_notif_message" msgid="1277346543068257549">"Ezinye izici zikhawulelwe ngenkathi ifoni iphola.\nThepha mayelana nolwazi olwengeziwe"</string>
<string name="high_temp_dialog_message" msgid="3793606072661253968">"Ifoni yakho izozama ngokuzenzakalela ukuphola. Ungasasebenzisa ifoni yakho, kodwa ingasebenza ngokungasheshi.\n\nUma ifoni yakho isipholile, izosebenza ngokuvamile."</string>
<string name="high_temp_dialog_help_text" msgid="7380171287943345858">"Bona izinyathelo zokunakekelwa"</string>
- <!-- no translation found for high_temp_alarm_title (8654754369605452169) -->
- <skip />
- <!-- no translation found for high_temp_alarm_notify_message (3917622943609118956) -->
- <skip />
+ <string name="high_temp_alarm_title" msgid="8654754369605452169">"Khipha idivayisi yakho"</string>
+ <string name="high_temp_alarm_notify_message" msgid="3917622943609118956">"Idivayisi yakho iqala ukufudumala eduze kwembobo yokushaja. Uma ixhunywe kushaja noma insiza ye-USB, yikhiphe, futhi uqaphele njengoba ikhebuli ingase ifudumale."</string>
<string name="high_temp_alarm_help_care_steps" msgid="5017002218341329566">"Bona izinyathelo zokunakekelwa"</string>
<string name="lockscreen_shortcut_left" msgid="1238765178956067599">"Isinqamuleli sangakwesokunxele"</string>
<string name="lockscreen_shortcut_right" msgid="4138414674531853719">"Isinqamuleli sangakwesokudla"</string>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index dd5987bfe1c2..9e8bef06270b 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -186,7 +186,7 @@
<color name="media_dialog_item_main_content">@color/material_dynamic_primary20</color>
<color name="media_dialog_item_background">@color/material_dynamic_secondary95</color>
<color name="media_dialog_connected_item_background">@color/material_dynamic_primary90</color>
- <color name="media_dialog_seekbar_progress">@color/material_dynamic_secondary40</color>
+ <color name="media_dialog_seekbar_progress">@android:color/system_accent1_200</color>
<color name="media_dialog_button_background">@color/material_dynamic_primary40</color>
<color name="media_dialog_solid_button_text">@color/material_dynamic_neutral95</color>
@@ -245,6 +245,10 @@
<color name="dream_overlay_aqi_very_unhealthy">#AD1457</color>
<color name="dream_overlay_aqi_hazardous">#880E4F</color>
<color name="dream_overlay_aqi_unknown">#BDC1C6</color>
+
+ <!-- Dream overlay text shadows -->
<color name="dream_overlay_clock_key_text_shadow_color">#4D000000</color>
<color name="dream_overlay_clock_ambient_text_shadow_color">#4D000000</color>
+ <color name="dream_overlay_status_bar_key_text_shadow_color">#66000000</color>
+ <color name="dream_overlay_status_bar_ambient_text_shadow_color">#59000000</color>
</resources>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index a8027238a0bf..37549c927a90 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -611,6 +611,33 @@
2 - Override the setting to never bypass keyguard -->
<integer name="config_face_unlock_bypass_override">0</integer>
+ <!-- Messages that should NOT be shown to the user during face authentication on keyguard.
+ This includes both lockscreen and bouncer. This should be used to hide messages that may be
+ too chatty or messages that the user can't do much about. Entries are defined in
+ android.hardware.biometrics.face@1.0 types.hal.
+
+ Although not visibly shown to the user, these acquired messages (sent per face auth frame)
+ are still counted towards the total frames to determine whether a deferred message
+ (see config_face_help_msgs_defer_until_timeout) meets the threshold % of frames to show on
+ face timeout. -->
+ <integer-array name="config_face_acquire_device_entry_ignorelist" translatable="false" >
+ </integer-array>
+
+ <!-- Which face help messages to defer until face auth times out. If face auth is cancelled
+ or ends on another error, then the message is never surfaced. May also never surface
+ if it doesn't meet a threshold % of authentication frames specified by.
+ config_face_help_msgs_defer_until_timeout_threshold. -->
+ <integer-array name="config_face_help_msgs_defer_until_timeout">
+ </integer-array>
+
+ <!-- Percentage of face auth frames received required to show a deferred message at
+ FACE_ERROR_TIMEOUT. See config_face_help_msgs_defer_until_timeout for messages
+ that are deferred.-->
+ <item name="config_face_help_msgs_defer_until_timeout_threshold"
+ translatable="false" format="float" type="dimen">
+ .75
+ </item>
+
<!-- Which face help messages to surface when fingerprint is also enrolled.
Message ids correspond with the acquired ids in BiometricFaceConstants -->
<integer-array name="config_face_help_msgs_when_fingerprint_enrolled">
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 08b1bdc4e8c7..ede6260cfdea 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -520,7 +520,7 @@
<dimen name="qs_tile_margin_vertical">@dimen/qs_tile_margin_horizontal</dimen>
<dimen name="qs_tile_margin_top_bottom">4dp</dimen>
<dimen name="qs_brightness_margin_top">8dp</dimen>
- <dimen name="qs_brightness_margin_bottom">24dp</dimen>
+ <dimen name="qs_brightness_margin_bottom">16dp</dimen>
<dimen name="qqs_layout_margin_top">16dp</dimen>
<dimen name="qqs_layout_padding_bottom">24dp</dimen>
@@ -1187,6 +1187,7 @@
<!-- Output switcher panel related dimensions -->
<dimen name="media_output_dialog_list_max_height">355dp</dimen>
+ <dimen name="media_output_dialog_list_item_height">76dp</dimen>
<dimen name="media_output_dialog_header_album_icon_size">72dp</dimen>
<dimen name="media_output_dialog_header_back_icon_size">32dp</dimen>
<dimen name="media_output_dialog_header_icon_padding">16dp</dimen>
@@ -1234,7 +1235,7 @@
<!-- The fraction at which the QS "squish" transition should start during the lockscreen shade
expansion. 0 is fully collapsed, 1 is fully expanded. -->
- <item type="dimen" format="float" name="lockscreen_shade_qs_squish_start_fraction">0</item>
+ <item type="dimen" format="float" name="lockscreen_shade_qs_squish_start_fraction">0.5</item>
<!-- Distance that the full shade transition takes in order for depth of the wallpaper to fully
change. -->
@@ -1467,6 +1468,8 @@
<dimen name="fgs_manager_list_top_spacing">12dp</dimen>
<dimen name="media_projection_app_selector_icon_size">32dp</dimen>
+ <dimen name="media_projection_app_selector_recents_padding">16dp</dimen>
+ <dimen name="media_projection_app_selector_loader_size">32dp</dimen>
<!-- Dream overlay related dimensions -->
<dimen name="dream_overlay_status_bar_height">60dp</dimen>
@@ -1477,7 +1480,7 @@
<dimen name="dream_overlay_camera_mic_off_indicator_size">8dp</dimen>
<dimen name="dream_overlay_notification_indicator_size">6dp</dimen>
<dimen name="dream_overlay_grey_chip_width">56dp</dimen>
- <dimen name="dream_overlay_status_bar_extra_margin">16dp</dimen>
+ <dimen name="dream_overlay_status_bar_extra_margin">8dp</dimen>
<!-- Dream overlay complications related dimensions -->
<dimen name="dream_overlay_complication_clock_time_text_size">86sp</dimen>
@@ -1560,10 +1563,20 @@
<dimen name="broadcast_dialog_btn_text_size">16sp</dimen>
<dimen name="broadcast_dialog_btn_minHeight">44dp</dimen>
<dimen name="broadcast_dialog_margin">16dp</dimen>
+
+ <!-- Shadow for dream overlay clock complication -->
<dimen name="dream_overlay_clock_key_text_shadow_dx">0dp</dimen>
<dimen name="dream_overlay_clock_key_text_shadow_dy">0dp</dimen>
- <dimen name="dream_overlay_clock_key_text_shadow_radius">5dp</dimen>
+ <dimen name="dream_overlay_clock_key_text_shadow_radius">3dp</dimen>
<dimen name="dream_overlay_clock_ambient_text_shadow_dx">0dp</dimen>
<dimen name="dream_overlay_clock_ambient_text_shadow_dy">0dp</dimen>
<dimen name="dream_overlay_clock_ambient_text_shadow_radius">1dp</dimen>
+
+ <!-- Shadow for dream overlay status bar complications -->
+ <dimen name="dream_overlay_status_bar_key_text_shadow_dx">0.5dp</dimen>
+ <dimen name="dream_overlay_status_bar_key_text_shadow_dy">0.5dp</dimen>
+ <dimen name="dream_overlay_status_bar_key_text_shadow_radius">1dp</dimen>
+ <dimen name="dream_overlay_status_bar_ambient_text_shadow_dx">0.5dp</dimen>
+ <dimen name="dream_overlay_status_bar_ambient_text_shadow_dy">0.5dp</dimen>
+ <dimen name="dream_overlay_status_bar_ambient_text_shadow_radius">2dp</dimen>
</resources>
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
index cbab0a75061e..fafc7744f439 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/SystemUIGoldenImagePathManager.kt
@@ -23,9 +23,11 @@ import platform.test.screenshot.PathConfig
/** A [GoldenImagePathManager] that should be used for all SystemUI screenshot tests. */
class SystemUIGoldenImagePathManager(
pathConfig: PathConfig,
+ override val assetsPathRelativeToRepo: String = "tests/screenshot/assets"
) :
GoldenImagePathManager(
appContext = InstrumentationRegistry.getInstrumentation().context,
+ assetsPathRelativeToRepo = assetsPathRelativeToRepo,
deviceLocalPath =
InstrumentationRegistry.getInstrumentation()
.targetContext
diff --git a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
index cdedc64ed0e9..97665145ce76 100644
--- a/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
+++ b/packages/SystemUI/screenshot/src/com/android/systemui/testing/screenshot/ViewCapture.kt
@@ -93,8 +93,8 @@ fun View.toBitmap(window: Window? = null): Bitmap {
Futures.addCallback(
captureToBitmap(window),
object : FutureCallback<Bitmap> {
- override fun onSuccess(result: Bitmap) {
- continuation.resumeWith(Result.success(result))
+ override fun onSuccess(result: Bitmap?) {
+ continuation.resumeWith(Result.success(result!!))
}
override fun onFailure(t: Throwable) {
diff --git a/packages/SystemUI/shared/res/values/attrs.xml b/packages/SystemUI/shared/res/values/attrs.xml
index f9d66ee583da..96a58405a764 100644
--- a/packages/SystemUI/shared/res/values/attrs.xml
+++ b/packages/SystemUI/shared/res/values/attrs.xml
@@ -25,4 +25,39 @@
<attr name="lockScreenWeight" format="integer" />
<attr name="chargeAnimationDelay" format="integer" />
</declare-styleable>
+
+ <declare-styleable name="DoubleShadowAttrDeclare">
+ <attr name="keyShadowBlur" format="dimension" />
+ <attr name="keyShadowOffsetX" format="dimension" />
+ <attr name="keyShadowOffsetY" format="dimension" />
+ <attr name="keyShadowAlpha" format="float" />
+ <attr name="ambientShadowBlur" format="dimension" />
+ <attr name="ambientShadowOffsetX" format="dimension" />
+ <attr name="ambientShadowOffsetY" format="dimension" />
+ <attr name="ambientShadowAlpha" format="float" />
+ </declare-styleable>
+
+ <declare-styleable name="DoubleShadowTextClock">
+ <attr name="keyShadowBlur" />
+ <attr name="keyShadowOffsetX" />
+ <attr name="keyShadowOffsetY" />
+ <attr name="keyShadowAlpha" />
+ <attr name="ambientShadowBlur" />
+ <attr name="ambientShadowOffsetX" />
+ <attr name="ambientShadowOffsetY" />
+ <attr name="ambientShadowAlpha" />
+ </declare-styleable>
+
+ <declare-styleable name="DoubleShadowTextView">
+ <attr name="keyShadowBlur" />
+ <attr name="keyShadowOffsetX" />
+ <attr name="keyShadowOffsetY" />
+ <attr name="keyShadowAlpha" />
+ <attr name="ambientShadowBlur" />
+ <attr name="ambientShadowOffsetX" />
+ <attr name="ambientShadowOffsetY" />
+ <attr name="ambientShadowAlpha" />
+ <attr name="drawableIconSize" format="dimension" />
+ <attr name="drawableIconInsetSize" format="dimension" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 34e2e83d7643..c2e74456c032 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -23,6 +23,7 @@ import android.annotation.SuppressLint
import android.app.compat.ChangeIdStateCache.invalidate
import android.content.Context
import android.graphics.Canvas
+import android.text.Layout
import android.text.TextUtils
import android.text.format.DateFormat
import android.util.AttributeSet
@@ -78,6 +79,8 @@ class AnimatableClockView @JvmOverloads constructor(
private var textAnimator: TextAnimator? = null
private var onTextAnimatorInitialized: Runnable? = null
+ @VisibleForTesting var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator =
+ { layout, invalidateCb -> TextAnimator(layout, invalidateCb) }
@VisibleForTesting var isAnimationEnabled: Boolean = true
@VisibleForTesting var timeOverrideInMillis: Long? = null
@@ -174,7 +177,7 @@ class AnimatableClockView @JvmOverloads constructor(
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val animator = textAnimator
if (animator == null) {
- textAnimator = TextAnimator(layout) { invalidate() }
+ textAnimator = textAnimatorFactory(layout, ::invalidate)
onTextAnimatorInitialized?.run()
onTextAnimatorInitialized = null
} else {
@@ -219,9 +222,6 @@ class AnimatableClockView @JvmOverloads constructor(
}
fun animateAppearOnLockscreen() {
- if (isAnimationEnabled && textAnimator == null) {
- return
- }
setTextStyle(
weight = dozingWeight,
textSize = -1f,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index 835d6e92a63d..38a312448bab 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -105,7 +105,8 @@ open class ClockRegistry(
)
}
- pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java)
+ pluginManager.addPluginListener(pluginListener, ClockProviderPlugin::class.java,
+ true /* allowMultiple */)
context.contentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_CUSTOM_CLOCK_FACE),
false,
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
index 422274469dcc..2111df501415 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/model/Task.java
@@ -237,6 +237,13 @@ public class Task {
public ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData lastSnapshotData =
new ActivityManager.RecentTaskInfo.PersistedTaskSnapshotData();
+ /**
+ * Indicates that this task for the desktop tile in recents.
+ *
+ * Used when desktop mode feature is enabled.
+ */
+ public boolean desktopTile;
+
public Task() {
// Do nothing
}
@@ -267,6 +274,7 @@ public class Task {
this(other.key, other.colorPrimary, other.colorBackground, other.isDockable,
other.isLocked, other.taskDescription, other.topActivity);
lastSnapshotData.set(other.lastSnapshotData);
+ desktopTile = other.desktopTile;
}
/**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
new file mode 100644
index 000000000000..3748eba47be5
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowIconDrawable.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.shadow
+
+import android.graphics.BlendMode
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorFilter
+import android.graphics.PixelFormat
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import android.graphics.RenderEffect
+import android.graphics.RenderNode
+import android.graphics.Shader
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.InsetDrawable
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo
+
+/** A component to draw an icon with two layers of shadows. */
+class DoubleShadowIconDrawable(
+ keyShadowInfo: ShadowInfo,
+ ambientShadowInfo: ShadowInfo,
+ iconDrawable: Drawable,
+ iconSize: Int,
+ val iconInsetSize: Int
+) : Drawable() {
+ private val mAmbientShadowInfo: ShadowInfo
+ private val mCanvasSize: Int
+ private val mKeyShadowInfo: ShadowInfo
+ private val mIconDrawable: InsetDrawable
+ private val mDoubleShadowNode: RenderNode
+
+ init {
+ mCanvasSize = iconSize + iconInsetSize * 2
+ mKeyShadowInfo = keyShadowInfo
+ mAmbientShadowInfo = ambientShadowInfo
+ setBounds(0, 0, mCanvasSize, mCanvasSize)
+ mIconDrawable = InsetDrawable(iconDrawable, iconInsetSize)
+ mIconDrawable.setBounds(0, 0, mCanvasSize, mCanvasSize)
+ mDoubleShadowNode = createShadowRenderNode()
+ }
+
+ private fun createShadowRenderNode(): RenderNode {
+ val renderNode = RenderNode("DoubleShadowNode")
+ renderNode.setPosition(0, 0, mCanvasSize, mCanvasSize)
+ // Create render effects
+ val ambientShadow =
+ createShadowRenderEffect(
+ mAmbientShadowInfo.blur,
+ mAmbientShadowInfo.offsetX,
+ mAmbientShadowInfo.offsetY,
+ mAmbientShadowInfo.alpha
+ )
+ val keyShadow =
+ createShadowRenderEffect(
+ mKeyShadowInfo.blur,
+ mKeyShadowInfo.offsetX,
+ mKeyShadowInfo.offsetY,
+ mKeyShadowInfo.alpha
+ )
+ val blend = RenderEffect.createBlendModeEffect(ambientShadow, keyShadow, BlendMode.DARKEN)
+ renderNode.setRenderEffect(blend)
+ return renderNode
+ }
+
+ private fun createShadowRenderEffect(
+ radius: Float,
+ offsetX: Float,
+ offsetY: Float,
+ alpha: Float
+ ): RenderEffect {
+ return RenderEffect.createColorFilterEffect(
+ PorterDuffColorFilter(Color.argb(alpha, 0f, 0f, 0f), PorterDuff.Mode.MULTIPLY),
+ RenderEffect.createOffsetEffect(
+ offsetX,
+ offsetY,
+ RenderEffect.createBlurEffect(radius, radius, Shader.TileMode.CLAMP)
+ )
+ )
+ }
+
+ override fun draw(canvas: Canvas) {
+ if (canvas.isHardwareAccelerated) {
+ if (!mDoubleShadowNode.hasDisplayList()) {
+ // Record render node if its display list is not recorded or discarded
+ // (which happens when it's no longer drawn by anything).
+ val recordingCanvas = mDoubleShadowNode.beginRecording()
+ mIconDrawable.draw(recordingCanvas)
+ mDoubleShadowNode.endRecording()
+ }
+ canvas.drawRenderNode(mDoubleShadowNode)
+ }
+ mIconDrawable.draw(canvas)
+ }
+
+ override fun getOpacity(): Int {
+ return PixelFormat.TRANSPARENT
+ }
+
+ override fun setAlpha(alpha: Int) {
+ mIconDrawable.alpha = alpha
+ }
+
+ override fun setColorFilter(colorFilter: ColorFilter?) {
+ mIconDrawable.colorFilter = colorFilter
+ }
+
+ override fun setTint(color: Int) {
+ mIconDrawable.setTint(color)
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextClock.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextClock.kt
new file mode 100644
index 000000000000..f2db129120e9
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextClock.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.shadow
+
+import android.content.Context
+import android.graphics.Canvas
+import android.util.AttributeSet
+import android.widget.TextClock
+import com.android.systemui.shared.R
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows
+
+/** Extension of [TextClock] which draws two shadows on the text (ambient and key shadows) */
+class DoubleShadowTextClock
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextClock(context, attrs, defStyleAttr, defStyleRes) {
+ private val mAmbientShadowInfo: ShadowInfo
+ private val mKeyShadowInfo: ShadowInfo
+
+ init {
+ val attributes =
+ context.obtainStyledAttributes(
+ attrs,
+ R.styleable.DoubleShadowTextClock,
+ defStyleAttr,
+ defStyleRes
+ )
+ try {
+ val keyShadowBlur =
+ attributes.getDimensionPixelSize(R.styleable.DoubleShadowTextClock_keyShadowBlur, 0)
+ val keyShadowOffsetX =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextClock_keyShadowOffsetX,
+ 0
+ )
+ val keyShadowOffsetY =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextClock_keyShadowOffsetY,
+ 0
+ )
+ val keyShadowAlpha =
+ attributes.getFloat(R.styleable.DoubleShadowTextClock_keyShadowAlpha, 0f)
+ mKeyShadowInfo =
+ ShadowInfo(
+ keyShadowBlur.toFloat(),
+ keyShadowOffsetX.toFloat(),
+ keyShadowOffsetY.toFloat(),
+ keyShadowAlpha
+ )
+ val ambientShadowBlur =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextClock_ambientShadowBlur,
+ 0
+ )
+ val ambientShadowOffsetX =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextClock_ambientShadowOffsetX,
+ 0
+ )
+ val ambientShadowOffsetY =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextClock_ambientShadowOffsetY,
+ 0
+ )
+ val ambientShadowAlpha =
+ attributes.getFloat(R.styleable.DoubleShadowTextClock_ambientShadowAlpha, 0f)
+ mAmbientShadowInfo =
+ ShadowInfo(
+ ambientShadowBlur.toFloat(),
+ ambientShadowOffsetX.toFloat(),
+ ambientShadowOffsetY.toFloat(),
+ ambientShadowAlpha
+ )
+ } finally {
+ attributes.recycle()
+ }
+ }
+
+ public override fun onDraw(canvas: Canvas) {
+ applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) }
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextHelper.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextHelper.kt
new file mode 100644
index 000000000000..eaac93dc2555
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextHelper.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.shadow
+
+import android.graphics.Canvas
+import android.graphics.Color
+import android.widget.TextView
+
+object DoubleShadowTextHelper {
+ data class ShadowInfo(
+ val blur: Float,
+ val offsetX: Float = 0f,
+ val offsetY: Float = 0f,
+ val alpha: Float
+ )
+
+ fun applyShadows(
+ keyShadowInfo: ShadowInfo,
+ ambientShadowInfo: ShadowInfo,
+ view: TextView,
+ canvas: Canvas,
+ onDrawCallback: () -> Unit
+ ) {
+ // We enhance the shadow by drawing the shadow twice
+ view.paint.setShadowLayer(
+ ambientShadowInfo.blur,
+ ambientShadowInfo.offsetX,
+ ambientShadowInfo.offsetY,
+ Color.argb(ambientShadowInfo.alpha, 0f, 0f, 0f)
+ )
+ onDrawCallback()
+ canvas.save()
+ canvas.clipRect(
+ view.scrollX,
+ view.scrollY + view.extendedPaddingTop,
+ view.scrollX + view.width,
+ view.scrollY + view.height
+ )
+
+ view.paint.setShadowLayer(
+ keyShadowInfo.blur,
+ keyShadowInfo.offsetX,
+ keyShadowInfo.offsetY,
+ Color.argb(keyShadowInfo.alpha, 0f, 0f, 0f)
+ )
+ onDrawCallback()
+ canvas.restore()
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
new file mode 100644
index 000000000000..25d272185bc0
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/shadow/DoubleShadowTextView.kt
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.shadow
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.widget.TextView
+import com.android.systemui.shared.R
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.ShadowInfo
+import com.android.systemui.shared.shadow.DoubleShadowTextHelper.applyShadows
+
+/** Extension of [TextView] which draws two shadows on the text (ambient and key shadows} */
+class DoubleShadowTextView
+@JvmOverloads
+constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextView(context, attrs, defStyleAttr, defStyleRes) {
+ private val mKeyShadowInfo: ShadowInfo
+ private val mAmbientShadowInfo: ShadowInfo
+
+ init {
+ val attributes =
+ context.obtainStyledAttributes(
+ attrs,
+ R.styleable.DoubleShadowTextView,
+ defStyleAttr,
+ defStyleRes
+ )
+ val drawableSize: Int
+ val drawableInsetSize: Int
+ try {
+ val keyShadowBlur =
+ attributes.getDimensionPixelSize(R.styleable.DoubleShadowTextView_keyShadowBlur, 0)
+ val keyShadowOffsetX =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_keyShadowOffsetX,
+ 0
+ )
+ val keyShadowOffsetY =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_keyShadowOffsetY,
+ 0
+ )
+ val keyShadowAlpha =
+ attributes.getFloat(R.styleable.DoubleShadowTextView_keyShadowAlpha, 0f)
+ mKeyShadowInfo =
+ ShadowInfo(
+ keyShadowBlur.toFloat(),
+ keyShadowOffsetX.toFloat(),
+ keyShadowOffsetY.toFloat(),
+ keyShadowAlpha
+ )
+ val ambientShadowBlur =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_ambientShadowBlur,
+ 0
+ )
+ val ambientShadowOffsetX =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_ambientShadowOffsetX,
+ 0
+ )
+ val ambientShadowOffsetY =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_ambientShadowOffsetY,
+ 0
+ )
+ val ambientShadowAlpha =
+ attributes.getFloat(R.styleable.DoubleShadowTextView_ambientShadowAlpha, 0f)
+ mAmbientShadowInfo =
+ ShadowInfo(
+ ambientShadowBlur.toFloat(),
+ ambientShadowOffsetX.toFloat(),
+ ambientShadowOffsetY.toFloat(),
+ ambientShadowAlpha
+ )
+ drawableSize =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_drawableIconSize,
+ 0
+ )
+ drawableInsetSize =
+ attributes.getDimensionPixelSize(
+ R.styleable.DoubleShadowTextView_drawableIconInsetSize,
+ 0
+ )
+ } finally {
+ attributes.recycle()
+ }
+
+ val drawables = arrayOf<Drawable?>(null, null, null, null)
+ for ((index, drawable) in compoundDrawablesRelative.withIndex()) {
+ if (drawable == null) continue
+ drawables[index] =
+ DoubleShadowIconDrawable(
+ mKeyShadowInfo,
+ mAmbientShadowInfo,
+ drawable,
+ drawableSize,
+ drawableInsetSize
+ )
+ }
+ setCompoundDrawablesRelative(drawables[0], drawables[1], drawables[2], drawables[3])
+ }
+
+ public override fun onDraw(canvas: Canvas) {
+ applyShadows(mKeyShadowInfo, mAmbientShadowInfo, this, canvas) { super.onDraw(canvas) }
+ }
+}
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 f0210fd4c914..5d6598d63a1b 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
@@ -49,6 +49,8 @@ public final class InteractionJankMonitorWrapper {
InteractionJankMonitor.CUJ_LAUNCHER_APP_LAUNCH_FROM_WIDGET;
public static final int CUJ_SPLIT_SCREEN_ENTER =
InteractionJankMonitor.CUJ_SPLIT_SCREEN_ENTER;
+ public static final int CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION =
+ InteractionJankMonitor.CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION;
@IntDef({
CUJ_APP_LAUNCH_FROM_RECENTS,
@@ -57,6 +59,7 @@ public final class InteractionJankMonitorWrapper {
CUJ_APP_CLOSE_TO_PIP,
CUJ_QUICK_SWITCH,
CUJ_APP_LAUNCH_FROM_WIDGET,
+ CUJ_LAUNCHER_UNLOCK_ENTRANCE_ANIMATION
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
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 97e024238778..85278dd4b883 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
@@ -47,6 +47,8 @@ public class QuickStepContract {
public static final String KEY_EXTRA_SHELL_PIP = "extra_shell_pip";
// See ISplitScreen.aidl
public static final String KEY_EXTRA_SHELL_SPLIT_SCREEN = "extra_shell_split_screen";
+ // See IFloatingTasks.aidl
+ public static final String KEY_EXTRA_SHELL_FLOATING_TASKS = "extra_shell_floating_tasks";
// See IOneHanded.aidl
public static final String KEY_EXTRA_SHELL_ONE_HANDED = "extra_shell_one_handed";
// See IShellTransitions.aidl
@@ -60,6 +62,8 @@ public class QuickStepContract {
// See IRecentTasks.aidl
public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks";
public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation";
+ // See IDesktopMode.aidl
+ public static final String KEY_EXTRA_SHELL_DESKTOP_MODE = "extra_shell_desktop_mode";
public static final String NAV_BAR_MODE_3BUTTON_OVERLAY =
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 0e1e0cb2adcf..b444f4c1110b 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -137,6 +137,10 @@ open class ClockEventController @Inject constructor(
override fun onThemeChanged() {
updateFun.updateColors()
}
+
+ override fun onDensityOrFontScaleChanged() {
+ clock?.events?.onFontSettingChanged()
+ }
}
private val batteryCallback = object : BatteryStateChangeCallback {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 919b71bbf879..71470e8870de 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -52,20 +52,20 @@ data class KeyguardFaceListenModel(
val becauseCannotSkipBouncer: Boolean,
val biometricSettingEnabledForUser: Boolean,
val bouncerFullyShown: Boolean,
- val bouncerIsOrWillShow: Boolean,
val faceAuthenticated: Boolean,
val faceDisabled: Boolean,
+ val faceLockedOut: Boolean,
+ val fpLockedOut: Boolean,
val goingToSleep: Boolean,
- val keyguardAwakeExcludingBouncerShowing: Boolean,
+ val keyguardAwake: Boolean,
val keyguardGoingAway: Boolean,
val listeningForFaceAssistant: Boolean,
val occludingAppRequestingFaceAuth: Boolean,
- val onlyFaceEnrolled: Boolean,
val primaryUser: Boolean,
val scanningAllowedByStrongAuth: Boolean,
val secureCameraLaunched: Boolean,
val switchingUser: Boolean,
- val udfpsBouncerShowing: Boolean
+ val udfpsBouncerShowing: Boolean,
) : KeyguardListenModel()
/**
* Verbose debug information associated with [KeyguardUpdateMonitor.shouldTriggerActiveUnlock].
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
index 20fa8f817dc0..453072bc42da 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java
@@ -48,6 +48,9 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
private ConstraintLayout mContainer;
private int mDisappearYTranslation;
private View[][] mViews;
+ private int mYTrans;
+ private int mYTransOffset;
+ private View mBouncerMessageView;
@DevicePostureInt private int mLastDevicePosture = DEVICE_POSTURE_UNKNOWN;
public KeyguardPINView(Context context) {
@@ -67,6 +70,8 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
mContext, android.R.interpolator.fast_out_linear_in));
mDisappearYTranslation = getResources().getDimensionPixelSize(
R.dimen.disappear_y_translation);
+ mYTrans = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry);
+ mYTransOffset = getResources().getDimensionPixelSize(R.dimen.pin_view_trans_y_entry_offset);
}
@Override
@@ -138,6 +143,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
super.onFinishInflate();
mContainer = findViewById(R.id.pin_container);
+ mBouncerMessageView = findViewById(R.id.bouncer_message_area);
mViews = new View[][]{
new View[]{
findViewById(R.id.row0), null, null
@@ -206,6 +212,12 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
/** Animate subviews according to expansion or time. */
private void animate(float progress) {
+ Interpolator standardDecelerate = Interpolators.STANDARD_DECELERATE;
+ Interpolator legacyDecelerate = Interpolators.LEGACY_DECELERATE;
+
+ mBouncerMessageView.setTranslationY(
+ mYTrans - mYTrans * standardDecelerate.getInterpolation(progress));
+
for (int i = 0; i < mViews.length; i++) {
View[] row = mViews[i];
for (View view : row) {
@@ -213,14 +225,15 @@ public class KeyguardPINView extends KeyguardPinBasedInputView {
continue;
}
- float scaledProgress = MathUtils.constrain(
+ float scaledProgress = legacyDecelerate.getInterpolation(MathUtils.constrain(
(progress - 0.075f * i) / (1f - 0.075f * mViews.length),
0f,
1f
- );
+ ));
view.setAlpha(scaledProgress);
- Interpolator interpolator = Interpolators.STANDARD_ACCELERATE;
- view.setTranslationY(40 - (40 * interpolator.getInterpolation(scaledProgress)));
+ int yDistance = mYTrans + mYTransOffset * i;
+ view.setTranslationY(
+ yDistance - (yDistance * standardDecelerate.getInterpolation(progress)));
if (view instanceof NumPadAnimationListener) {
((NumPadAnimationListener) view).setProgress(scaledProgress);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index 2cc5ccdc3fa1..1e5c53de4446 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -24,6 +24,7 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
import android.animation.Animator;
@@ -106,6 +107,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView {
return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
return R.string.kg_prompt_reason_timeout_password;
+ case PROMPT_REASON_TRUSTAGENT_EXPIRED:
+ return R.string.kg_prompt_reason_timeout_password;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 987164557a7a..5b223242670c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -330,6 +330,9 @@ public class KeyguardPatternViewController
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
break;
+ case PROMPT_REASON_TRUSTAGENT_EXPIRED:
+ mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ break;
case PROMPT_REASON_NONE:
break;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index c46e33d9fd53..0a91150e6c39 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -22,6 +22,7 @@ import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NON_STRONG
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT;
+import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
import android.animation.Animator;
@@ -123,6 +124,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView
return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
return R.string.kg_prompt_reason_timeout_pin;
+ case PROMPT_REASON_TRUSTAGENT_EXPIRED:
+ return R.string.kg_prompt_reason_timeout_pin;
case PROMPT_REASON_NONE:
return 0;
default:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 9f4585fb1a92..89fcc47caf57 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -82,6 +82,12 @@ public class KeyguardPinViewController
}
@Override
+ public void startAppearAnimation() {
+ mMessageAreaController.setMessageIfEmpty(R.string.keyguard_enter_your_pin);
+ super.startAppearAnimation();
+ }
+
+ @Override
public boolean startDisappearAnimation(Runnable finishRunnable) {
return mView.startDisappearAnimation(
mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index f73c98e4971b..2bdb1b894c4a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -22,8 +22,6 @@ import static android.view.WindowInsets.Type.systemBars;
import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
import static java.lang.Integer.max;
@@ -87,8 +85,8 @@ import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
+import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter;
import com.android.systemui.statusbar.policy.UserSwitcherController;
-import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter;
import com.android.systemui.user.data.source.UserRecord;
import com.android.systemui.util.settings.GlobalSettings;
@@ -1098,6 +1096,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
return;
}
+ mView.setAlpha(1f);
mUserSwitcherViewGroup.setAlpha(0f);
ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA,
1f);
@@ -1137,7 +1136,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
KeyguardUserSwitcherAnchor anchor = mView.findViewById(R.id.user_switcher_anchor);
- BaseUserAdapter adapter = new BaseUserAdapter(mUserSwitcherController) {
+ BaseUserSwitcherAdapter adapter = new BaseUserSwitcherAdapter(mUserSwitcherController) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
UserRecord item = getItem(position);
@@ -1172,8 +1171,7 @@ public class KeyguardSecurityContainer extends FrameLayout {
}
textView.setSelected(item == currentUser);
view.setEnabled(item.isSwitchToEnabled);
- view.setAlpha(view.isEnabled() ? USER_SWITCH_ENABLED_ALPHA :
- USER_SWITCH_DISABLED_ALPHA);
+ UserSwitcherController.setSelectableAlpha(view);
return view;
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index ac00e9453c97..9d0a8acf02b4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -61,6 +61,12 @@ public interface KeyguardSecurityView {
int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
/**
+ * Some auth is required because the trustagent expired either from timeout or manually by
+ * the user
+ */
+ int PROMPT_REASON_TRUSTAGENT_EXPIRED = 8;
+
+ /**
* Reset the view and prepare to take input. This should do things like clearing the
* password or pattern and clear error messages.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
new file mode 100644
index 000000000000..b4bfca1185f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimInputView.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.content.Context
+import android.util.AttributeSet
+import android.widget.ImageView
+import androidx.core.graphics.drawable.DrawableCompat
+import com.android.systemui.R
+
+abstract class KeyguardSimInputView(context: Context, attrs: AttributeSet) :
+ KeyguardPinBasedInputView(context, attrs) {
+ private var simImageView: ImageView? = null
+ private var disableESimButton: KeyguardEsimArea? = null
+
+ override fun onFinishInflate() {
+ simImageView = findViewById(R.id.keyguard_sim)
+ disableESimButton = findViewById(R.id.keyguard_esim_area)
+ super.onFinishInflate()
+ }
+
+ /** Set UI state based on whether there is a locked eSim card */
+ fun setESimLocked(isESimLocked: Boolean, subId: Int) {
+ disableESimButton?.setSubscriptionId(subId)
+ disableESimButton?.visibility = if (isESimLocked) VISIBLE else GONE
+ simImageView?.visibility = if (isESimLocked) GONE else VISIBLE
+ }
+
+ override fun reloadColors() {
+ super.reloadColors()
+ val customAttrs = intArrayOf(android.R.attr.textColorSecondary)
+ val a = context.obtainStyledAttributes(customAttrs)
+ val imageColor = a.getColor(0, 0)
+ a.recycle()
+ simImageView?.let {
+ val wrappedDrawable = DrawableCompat.wrap(it.drawable)
+ DrawableCompat.setTint(wrappedDrawable, imageColor)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index ae9d3dfec3b2..9d170150a709 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -18,21 +18,14 @@ package com.android.keyguard;
import android.content.Context;
import android.content.res.Configuration;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
-import android.view.View;
-import android.widget.ImageView;
-
-import androidx.core.graphics.drawable.DrawableCompat;
import com.android.systemui.R;
/**
* Displays a PIN pad for unlocking.
*/
-public class KeyguardSimPinView extends KeyguardPinBasedInputView {
- private ImageView mSimImageView;
+public class KeyguardSimPinView extends KeyguardSimInputView {
public static final String TAG = "KeyguardSimPinView";
public KeyguardSimPinView(Context context) {
@@ -43,12 +36,6 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
super(context, attrs);
}
- public void setEsimLocked(boolean locked, int subscriptionId) {
- KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area);
- esimButton.setSubscriptionId(subscriptionId);
- esimButton.setVisibility(locked ? View.VISIBLE : View.GONE);
- }
-
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -68,7 +55,6 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
@Override
protected void onFinishInflate() {
- mSimImageView = findViewById(R.id.keyguard_sim);
super.onFinishInflate();
if (mEcaView instanceof EmergencyCarrierArea) {
@@ -86,17 +72,4 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_sim_pin_unlock);
}
-
- @Override
- public void reloadColors() {
- super.reloadColors();
-
- int[] customAttrs = {android.R.attr.textColorSecondary};
- TypedArray a = getContext().obtainStyledAttributes(customAttrs);
- int imageColor = a.getColor(0, 0);
- a.recycle();
- Drawable wrappedDrawable = DrawableCompat.wrap(mSimImageView.getDrawable());
- DrawableCompat.setTint(wrappedDrawable, imageColor);
- }
}
-
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 2a2e9bf54bb2..91bf20f90690 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -105,7 +105,7 @@ public class KeyguardSimPinViewController
showDefaultMessage();
}
- mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId), mSubId);
+ mView.setESimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId), mSubId);
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index c0971bf8c16d..5f45fe31a779 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -19,13 +19,8 @@ package com.android.keyguard;
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
-import android.widget.ImageView;
-
-import androidx.core.graphics.drawable.DrawableCompat;
import com.android.systemui.R;
@@ -33,8 +28,7 @@ import com.android.systemui.R;
/**
* Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier.
*/
-public class KeyguardSimPukView extends KeyguardPinBasedInputView {
- private ImageView mSimImageView;
+public class KeyguardSimPukView extends KeyguardSimInputView {
private static final boolean DEBUG = KeyguardConstants.DEBUG;
public static final String TAG = "KeyguardSimPukView";
@@ -86,7 +80,6 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
@Override
protected void onFinishInflate() {
- mSimImageView = findViewById(R.id.keyguard_sim);
super.onFinishInflate();
if (mEcaView instanceof EmergencyCarrierArea) {
@@ -104,18 +97,4 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
return getContext().getString(
com.android.internal.R.string.keyguard_accessibility_sim_puk_unlock);
}
-
- @Override
- public void reloadColors() {
- super.reloadColors();
-
- int[] customAttrs = {android.R.attr.textColorSecondary};
- TypedArray a = getContext().obtainStyledAttributes(customAttrs);
- int imageColor = a.getColor(0, 0);
- a.recycle();
- Drawable wrappedDrawable = DrawableCompat.wrap(mSimImageView.getDrawable());
- DrawableCompat.setTint(wrappedDrawable, imageColor);
- }
}
-
-
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index 203f9b660536..5995e859c786 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -30,7 +30,6 @@ import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
-import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
@@ -108,6 +107,14 @@ public class KeyguardSimPukViewController
}
@Override
+ public void onResume(int reason) {
+ super.onResume(reason);
+ if (mShowDefaultMessage) {
+ showDefaultMessage();
+ }
+ }
+
+ @Override
void resetState() {
super.resetState();
mStateMachine.reset();
@@ -173,11 +180,9 @@ public class KeyguardSimPukViewController
if (mShowDefaultMessage) {
showDefaultMessage();
}
- boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId);
- KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area);
- esimButton.setSubscriptionId(mSubId);
- esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE);
+ mView.setESimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId), mSubId);
+
mPasswordEntry.requestFocus();
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 1463840b4cde..f259a542908c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -157,6 +157,7 @@ import com.google.android.collect.Lists;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -167,6 +168,7 @@ import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
+import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Provider;
@@ -266,6 +268,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final AuthController mAuthController;
private final StatusBarStateController mStatusBarStateController;
private final UiEventLogger mUiEventLogger;
+ private final Set<Integer> mFaceAcquiredInfoIgnoreList;
private int mStatusBarState;
private final StatusBarStateController.StateListener mStatusBarStateControllerListener =
new StatusBarStateController.StateListener() {
@@ -1008,6 +1011,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private void handleFaceAuthFailed() {
Assert.isMainThread();
+ mLogger.d("onFaceAuthFailed");
mFaceCancelSignal = null;
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -1624,6 +1628,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void onAuthenticationHelp(int helpMsgId, CharSequence helpString) {
+ if (mFaceAcquiredInfoIgnoreList.contains(helpMsgId)) {
+ return;
+ }
handleFaceHelp(helpMsgId, helpString.toString());
}
@@ -1916,6 +1923,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
mWakeOnFingerprintAcquiredStart = context.getResources()
.getBoolean(com.android.internal.R.bool.kg_wake_on_acquire_start);
+ mFaceAcquiredInfoIgnoreList = Arrays.stream(
+ mContext.getResources().getIntArray(
+ R.array.config_face_acquire_device_entry_ignorelist))
+ .boxed()
+ .collect(Collectors.toSet());
mHandler = new Handler(mainLooper) {
@Override
@@ -2567,11 +2579,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
final boolean statusBarShadeLocked = mStatusBarState == StatusBarState.SHADE_LOCKED;
- // mKeyguardIsVisible is true even when the bouncer is shown, we don't want to run face auth
- // on bouncer if both fp and fingerprint are enrolled.
- final boolean awakeKeyguardExcludingBouncerShowing = mKeyguardIsVisible
- && mDeviceInteractive && !mGoingToSleep
- && !statusBarShadeLocked && !mBouncerIsOrWillBeShowing;
+ final boolean awakeKeyguard = mKeyguardIsVisible && mDeviceInteractive && !mGoingToSleep
+ && !statusBarShadeLocked;
final int user = getCurrentUser();
final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
final boolean isLockDown =
@@ -2611,15 +2620,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean faceDisabledForUser = isFaceDisabled(user);
final boolean biometricEnabledForUser = mBiometricEnabledForUser.get(user);
final boolean shouldListenForFaceAssistant = shouldListenForFaceAssistant();
- final boolean onlyFaceEnrolled = isOnlyFaceEnrolled();
+ final boolean fpOrFaceIsLockedOut = isFaceLockedOut() || fpLockedout;
// Only listen if this KeyguardUpdateMonitor belongs to the primary user. There is an
// instance of KeyguardUpdateMonitor for each user but KeyguardUpdateMonitor is user-aware.
final boolean shouldListen =
- ((mBouncerFullyShown && !mGoingToSleep && onlyFaceEnrolled)
+ (mBouncerFullyShown && !mGoingToSleep
|| mAuthInterruptActive
|| mOccludingAppRequestingFace
- || awakeKeyguardExcludingBouncerShowing
+ || awakeKeyguard
|| shouldListenForFaceAssistant
|| mAuthController.isUdfpsFingerDown()
|| mUdfpsBouncerShowing)
@@ -2628,7 +2637,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& strongAuthAllowsScanning && mIsPrimaryUser
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
&& !faceAuthenticated
- && !fpLockedout;
+ && !fpOrFaceIsLockedOut;
// Aggregate relevant fields for debug logging.
maybeLogListenerModelData(
@@ -2640,15 +2649,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
becauseCannotSkipBouncer,
biometricEnabledForUser,
mBouncerFullyShown,
- mBouncerIsOrWillBeShowing,
faceAuthenticated,
faceDisabledForUser,
+ isFaceLockedOut(),
+ fpLockedout,
mGoingToSleep,
- awakeKeyguardExcludingBouncerShowing,
+ awakeKeyguard,
mKeyguardGoingAway,
shouldListenForFaceAssistant,
mOccludingAppRequestingFace,
- onlyFaceEnrolled,
mIsPrimaryUser,
strongAuthAllowsScanning,
mSecureCameraLaunched,
@@ -2658,11 +2667,6 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return shouldListen;
}
- private boolean isOnlyFaceEnrolled() {
- return isFaceEnrolled()
- && !getCachedIsUnlockWithFingerprintPossible(sCurrentUser);
- }
-
private void maybeLogListenerModelData(KeyguardListenModel model) {
mLogger.logKeyguardListenerModel(model);
@@ -3214,8 +3218,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onKeyguardBouncerStateChanged(mBouncerIsOrWillBeShowing);
}
}
- updateBiometricListeningState(BIOMETRIC_ACTION_UPDATE,
- FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN);
+ updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
}
if (wasBouncerFullyShown != mBouncerFullyShown) {
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
index e0cafaed5a35..41111e3d3c6c 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -86,7 +86,7 @@ class NumPadAnimator {
public void setProgress(float progress) {
mBackground.setCornerRadius(mEndRadius + (mStartRadius - mEndRadius) * progress);
- int height = (int) (mHeight * 0.8f + mHeight * 0.2 * progress);
+ int height = (int) (mHeight * 0.7f + mHeight * 0.3 * progress);
int difference = mHeight - height;
mBackground.setBounds(0, difference / 2, mHeight, mHeight - difference / 2);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
new file mode 100644
index 000000000000..2c2ab7b39161
--- /dev/null
+++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.logging
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.dagger.BiometricMessagesLog
+import javax.inject.Inject
+
+/** Helper class for logging for [com.android.systemui.biometrics.FaceHelpMessageDeferral] */
+@SysUISingleton
+class FaceMessageDeferralLogger
+@Inject
+constructor(@BiometricMessagesLog private val logBuffer: LogBuffer) :
+ BiometricMessageDeferralLogger(logBuffer, "FaceMessageDeferralLogger")
+
+open class BiometricMessageDeferralLogger(
+ private val logBuffer: LogBuffer,
+ private val tag: String
+) {
+ fun reset() {
+ logBuffer.log(tag, DEBUG, "reset")
+ }
+
+ fun logUpdateMessage(acquiredInfo: Int, helpString: String) {
+ logBuffer.log(
+ tag,
+ DEBUG,
+ {
+ int1 = acquiredInfo
+ str1 = helpString
+ },
+ { "updateMessage acquiredInfo=$int1 helpString=$str1" }
+ )
+ }
+
+ fun logFrameProcessed(
+ acquiredInfo: Int,
+ totalFrames: Int,
+ mostFrequentAcquiredInfoToDefer: String? // may not meet the threshold
+ ) {
+ logBuffer.log(
+ tag,
+ DEBUG,
+ {
+ int1 = acquiredInfo
+ int2 = totalFrames
+ str1 = mostFrequentAcquiredInfoToDefer
+ },
+ {
+ "frameProcessed acquiredInfo=$int1 totalFrames=$int2 " +
+ "messageToShowOnTimeout=$str1"
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 7fc81231c90b..a5fdc68226e8 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -103,7 +103,6 @@ import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
import com.android.systemui.statusbar.policy.AccessibilityController;
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
-import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.BluetoothController;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -253,7 +252,6 @@ public class Dependency {
@Inject Lazy<UserInfoController> mUserInfoController;
@Inject Lazy<KeyguardStateController> mKeyguardMonitor;
@Inject Lazy<KeyguardUpdateMonitor> mKeyguardUpdateMonitor;
- @Inject Lazy<BatteryController> mBatteryController;
@Inject Lazy<NightDisplayListener> mNightDisplayListener;
@Inject Lazy<ReduceBrightColorsController> mReduceBrightColorsController;
@Inject Lazy<ManagedProfileController> mManagedProfileController;
@@ -404,8 +402,6 @@ public class Dependency {
mProviders.put(UserInfoController.class, mUserInfoController::get);
- mProviders.put(BatteryController.class, mBatteryController::get);
-
mProviders.put(NightDisplayListener.class, mNightDisplayListener::get);
mProviders.put(ReduceBrightColorsController.class, mReduceBrightColorsController::get);
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 2e13903814a5..67b683ec643a 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -455,7 +455,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
}
}
- boolean needToUpdateProviderViews = false;
final String newUniqueId = mDisplayInfo.uniqueId;
if (!Objects.equals(newUniqueId, mDisplayUniqueId)) {
mDisplayUniqueId = newUniqueId;
@@ -473,37 +472,6 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
setupDecorations();
return;
}
-
- if (mScreenDecorHwcLayer != null) {
- updateHwLayerRoundedCornerDrawable();
- updateHwLayerRoundedCornerExistAndSize();
- }
- needToUpdateProviderViews = true;
- }
-
- final float newRatio = getPhysicalPixelDisplaySizeRatio();
- if (mRoundedCornerResDelegate.getPhysicalPixelDisplaySizeRatio() != newRatio) {
- mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(newRatio);
- if (mScreenDecorHwcLayer != null) {
- updateHwLayerRoundedCornerExistAndSize();
- }
- needToUpdateProviderViews = true;
- }
-
- if (needToUpdateProviderViews) {
- updateOverlayProviderViews(null);
- } else {
- updateOverlayProviderViews(new Integer[] {
- mFaceScanningViewId,
- R.id.display_cutout,
- R.id.display_cutout_left,
- R.id.display_cutout_right,
- R.id.display_cutout_bottom,
- });
- }
-
- if (mScreenDecorHwcLayer != null) {
- mScreenDecorHwcLayer.onDisplayChanged(newUniqueId);
}
}
};
@@ -1069,6 +1037,8 @@ public class ScreenDecorations extends CoreStartable implements Tunable , Dumpab
&& (newRotation != mRotation || displayModeChanged(mDisplayMode, newMod))) {
mRotation = newRotation;
mDisplayMode = newMod;
+ mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
+ getPhysicalPixelDisplaySizeRatio());
if (mScreenDecorHwcLayer != null) {
mScreenDecorHwcLayer.pendingConfigChange = false;
mScreenDecorHwcLayer.updateRotation(mRotation);
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 9138b2346ab8..9cfd3999a0ec 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,7 +16,6 @@
package com.android.systemui;
-import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.Application;
import android.app.Notification;
@@ -29,7 +28,6 @@ import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Looper;
import android.os.Process;
-import android.os.RemoteException;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
@@ -130,13 +128,6 @@ public class SystemUIApplication extends Application implements
ThreadedRenderer.EGL_CONTEXT_PRIORITY_HIGH_IMG);
}
- // Enable binder tracing on system server for calls originating from SysUI
- try {
- ActivityManager.getService().enableBinderTracing();
- } catch (RemoteException e) {
- Log.e(TAG, "Unable to enable binder tracing", e);
- }
-
registerReceiver(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
index 5f586c927ef7..a21f45f701b3 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIInitializer.java
@@ -96,7 +96,9 @@ public abstract class SystemUIInitializer {
.setStartingSurface(mWMComponent.getStartingSurface())
.setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper())
.setRecentTasks(mWMComponent.getRecentTasks())
- .setBackAnimation(mWMComponent.getBackAnimation());
+ .setBackAnimation(mWMComponent.getBackAnimation())
+ .setFloatingTasks(mWMComponent.getFloatingTasks())
+ .setDesktopMode(mWMComponent.getDesktopMode());
// Only initialize when not starting from tests since this currently initializes some
// components that shouldn't be run in the test environment
@@ -115,7 +117,9 @@ public abstract class SystemUIInitializer {
.setDisplayAreaHelper(Optional.ofNullable(null))
.setStartingSurface(Optional.ofNullable(null))
.setRecentTasks(Optional.ofNullable(null))
- .setBackAnimation(Optional.ofNullable(null));
+ .setBackAnimation(Optional.ofNullable(null))
+ .setFloatingTasks(Optional.ofNullable(null))
+ .setDesktopMode(Optional.ofNullable(null));
}
mSysUIComponent = builder.build();
if (initializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index b976181b3645..e3c04a379fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -689,7 +689,7 @@ class WindowMagnificationController implements View.OnTouchListener, SurfaceHold
if (Float.isNaN(centerX)) {
centerX = mMagnificationFrame.centerX();
}
- if (Float.isNaN(centerX)) {
+ if (Float.isNaN(centerY)) {
centerY = mMagnificationFrame.centerY();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 436b756ea0cb..8f5cbb76222f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -54,6 +54,8 @@ import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.jank.InteractionJankMonitor;
@@ -127,6 +129,7 @@ public class AuthContainerView extends LinearLayout
private final float mTranslationY;
@ContainerState private int mContainerState = STATE_UNKNOWN;
private final Set<Integer> mFailedModalities = new HashSet<Integer>();
+ private final OnBackInvokedCallback mBackCallback = this::onBackInvoked;
private final @Background DelayableExecutor mBackgroundExecutor;
private int mOrientation;
@@ -362,8 +365,7 @@ public class AuthContainerView extends LinearLayout
return false;
}
if (event.getAction() == KeyEvent.ACTION_UP) {
- sendEarlyUserCanceled();
- animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ onBackInvoked();
}
return true;
});
@@ -373,6 +375,11 @@ public class AuthContainerView extends LinearLayout
requestFocus();
}
+ private void onBackInvoked() {
+ sendEarlyUserCanceled();
+ animateAway(AuthDialogCallback.DISMISSED_USER_CANCELED);
+ }
+
void sendEarlyUserCanceled() {
mConfig.mCallback.onSystemEvent(
BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL, getRequestId());
@@ -520,6 +527,11 @@ public class AuthContainerView extends LinearLayout
.start();
});
}
+ OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
+ if (dispatcher != null) {
+ dispatcher.registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mBackCallback);
+ }
}
private Animator.AnimatorListener getJankListener(View v, String type, long timeout) {
@@ -618,6 +630,10 @@ public class AuthContainerView extends LinearLayout
@Override
public void onDetachedFromWindow() {
+ OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
+ if (dispatcher != null) {
+ findOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback);
+ }
super.onDetachedFromWindow();
mWakefulnessLifecycle.removeObserver(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 163e3abd4cbc..1ceb6b3ca172 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -57,6 +57,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserManager;
+import android.util.DisplayUtils;
import android.util.Log;
import android.util.RotationUtils;
import android.util.SparseBooleanArray;
@@ -120,8 +121,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
private final Provider<UdfpsController> mUdfpsControllerFactory;
private final Provider<SidefpsController> mSidefpsControllerFactory;
- @NonNull private Point mStableDisplaySize = new Point();
-
private final Display mDisplay;
private float mScaleFactor = 1f;
// sensor locations without any resolution scaling nor rotation adjustments:
@@ -188,7 +187,10 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
public void onReceive(Context context, Intent intent) {
if (mCurrentDialog != null
&& Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
- Log.w(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received");
+ String reason = intent.getStringExtra("reason");
+ reason = (reason != null) ? reason : "unknown";
+ Log.d(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received, reason: " + reason);
+
mCurrentDialog.dismissWithoutCallback(true /* animate */);
mCurrentDialog = null;
@@ -287,7 +289,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
});
mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation);
- mUdfpsController.setHalControlsIllumination(mUdfpsProps.get(0).halControlsIllumination);
mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect();
}
@@ -372,18 +373,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Override
public void onTryAgainPressed(long requestId) {
- if (mReceiver == null) {
- Log.e(TAG, "onTryAgainPressed: Receiver is null");
- return;
- }
-
- if (requestId != mCurrentDialog.getRequestId()) {
- Log.w(TAG, "requestId doesn't match, skip onTryAgainPressed");
+ final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
+ if (receiver == null) {
+ Log.w(TAG, "Skip onTryAgainPressed");
return;
}
try {
- mReceiver.onTryAgainPressed();
+ receiver.onTryAgainPressed();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException when handling try again", e);
}
@@ -391,18 +388,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Override
public void onDeviceCredentialPressed(long requestId) {
- if (mReceiver == null) {
- Log.e(TAG, "onDeviceCredentialPressed: Receiver is null");
- return;
- }
-
- if (requestId != mCurrentDialog.getRequestId()) {
- Log.w(TAG, "requestId doesn't match, skip onDeviceCredentialPressed");
+ final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
+ if (receiver == null) {
+ Log.w(TAG, "Skip onDeviceCredentialPressed");
return;
}
try {
- mReceiver.onDeviceCredentialPressed();
+ receiver.onDeviceCredentialPressed();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException when handling credential button", e);
}
@@ -410,18 +403,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Override
public void onSystemEvent(int event, long requestId) {
- if (mReceiver == null) {
- Log.e(TAG, "onSystemEvent(" + event + "): Receiver is null");
- return;
- }
-
- if (requestId != mCurrentDialog.getRequestId()) {
- Log.w(TAG, "requestId doesn't match, skip onSystemEvent");
+ final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
+ if (receiver == null) {
+ Log.w(TAG, "Skip onSystemEvent");
return;
}
try {
- mReceiver.onSystemEvent(event);
+ receiver.onSystemEvent(event);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException when sending system event", e);
}
@@ -429,23 +418,46 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Override
public void onDialogAnimatedIn(long requestId) {
- if (mReceiver == null) {
- Log.e(TAG, "onDialogAnimatedIn: Receiver is null");
- return;
- }
-
- if (requestId != mCurrentDialog.getRequestId()) {
- Log.w(TAG, "requestId doesn't match, skip onDialogAnimatedIn");
+ final IBiometricSysuiReceiver receiver = getCurrentReceiver(requestId);
+ if (receiver == null) {
+ Log.w(TAG, "Skip onDialogAnimatedIn");
return;
}
try {
- mReceiver.onDialogAnimatedIn();
+ receiver.onDialogAnimatedIn();
} catch (RemoteException e) {
Log.e(TAG, "RemoteException when sending onDialogAnimatedIn", e);
}
}
+ @Nullable
+ private IBiometricSysuiReceiver getCurrentReceiver(long requestId) {
+ if (!isRequestIdValid(requestId)) {
+ return null;
+ }
+
+ if (mReceiver == null) {
+ Log.w(TAG, "getCurrentReceiver: Receiver is null");
+ }
+
+ return mReceiver;
+ }
+
+ private boolean isRequestIdValid(long requestId) {
+ if (mCurrentDialog == null) {
+ Log.w(TAG, "shouldNotifyReceiver: dialog already gone");
+ return false;
+ }
+
+ if (requestId != mCurrentDialog.getRequestId()) {
+ Log.w(TAG, "shouldNotifyReceiver: requestId doesn't match");
+ return false;
+ }
+
+ return true;
+ }
+
@Override
public void onDismissed(@DismissedReason int reason,
@Nullable byte[] credentialAttestation, long requestId) {
@@ -532,10 +544,11 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
*/
private void updateSensorLocations() {
mDisplay.getDisplayInfo(mCachedDisplayInfo);
-
+ final Display.Mode maxDisplayMode =
+ DisplayUtils.getMaximumResolutionDisplayMode(mCachedDisplayInfo.supportedModes);
final float scaleFactor = android.util.DisplayUtils.getPhysicalPixelDisplaySizeRatio(
- mStableDisplaySize.x, mStableDisplaySize.y, mCachedDisplayInfo.getNaturalWidth(),
- mCachedDisplayInfo.getNaturalHeight());
+ maxDisplayMode.getPhysicalWidth(), maxDisplayMode.getPhysicalHeight(),
+ mCachedDisplayInfo.getNaturalWidth(), mCachedDisplayInfo.getNaturalHeight());
if (scaleFactor == Float.POSITIVE_INFINITY) {
mScaleFactor = 1f;
} else {
@@ -813,7 +826,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
);
}
- mStableDisplaySize = mDisplayManager.getStableDisplaySize();
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
mOrientationListener.enable();
updateSensorLocations();
@@ -1197,7 +1209,6 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
final AuthDialog dialog = mCurrentDialog;
- pw.println(" stableDisplaySize=" + mStableDisplaySize);
pw.println(" mCachedDisplayInfo=" + mCachedDisplayInfo);
pw.println(" mScaleFactor=" + mScaleFactor);
pw.println(" faceAuthSensorLocationDefault=" + mFaceSensorLocationDefault);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 35d96c912b01..31e1fb4e5d9b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -23,7 +23,6 @@ import android.content.Context
import android.graphics.Point
import android.hardware.biometrics.BiometricFingerprintConstants
import android.hardware.biometrics.BiometricSourceType
-import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
@@ -95,7 +94,6 @@ class AuthRippleController @Inject constructor(
public override fun onViewAttached() {
authController.addCallback(authControllerCallback)
updateRippleColor()
- updateSensorLocation()
updateUdfpsDependentParams()
udfpsController?.addCallback(udfpsControllerCallback)
configurationController.addCallback(configurationChangedListener)
@@ -237,7 +235,11 @@ class AuthRippleController @Inject constructor(
}
private fun showDwellRipple() {
- mView.startDwellRipple(statusBarStateController.isDozing)
+ updateSensorLocation()
+ fingerprintSensorLocation?.let {
+ mView.setFingerprintSensorLocation(it, udfpsRadius)
+ mView.startDwellRipple(statusBarStateController.isDozing)
+ }
}
private val keyguardUpdateMonitorCallback =
@@ -264,7 +266,7 @@ class AuthRippleController @Inject constructor(
acquireInfo: Int
) {
if (biometricSourceType == BiometricSourceType.FINGERPRINT &&
- BiometricFingerprintConstants.shouldTurnOffHbm(acquireInfo) &&
+ BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquireInfo) &&
acquireInfo != BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD) {
// received an 'acquiredBad' message, so immediately retract
mView.retractDwellRipple()
@@ -291,13 +293,6 @@ class AuthRippleController @Inject constructor(
private val udfpsControllerCallback =
object : UdfpsController.Callback {
override fun onFingerDown() {
- if (fingerprintSensorLocation == null) {
- Log.e("AuthRipple", "fingerprintSensorLocation=null onFingerDown. " +
- "Skip showing dwell ripple")
- return
- }
-
- mView.setFingerprintSensorLocation(fingerprintSensorLocation!!, udfpsRadius)
showDwellRipple()
}
@@ -310,12 +305,10 @@ class AuthRippleController @Inject constructor(
object : AuthController.Callback {
override fun onAllAuthenticatorsRegistered(modality: Int) {
updateUdfpsDependentParams()
- updateSensorLocation()
}
override fun onUdfpsLocationChanged() {
updateUdfpsDependentParams()
- updateSensorLocation()
}
}
@@ -345,13 +338,11 @@ class AuthRippleController @Inject constructor(
"\n\tudfpsRadius=$udfpsRadius")
}
"fingerprint" -> {
- updateSensorLocation()
pw.println("fingerprint ripple sensorLocation=$fingerprintSensorLocation")
showUnlockRipple(BiometricSourceType.FINGERPRINT)
}
"face" -> {
// note: only shows when about to proceed to the home screen
- updateSensorLocation()
pw.println("face ripple sensorLocation=$faceSensorLocation")
showUnlockRipple(BiometricSourceType.FACE)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt
deleted file mode 100644
index f2d8aaa30f21..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricMessageDeferral.kt
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-/**
- * Provides whether an acquired error message should be shown immediately when its received (see
- * [shouldDefer]) or should be shown when the biometric error is received [getDeferredMessage].
- * @property excludedMessages messages that are excluded from counts
- * @property messagesToDefer messages that shouldn't show immediately when received, but may be
- * shown later if the message is the most frequent message processed and meets [THRESHOLD]
- * percentage of all messages (excluding [excludedMessages])
- */
-class BiometricMessageDeferral(
- private val excludedMessages: Set<Int>,
- private val messagesToDefer: Set<Int>
-) {
- private val msgCounts: MutableMap<Int, Int> = HashMap() // msgId => frequency of msg
- private val msgIdToCharSequence: MutableMap<Int, CharSequence> = HashMap() // msgId => message
- private var totalRelevantMessages = 0
- private var mostFrequentMsgIdToDefer: Int? = null
-
- /** Reset all saved counts. */
- fun reset() {
- totalRelevantMessages = 0
- msgCounts.clear()
- msgIdToCharSequence.clear()
- }
-
- /** Whether the given message should be deferred instead of being shown immediately. */
- fun shouldDefer(acquiredMsgId: Int): Boolean {
- return messagesToDefer.contains(acquiredMsgId)
- }
-
- /**
- * Adds the acquiredMsgId to the counts if it's not in [excludedMessages]. We still count
- * messages that shouldn't be deferred in these counts.
- */
- fun processMessage(acquiredMsgId: Int, helpString: CharSequence) {
- if (excludedMessages.contains(acquiredMsgId)) {
- return
- }
-
- totalRelevantMessages++
- msgIdToCharSequence[acquiredMsgId] = helpString
-
- val newAcquiredMsgCount = msgCounts.getOrDefault(acquiredMsgId, 0) + 1
- msgCounts[acquiredMsgId] = newAcquiredMsgCount
- if (
- messagesToDefer.contains(acquiredMsgId) &&
- (mostFrequentMsgIdToDefer == null ||
- newAcquiredMsgCount > msgCounts.getOrDefault(mostFrequentMsgIdToDefer!!, 0))
- ) {
- mostFrequentMsgIdToDefer = acquiredMsgId
- }
- }
-
- /**
- * Get the most frequent deferred message that meets the [THRESHOLD] percentage of processed
- * messages excluding [excludedMessages].
- * @return null if no messages have been deferred OR deferred messages didn't meet the
- * [THRESHOLD] percentage of messages to show.
- */
- fun getDeferredMessage(): CharSequence? {
- mostFrequentMsgIdToDefer?.let {
- if (msgCounts.getOrDefault(it, 0) > (THRESHOLD * totalRelevantMessages)) {
- return msgIdToCharSequence[mostFrequentMsgIdToDefer]
- }
- }
-
- return null
- }
- companion object {
- const val THRESHOLD = .5f
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
new file mode 100644
index 000000000000..fabc1c1bb908
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/FaceHelpMessageDeferral.kt
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.content.res.Resources
+import com.android.keyguard.logging.BiometricMessageDeferralLogger
+import com.android.keyguard.logging.FaceMessageDeferralLogger
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import java.io.PrintWriter
+import java.util.*
+import javax.inject.Inject
+
+/**
+ * Provides whether a face acquired help message should be shown immediately when its received or
+ * should be shown when face auth times out. See [updateMessage] and [getDeferredMessage].
+ */
+@SysUISingleton
+class FaceHelpMessageDeferral
+@Inject
+constructor(
+ @Main resources: Resources,
+ logBuffer: FaceMessageDeferralLogger,
+ dumpManager: DumpManager
+) :
+ BiometricMessageDeferral(
+ resources.getIntArray(R.array.config_face_help_msgs_defer_until_timeout).toHashSet(),
+ resources.getFloat(R.dimen.config_face_help_msgs_defer_until_timeout_threshold),
+ logBuffer,
+ dumpManager
+ )
+
+/**
+ * @property messagesToDefer messages that shouldn't show immediately when received, but may be
+ * shown later if the message is the most frequent acquiredInfo processed and meets [threshold]
+ * percentage of all passed acquired frames.
+ */
+open class BiometricMessageDeferral(
+ private val messagesToDefer: Set<Int>,
+ private val threshold: Float,
+ private val logBuffer: BiometricMessageDeferralLogger,
+ dumpManager: DumpManager
+) : Dumpable {
+ private val acquiredInfoToFrequency: MutableMap<Int, Int> = HashMap()
+ private val acquiredInfoToHelpString: MutableMap<Int, String> = HashMap()
+ private var mostFrequentAcquiredInfoToDefer: Int? = null
+ private var totalFrames = 0
+
+ init {
+ dumpManager.registerDumpable(this.javaClass.name, this)
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("messagesToDefer=$messagesToDefer")
+ pw.println("totalFrames=$totalFrames")
+ pw.println("threshold=$threshold")
+ }
+
+ /** Reset all saved counts. */
+ fun reset() {
+ totalFrames = 0
+ mostFrequentAcquiredInfoToDefer = null
+ acquiredInfoToFrequency.clear()
+ acquiredInfoToHelpString.clear()
+ logBuffer.reset()
+ }
+
+ /** Updates the message associated with the acquiredInfo if it's a message we may defer. */
+ fun updateMessage(acquiredInfo: Int, helpString: String) {
+ if (!messagesToDefer.contains(acquiredInfo)) {
+ return
+ }
+ if (!Objects.equals(acquiredInfoToHelpString[acquiredInfo], helpString)) {
+ logBuffer.logUpdateMessage(acquiredInfo, helpString)
+ acquiredInfoToHelpString[acquiredInfo] = helpString
+ }
+ }
+
+ /** Whether the given message should be deferred instead of being shown immediately. */
+ fun shouldDefer(acquiredMsgId: Int): Boolean {
+ return messagesToDefer.contains(acquiredMsgId)
+ }
+
+ /** Adds the acquiredInfo frame to the counts. We account for all frames. */
+ fun processFrame(acquiredInfo: Int) {
+ if (messagesToDefer.isEmpty()) {
+ return
+ }
+
+ totalFrames++
+
+ val newAcquiredInfoCount = acquiredInfoToFrequency.getOrDefault(acquiredInfo, 0) + 1
+ acquiredInfoToFrequency[acquiredInfo] = newAcquiredInfoCount
+ if (
+ messagesToDefer.contains(acquiredInfo) &&
+ (mostFrequentAcquiredInfoToDefer == null ||
+ newAcquiredInfoCount >
+ acquiredInfoToFrequency.getOrDefault(mostFrequentAcquiredInfoToDefer!!, 0))
+ ) {
+ mostFrequentAcquiredInfoToDefer = acquiredInfo
+ }
+
+ logBuffer.logFrameProcessed(
+ acquiredInfo,
+ totalFrames,
+ mostFrequentAcquiredInfoToDefer?.toString()
+ )
+ }
+
+ /**
+ * Get the most frequent deferred message that meets the [threshold] percentage of processed
+ * frames.
+ * @return null if no acquiredInfo have been deferred OR deferred messages didn't meet the
+ * [threshold] percentage.
+ */
+ fun getDeferredMessage(): CharSequence? {
+ mostFrequentAcquiredInfoToDefer?.let {
+ if (acquiredInfoToFrequency.getOrDefault(it, 0) > (threshold * totalFrames)) {
+ return acquiredInfoToHelpString[it]
+ }
+ }
+ return null
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 9281eb8acb56..ad966125b9e8 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -54,13 +54,13 @@ public abstract class UdfpsAnimationView extends FrameLayout {
getDrawable().onSensorRectUpdated(bounds);
}
- void onIlluminationStarting() {
- getDrawable().setIlluminationShowing(true);
+ void onDisplayConfiguring() {
+ getDrawable().setDisplayConfigured(true);
getDrawable().invalidateSelf();
}
- void onIlluminationStopped() {
- getDrawable().setIlluminationShowing(false);
+ void onDisplayUnconfigured() {
+ getDrawable().setDisplayConfigured(false);
getDrawable().invalidateSelf();
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index 742c65c2f854..3ad2bef97ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -31,10 +31,10 @@ import java.io.PrintWriter
/**
* Handles:
* 1. registering for listeners when its view is attached and unregistering on view detached
- * 2. pausing udfps when fingerprintManager may still be running but we temporarily want to hide
+ * 2. pausing UDFPS when FingerprintManager may still be running but we temporarily want to hide
* the affordance. this allows us to fade the view in and out nicely (see shouldPauseAuth)
* 3. sending events to its view including:
- * - illumination events
+ * - enabling and disabling of the UDFPS display mode
* - sensor position changes
* - doze time event
*/
@@ -167,19 +167,20 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
}
/**
- * Udfps has started illuminating and the fingerprint manager is working on authenticating.
+ * The display began transitioning into the UDFPS mode and the fingerprint manager started
+ * authenticating.
*/
- fun onIlluminationStarting() {
- view.onIlluminationStarting()
+ fun onDisplayConfiguring() {
+ view.onDisplayConfiguring()
view.postInvalidate()
}
/**
- * Udfps has stopped illuminating and the fingerprint manager is no longer attempting to
- * authenticate.
+ * The display transitioned away from the UDFPS mode and the fingerprint manager stopped
+ * authenticating.
*/
- fun onIlluminationStopped() {
- view.onIlluminationStopped()
+ fun onDisplayUnconfigured() {
+ view.onDisplayUnconfigured()
view.postInvalidate()
}
@@ -197,4 +198,4 @@ abstract class UdfpsAnimationViewController<T : UdfpsAnimationView>(
* Called when a view should announce an accessibility event.
*/
open fun doAnnounceForAccessibility(str: String) {}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 7f2680b2137d..412dc0577876 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -86,7 +86,7 @@ import kotlin.Unit;
/**
* Shows and hides the under-display fingerprint sensor (UDFPS) overlay, handles UDFPS touch events,
- * and coordinates triggering of the high-brightness mode (HBM).
+ * and toggles the UDFPS display mode.
*
* Note that the current architecture is designed so that a single {@link UdfpsController}
* controls/manages all UDFPS sensors. In other words, a single controller is registered with
@@ -123,7 +123,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
@NonNull private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Nullable private final UdfpsHbmProvider mHbmProvider;
+ @Nullable private final UdfpsDisplayModeProvider mUdfpsDisplayMode;
@NonNull private final ConfigurationController mConfigurationController;
@NonNull private final SystemClock mSystemClock;
@NonNull private final UnlockedScreenOffAnimationController
@@ -135,7 +135,6 @@ public class UdfpsController implements DozeReceiver {
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@VisibleForTesting int mSensorId;
- private boolean mHalControlsIllumination;
@VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams();
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
@Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
@@ -147,10 +146,9 @@ public class UdfpsController implements DozeReceiver {
private int mActivePointerId = -1;
// The timestamp of the most recent touch log.
private long mTouchLogTime;
- // Sensor has a capture (good or bad) for this touch. Do not need to illuminate for this
- // particular touch event anymore. In other words, do not illuminate until user lifts and
- // touches the sensor area again.
- // TODO: We should probably try to make touch/illumination things more of a FSM
+ // Sensor has a capture (good or bad) for this touch. No need to enable the UDFPS display mode
+ // anymore for this particular touch event. In other words, do not enable the UDFPS mode until
+ // the user touches the sensor area again.
private boolean mAcquiredReceived;
// The current request from FingerprintService. Null if no current request.
@@ -211,8 +209,8 @@ public class UdfpsController implements DozeReceiver {
mKeyguardUpdateMonitor, mDialogManager, mDumpManager,
mLockscreenShadeTransitionController, mConfigurationController,
mSystemClock, mKeyguardStateController,
- mUnlockedScreenOffAnimationController, mHalControlsIllumination,
- mHbmProvider, requestId, reason, callback,
+ mUnlockedScreenOffAnimationController,
+ mUdfpsDisplayMode, requestId, reason, callback,
(view, event, fromUdfpsView) -> onTouch(requestId, event,
fromUdfpsView), mActivityLaunchAnimator)));
}
@@ -236,7 +234,7 @@ public class UdfpsController implements DozeReceiver {
int sensorId,
@BiometricFingerprintConstants.FingerprintAcquired int acquiredInfo
) {
- if (BiometricFingerprintConstants.shouldTurnOffHbm(acquiredInfo)) {
+ if (BiometricFingerprintConstants.shouldDisableUdfpsDisplayMode(acquiredInfo)) {
boolean acquiredGood = acquiredInfo == FINGERPRINT_ACQUIRED_GOOD;
mFgExecutor.execute(() -> {
if (mOverlay == null) {
@@ -247,7 +245,7 @@ public class UdfpsController implements DozeReceiver {
mAcquiredReceived = true;
final UdfpsView view = mOverlay.getOverlayView();
if (view != null) {
- view.stopIllumination(); // turn off HBM
+ view.unconfigureDisplay();
}
if (acquiredGood) {
mOverlay.onAcquiredGood();
@@ -292,7 +290,7 @@ public class UdfpsController implements DozeReceiver {
/**
* Updates the overlay parameters and reconstructs or redraws the overlay, if necessary.
*
- * @param sensorId sensor for which the overlay is getting updated.
+ * @param sensorId sensor for which the overlay is getting updated.
* @param overlayParams See {@link UdfpsOverlayParams}.
*/
public void updateOverlayParams(int sensorId, @NonNull UdfpsOverlayParams overlayParams) {
@@ -321,11 +319,6 @@ public class UdfpsController implements DozeReceiver {
mAuthControllerUpdateUdfpsLocation = r;
}
- // TODO(b/229290039): UDFPS controller should manage its properties on its own. Remove this.
- public void setHalControlsIllumination(boolean value) {
- mHalControlsIllumination = value;
- }
-
/**
* Calculate the pointer speed given a velocity tracker and the pointer id.
* This assumes that the velocity tracker has already been passed all relevant motion events.
@@ -349,8 +342,11 @@ public class UdfpsController implements DozeReceiver {
if (mOverlay != null
&& mOverlay.getRequestReason() != REASON_AUTH_KEYGUARD
&& Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
- Log.d(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received, mRequestReason: "
- + mOverlay.getRequestReason());
+ String reason = intent.getStringExtra("reason");
+ reason = (reason != null) ? reason : "unknown";
+ Log.d(TAG, "ACTION_CLOSE_SYSTEM_DIALOGS received, reason: " + reason
+ + ", mRequestReason: " + mOverlay.getRequestReason());
+
mOverlay.cancel();
hideUdfpsOverlay();
}
@@ -369,8 +365,8 @@ public class UdfpsController implements DozeReceiver {
}
/**
- * @param x coordinate
- * @param y coordinate
+ * @param x coordinate
+ * @param y coordinate
* @param relativeToUdfpsView true if the coordinates are relative to the udfps view; else,
* calculate from the display dimensions in portrait orientation
*/
@@ -423,7 +419,7 @@ public class UdfpsController implements DozeReceiver {
}
final UdfpsView udfpsView = mOverlay.getOverlayView();
- final boolean isIlluminationRequested = udfpsView.isIlluminationRequested();
+ final boolean isDisplayConfigured = udfpsView.isDisplayConfigured();
boolean handled = false;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_OUTSIDE:
@@ -507,7 +503,7 @@ public class UdfpsController implements DozeReceiver {
"minor: %.1f, major: %.1f, v: %.1f, exceedsVelocityThreshold: %b",
minor, major, v, exceedsVelocityThreshold);
final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
- if (!isIlluminationRequested && !mAcquiredReceived
+ if (!isDisplayConfigured && !mAcquiredReceived
&& !exceedsVelocityThreshold) {
final float scale = mOverlayParams.getScaleFactor();
@@ -598,7 +594,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull VibratorHelper vibrator,
@NonNull UdfpsHapticsSimulator udfpsHapticsSimulator,
@NonNull UdfpsShell udfpsShell,
- @NonNull Optional<UdfpsHbmProvider> hbmProvider,
+ @NonNull Optional<UdfpsDisplayModeProvider> udfpsDisplayMode,
@NonNull KeyguardStateController keyguardStateController,
@NonNull DisplayManager displayManager,
@Main Handler mainHandler,
@@ -630,7 +626,7 @@ public class UdfpsController implements DozeReceiver {
mPowerManager = powerManager;
mAccessibilityManager = accessibilityManager;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
- mHbmProvider = hbmProvider.orElse(null);
+ mUdfpsDisplayMode = udfpsDisplayMode.orElse(null);
screenLifecycle.addObserver(mScreenObserver);
mScreenOn = screenLifecycle.getScreenState() == ScreenLifecycle.SCREEN_ON;
mConfigurationController = configurationController;
@@ -804,15 +800,14 @@ public class UdfpsController implements DozeReceiver {
}
/**
- * Cancel updfs scan affordances - ability to hide the HbmSurfaceView (white circle) before
- * user explicitly lifts their finger. Generally, this should be called whenever udfps fails
- * or errors.
+ * Cancel UDFPS affordances - ability to hide the UDFPS overlay before the user explicitly
+ * lifts their finger. Generally, this should be called on errors in the authentication flow.
*
* The sensor that triggers an AOD fingerprint interrupt (see onAodInterrupt) doesn't give
* ACTION_UP/ACTION_CANCEL events, so and AOD interrupt scan needs to be cancelled manually.
* This should be called when authentication either succeeds or fails. Failing to cancel the
- * scan will leave the screen in high brightness mode and will show the HbmSurfaceView until
- * the user lifts their finger.
+ * scan will leave the display in the UDFPS mode until the user lifts their finger. On optical
+ * sensors, this can result in illumination persisting for longer than necessary.
*/
void onCancelUdfps() {
if (mOverlay != null && mOverlay.getOverlayView() != null) {
@@ -874,7 +869,7 @@ public class UdfpsController implements DozeReceiver {
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
final UdfpsView view = mOverlay.getOverlayView();
if (view != null) {
- view.startIllumination(() -> {
+ view.configureDisplay(() -> {
if (mAlternateTouchProvider != null) {
mBiometricExecutor.execute(() -> {
mAlternateTouchProvider.onUiReady();
@@ -914,9 +909,15 @@ public class UdfpsController implements DozeReceiver {
}
}
mOnFingerDown = false;
- if (view.isIlluminationRequested()) {
- view.stopIllumination();
+ if (view.isDisplayConfigured()) {
+ view.unconfigureDisplay();
}
+
+ if (mCancelAodTimeoutAction != null) {
+ mCancelAodTimeoutAction.run();
+ mCancelAodTimeoutAction = null;
+ }
+ mIsAodInterruptActive = false;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index ec720579fbee..1c62f8a4e508 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -77,8 +77,7 @@ class UdfpsControllerOverlay(
private val systemClock: SystemClock,
private val keyguardStateController: KeyguardStateController,
private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
- private val halControlsIllumination: Boolean,
- private var hbmProvider: UdfpsHbmProvider,
+ private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
val requestId: Long,
@ShowReason val requestReason: Int,
private val controllerCallback: IUdfpsOverlayControllerCallback,
@@ -102,8 +101,8 @@ class UdfpsControllerOverlay(
fitInsetsTypes = 0
gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
- flags =
- (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
+ flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or
+ WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
// Avoid announcing window title.
accessibilityTitle = " "
@@ -140,8 +139,7 @@ class UdfpsControllerOverlay(
R.layout.udfps_view, null, false
) as UdfpsView).apply {
overlayParams = params
- halControlsIllumination = this@UdfpsControllerOverlay.halControlsIllumination
- setHbmProvider(hbmProvider)
+ setUdfpsDisplayModeProvider(udfpsDisplayModeProvider)
val animation = inflateUdfpsAnimation(this, controller)
if (animation != null) {
animation.init()
@@ -250,8 +248,8 @@ class UdfpsControllerOverlay(
val wasShowing = isShowing
overlayView?.apply {
- if (isIlluminationRequested) {
- stopIllumination()
+ if (isDisplayConfigured) {
+ unconfigureDisplay()
}
windowManager.removeView(this)
setOnTouchListener(null)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java
new file mode 100644
index 000000000000..c6957acf1dc2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDisplayModeProvider.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import android.annotation.Nullable;
+
+/**
+ * Interface for toggling the optimal display mode for the under-display fingerprint sensor
+ * (UDFPS). For example, the implementation might change the refresh rate and activate a
+ * high-brightness mode.
+ */
+public interface UdfpsDisplayModeProvider {
+
+ /**
+ * Enables the optimal display mode for UDFPS. The mode will persist until
+ * {@link #disable(Runnable)} is called.
+ *
+ * This call must be made from the UI thread. The callback, if provided, will also be invoked
+ * from the UI thread.
+ *
+ * @param onEnabled A runnable that will be executed once the mode is enabled.
+ */
+ void enable(@Nullable Runnable onEnabled);
+
+ /**
+ * Disables the mode that was enabled by {@link #enable(Runnable)}.
+ *
+ * The call must be made from the UI thread. The callback, if provided, will also be invoked
+ * from the UI thread.
+ *
+ * @param onDisabled A runnable that will be executed once mode is disabled.
+ */
+ void disable(@Nullable Runnable onDisabled);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
index ee112b47e243..511b4e34fa0e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.kt
@@ -51,7 +51,7 @@ abstract class UdfpsDrawable(
invalidateSelf()
}
- var isIlluminationShowing: Boolean = false
+ var isDisplayConfigured: Boolean = false
set(showing) {
if (field == showing) {
return
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 1317492aefac..1e359584ceec 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -197,7 +197,7 @@ public class UdfpsEnrollDrawable extends UdfpsDrawable {
@Override
public void draw(@NonNull Canvas canvas) {
- if (isIlluminationShowing()) {
+ if (isDisplayConfigured()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
index 1afa36bd5000..9f6b6d7472f9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpDrawable.kt
@@ -23,7 +23,7 @@ import android.graphics.Canvas
*/
class UdfpsFpDrawable(context: Context) : UdfpsDrawable(context) {
override fun draw(canvas: Canvas) {
- if (isIlluminationShowing) {
+ if (isDisplayConfigured) {
return
}
fingerprintDrawable.draw(canvas)
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
deleted file mode 100644
index f26dd5f57061..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsHbmProvider.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.annotation.Nullable;
-
-/**
- * Interface for controlling the high-brightness mode (HBM). UdfpsView can use this callback to
- * enable the HBM while showing the fingerprint illumination, and to disable the HBM after the
- * illumination is no longer necessary.
- */
-public interface UdfpsHbmProvider {
-
- /**
- * UdfpsView will call this to enable the HBM when the fingerprint illumination is needed.
- *
- * This method is a no-op when some type of HBM is already enabled.
- *
- * This method must be called from the UI thread. The callback, if provided, will also be
- * invoked from the UI thread.
- *
- * @param onHbmEnabled A runnable that will be executed once HBM is enabled.
- *
- * TODO(b/231335067): enableHbm with halControlsIllumination=true shouldn't make sense.
- * This only makes sense now because vendor code may rely on the side effects of enableHbm.
- */
- void enableHbm(boolean halControlsIllumination, @Nullable Runnable onHbmEnabled);
-
- /**
- * UdfpsView will call this to disable HBM when illumination is no longer needed.
- *
- * This method will disable HBM if HBM is enabled. Otherwise, if HBM is already disabled,
- * this method is a no-op.
- *
- * The call must be made from the UI thread. The callback, if provided, will also be invoked
- * from the UI thread.
- *
- * @param onHbmDisabled A runnable that will be executed once HBM is disabled.
- */
- void disableHbm(@Nullable Runnable onHbmDisabled);
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
deleted file mode 100644
index f85e9361ecf2..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsIlluminator.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import android.annotation.Nullable;
-
-/**
- * Interface that should be implemented by UI's that need to coordinate user touches,
- * views/animations, and modules that start/stop display illumination.
- */
-interface UdfpsIlluminator {
- /**
- * @param hbmProvider Invoked when HBM should be enabled or disabled.
- */
- void setHbmProvider(@Nullable UdfpsHbmProvider hbmProvider);
-
- /**
- * Invoked when illumination should start.
- * @param onIlluminatedRunnable Invoked when the display has been illuminated.
- */
- void startIllumination(@Nullable Runnable onIlluminatedRunnable);
-
- /**
- * Invoked when illumination should end.
- */
- void stopIllumination();
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index f28fedb9155b..bc274a0af95f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -101,11 +101,11 @@ public class UdfpsKeyguardView extends UdfpsAnimationView {
}
@Override
- void onIlluminationStarting() {
+ void onDisplayConfiguring() {
}
@Override
- void onIlluminationStopped() {
+ void onDisplayUnconfigured() {
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
index 245c2252d57b..a15456d46897 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.kt
@@ -36,12 +36,12 @@ private const val TAG = "UdfpsView"
class UdfpsView(
context: Context,
attrs: AttributeSet?
-) : FrameLayout(context, attrs), DozeReceiver, UdfpsIlluminator {
+) : FrameLayout(context, attrs), DozeReceiver {
// sensorRect may be bigger than the sensor. True sensor dimensions are defined in
// overlayParams.sensorBounds
private val sensorRect = RectF()
- private var hbmProvider: UdfpsHbmProvider? = null
+ private var mUdfpsDisplayMode: UdfpsDisplayModeProvider? = null
private val debugTextPaint = Paint().apply {
isAntiAlias = true
color = Color.BLUE
@@ -56,19 +56,12 @@ class UdfpsView(
a.getFloat(R.styleable.UdfpsView_sensorTouchAreaCoefficient, 0f)
}
- private val onIlluminatedDelayMs = context.resources.getInteger(
- com.android.internal.R.integer.config_udfps_illumination_transition_ms
- ).toLong()
-
/** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
var animationViewController: UdfpsAnimationViewController<*>? = null
/** Parameters that affect the position and size of the overlay. */
var overlayParams = UdfpsOverlayParams()
- /** Whether the HAL is responsible for enabling and disabling of LHBM. */
- var halControlsIllumination: Boolean = true
-
/** Debug message. */
var debugMessage: String? = null
set(value) {
@@ -76,12 +69,12 @@ class UdfpsView(
postInvalidate()
}
- /** When [startIllumination] has been called but not stopped via [stopIllumination]. */
- var isIlluminationRequested: Boolean = false
+ /** True after the call to [configureDisplay] and before the call to [unconfigureDisplay]. */
+ var isDisplayConfigured: Boolean = false
private set
- override fun setHbmProvider(provider: UdfpsHbmProvider?) {
- hbmProvider = provider
+ fun setUdfpsDisplayModeProvider(udfpsDisplayModeProvider: UdfpsDisplayModeProvider?) {
+ mUdfpsDisplayMode = udfpsDisplayModeProvider
}
// Don't propagate any touch events to the child views.
@@ -124,7 +117,7 @@ class UdfpsView(
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
- if (!isIlluminationRequested) {
+ if (!isDisplayConfigured) {
if (!debugMessage.isNullOrEmpty()) {
canvas.drawText(debugMessage!!, 0f, 160f, debugTextPaint)
}
@@ -147,36 +140,15 @@ class UdfpsView(
!(animationViewController?.shouldPauseAuth() ?: false)
}
- /**
- * Start and run [onIlluminatedRunnable] when the first illumination frame reaches the panel.
- */
- override fun startIllumination(onIlluminatedRunnable: Runnable?) {
- isIlluminationRequested = true
- animationViewController?.onIlluminationStarting()
- doIlluminate(onIlluminatedRunnable)
- }
-
- private fun doIlluminate(onIlluminatedRunnable: Runnable?) {
- // TODO(b/231335067): enableHbm with halControlsIllumination=true shouldn't make sense.
- // This only makes sense now because vendor code may rely on the side effects of enableHbm.
- hbmProvider?.enableHbm(halControlsIllumination) {
- if (onIlluminatedRunnable != null) {
- if (halControlsIllumination) {
- onIlluminatedRunnable.run()
- } else {
- // No framework API can reliably tell when a frame reaches the panel. A timeout
- // is the safest solution.
- postDelayed(onIlluminatedRunnable, onIlluminatedDelayMs)
- }
- } else {
- Log.w(TAG, "doIlluminate | onIlluminatedRunnable is null")
- }
- }
+ fun configureDisplay(onDisplayConfigured: Runnable) {
+ isDisplayConfigured = true
+ animationViewController?.onDisplayConfiguring()
+ mUdfpsDisplayMode?.enable(onDisplayConfigured)
}
- override fun stopIllumination() {
- isIlluminationRequested = false
- animationViewController?.onIlluminationStopped()
- hbmProvider?.disableHbm(null /* onHbmDisabled */)
+ fun unconfigureDisplay() {
+ isDisplayConfigured = false
+ animationViewController?.onDisplayUnconfigured()
+ mUdfpsDisplayMode?.disable(null /* onDisabled */)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
new file mode 100644
index 000000000000..96af42bfda22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.bluetooth
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+import com.android.systemui.log.dagger.BluetoothLog
+import javax.inject.Inject
+
+/** Helper class for logging bluetooth events. */
+@SysUISingleton
+class BluetoothLogger @Inject constructor(@BluetoothLog private val logBuffer: LogBuffer) {
+ fun logActiveDeviceChanged(address: String?, profileId: Int) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = address
+ int1 = profileId
+ },
+ { "ActiveDeviceChanged. address=$str1 profileId=$int1" }
+ )
+
+ fun logDeviceConnectionStateChanged(address: String?, state: String) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = address
+ str2 = state
+ },
+ { "DeviceConnectionStateChanged. address=$str1 state=$str2" }
+ )
+
+ fun logAclConnectionStateChanged(address: String, state: String) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = address
+ str2 = state
+ },
+ { "AclConnectionStateChanged. address=$str1 state=$str2" }
+ )
+
+ fun logProfileConnectionStateChanged(address: String?, state: String, profileId: Int) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = address
+ str2 = state
+ int1 = profileId
+ },
+ { "ProfileConnectionStateChanged. address=$str1 state=$str2 profileId=$int1" }
+ )
+
+ fun logStateChange(state: String) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = state },
+ { "BluetoothStateChanged. state=$str1" }
+ )
+
+ fun logBondStateChange(address: String, state: Int) =
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = address
+ int1 = state
+ },
+ { "DeviceBondStateChanged. address=$str1 state=$int1" }
+ )
+
+ fun logDeviceAdded(address: String) =
+ logBuffer.log(TAG, LogLevel.DEBUG, { str1 = address }, { "DeviceAdded. address=$str1" })
+
+ fun logDeviceDeleted(address: String) =
+ logBuffer.log(TAG, LogLevel.DEBUG, { str1 = address }, { "DeviceDeleted. address=$str1" })
+
+ fun logDeviceAttributesChanged() =
+ logBuffer.log(TAG, LogLevel.DEBUG, {}, { "DeviceAttributesChanged." })
+}
+
+private const val TAG = "BluetoothLog"
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index 31a2134851a2..d53e56f7b852 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -220,7 +220,7 @@ public class BrightLineFalsingManager implements FalsingManager {
return r;
}).collect(Collectors.toList());
- logDebug("False Gesture: " + localResult[0]);
+ logDebug("False Gesture (type: " + interactionType + "): " + localResult[0]);
return localResult[0];
}
@@ -291,7 +291,7 @@ public class BrightLineFalsingManager implements FalsingManager {
FalsingClassifier.Result.falsed(
0, getClass().getSimpleName(), "bad history"));
logDebug("False Single Tap: true (bad history)");
- mFalsingTapListeners.forEach(FalsingTapListener::onDoubleTapRequired);
+ mFalsingTapListeners.forEach(FalsingTapListener::onAdditionalTapRequired);
return true;
} else {
mPriorResults = getPassedResult(0.1);
@@ -321,7 +321,7 @@ public class BrightLineFalsingManager implements FalsingManager {
mHistoryTracker.falseBelief(),
mHistoryTracker.falseConfidence());
mPriorResults = Collections.singleton(result);
- logDebug("False Double Tap: " + result.isFalse());
+ logDebug("False Double Tap: " + result.isFalse() + " reason=" + result.getReason());
return result.isFalse();
}
@@ -454,6 +454,12 @@ public class BrightLineFalsingManager implements FalsingManager {
}
}
+ static void logVerbose(String msg) {
+ if (DEBUG) {
+ Log.v(TAG, msg);
+ }
+ }
+
static void logInfo(String msg) {
Log.i(TAG, msg);
RECENT_INFO_LOG.add(msg);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index f18413be0134..c2922968b58e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -42,8 +42,9 @@ public abstract class Classifier {
public static final int QS_COLLAPSE = 12;
public static final int UDFPS_AUTHENTICATION = 13;
public static final int LOCK_ICON = 14;
- public static final int QS_SWIPE = 15;
+ public static final int QS_SWIPE_SIDE = 15;
public static final int BACK_GESTURE = 16;
+ public static final int QS_SWIPE_NESTED = 17;
@IntDef({
QUICK_SETTINGS,
@@ -62,7 +63,8 @@ public abstract class Classifier {
BRIGHTNESS_SLIDER,
UDFPS_AUTHENTICATION,
LOCK_ICON,
- QS_SWIPE,
+ QS_SWIPE_SIDE,
+ QS_SWIPE_NESTED,
BACK_GESTURE
})
@Retention(RetentionPolicy.SOURCE)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index d0fe1c37d4fa..5e4f149d3ca3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -24,6 +24,7 @@ import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHT
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DISTANCE_VERTICAL_SWIPE_THRESHOLD_IN;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED;
import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
import android.provider.DeviceConfig;
@@ -156,7 +157,8 @@ class DistanceClassifier extends FalsingClassifier {
|| interactionType == QS_COLLAPSE
|| interactionType == Classifier.UDFPS_AUTHENTICATION
|| interactionType == Classifier.LOCK_ICON
- || interactionType == Classifier.QS_SWIPE) {
+ || interactionType == Classifier.QS_SWIPE_SIDE
+ || interactionType == QS_SWIPE_NESTED) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
index d75752841023..d18d62f025fe 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingClassifier.java
@@ -148,6 +148,11 @@ public abstract class FalsingClassifier {
}
/** */
+ public static void logVerbose(String msg) {
+ BrightLineFalsingManager.logVerbose(msg);
+ }
+
+ /** */
public static void logInfo(String msg) {
BrightLineFalsingManager.logInfo(msg);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 23d87ff980ca..f5f9655ef24b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -303,9 +303,7 @@ class FalsingCollectorImpl implements FalsingCollector {
@Override
public void onTouchEvent(MotionEvent ev) {
- if (!mKeyguardStateController.isShowing()
- || (mStatusBarStateController.isDozing()
- && !mStatusBarStateController.isPulsing())) {
+ if (!mKeyguardStateController.isShowing()) {
avoidGesture();
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index a3ecb0c0b273..3991a35e958a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -78,10 +78,10 @@ public class FalsingDataProvider {
void onMotionEvent(MotionEvent motionEvent) {
List<MotionEvent> motionEvents = unpackMotionEvent(motionEvent);
- FalsingClassifier.logDebug("Unpacked into: " + motionEvents.size());
+ FalsingClassifier.logVerbose("Unpacked into: " + motionEvents.size());
if (BrightLineFalsingManager.DEBUG) {
for (MotionEvent m : motionEvents) {
- FalsingClassifier.logDebug(
+ FalsingClassifier.logVerbose(
"x,y,t: " + m.getX() + "," + m.getY() + "," + m.getEventTime());
}
}
@@ -92,7 +92,7 @@ public class FalsingDataProvider {
}
mRecentMotionEvents.addAll(motionEvents);
- FalsingClassifier.logDebug("Size: " + mRecentMotionEvents.size());
+ FalsingClassifier.logVerbose("Size: " + mRecentMotionEvents.size());
mMotionEventListeners.forEach(listener -> listener.onMotionEvent(motionEvent));
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
index 32d9ca59951d..07f94e792a93 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ProximityClassifier.java
@@ -19,7 +19,7 @@ package com.android.systemui.classifier;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_PROXIMITY_PERCENT_COVERED_THRESHOLD;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
-import static com.android.systemui.classifier.Classifier.QS_SWIPE;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import android.provider.DeviceConfig;
@@ -119,7 +119,7 @@ class ProximityClassifier extends FalsingClassifier {
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
if (interactionType == QUICK_SETTINGS || interactionType == BRIGHTNESS_SLIDER
- || interactionType == QS_COLLAPSE || interactionType == QS_SWIPE) {
+ || interactionType == QS_COLLAPSE || interactionType == QS_SWIPE_SIDE) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index f040712706cd..776bc88ad6bf 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -24,7 +24,8 @@ import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
-import static com.android.systemui.classifier.Classifier.QS_SWIPE;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
@@ -86,9 +87,12 @@ public class TypeClassifier extends FalsingClassifier {
case QS_COLLAPSE:
wrongDirection = !vertical || !up;
break;
- case QS_SWIPE:
+ case QS_SWIPE_SIDE:
wrongDirection = vertical;
break;
+ case QS_SWIPE_NESTED:
+ wrongDirection = !vertical;
+ break;
default:
wrongDirection = true;
break;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index 40c28fab51b8..de2bdf7ded75 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -137,8 +137,8 @@ class ZigZagClassifier extends FalsingClassifier {
runningAbsDy += Math.abs(point.y - pY);
pX = point.x;
pY = point.y;
- logDebug("(x, y, runningAbsDx, runningAbsDy) - (" + pX + ", " + pY + ", " + runningAbsDx
- + ", " + runningAbsDy + ")");
+ logVerbose("(x, y, runningAbsDx, runningAbsDy) - ("
+ + pX + ", " + pY + ", " + runningAbsDx + ", " + runningAbsDy + ")");
}
float devianceX = runningAbsDx - actualDx;
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index c21e36ab6ecc..7e499ebdf691 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -119,15 +119,12 @@ import java.util.ArrayList;
*/
public class ClipboardOverlayController {
private static final String TAG = "ClipboardOverlayCtrlr";
- private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
/** Constants for screenshot/copy deconflicting */
public static final String SCREENSHOT_ACTION = "com.android.systemui.SCREENSHOT";
public static final String SELF_PERMISSION = "com.android.systemui.permission.SELF";
public static final String COPY_OVERLAY_ACTION = "com.android.systemui.COPY";
- private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard";
-
private static final int CLIPBOARD_DEFAULT_TIMEOUT_MILLIS = 6000;
private static final int SWIPE_PADDING_DP = 12; // extra padding around views to allow swipe
private static final int FONT_SEARCH_STEP_PX = 4;
@@ -383,7 +380,7 @@ public class ClipboardOverlayController {
mTextPreview);
accessibilityAnnouncement = mContext.getString(R.string.clipboard_content_copied);
}
- Intent remoteCopyIntent = getRemoteCopyIntent(clipData);
+ Intent remoteCopyIntent = IntentCreator.getRemoteCopyIntent(clipData, mContext);
// Only show remote copy if it's available.
PackageManager packageManager = mContext.getPackageManager();
if (packageManager.resolveActivity(
@@ -500,41 +497,19 @@ public class ClipboardOverlayController {
private void editImage(Uri uri) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- String editorPackage = mContext.getString(R.string.config_screenshotEditor);
- Intent editIntent = new Intent(Intent.ACTION_EDIT);
- if (!TextUtils.isEmpty(editorPackage)) {
- editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
- }
- editIntent.setDataAndType(uri, "image/*");
- editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- editIntent.putExtra(EXTRA_EDIT_SOURCE_CLIPBOARD, true);
- mContext.startActivity(editIntent);
+ mContext.startActivity(IntentCreator.getImageEditIntent(uri, mContext));
animateOut();
}
private void editText() {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_EDIT_TAPPED);
- Intent editIntent = new Intent(mContext, EditTextActivity.class);
- editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mContext.startActivity(editIntent);
+ mContext.startActivity(IntentCreator.getTextEditorIntent(mContext));
animateOut();
}
private void shareContent(ClipData clip) {
mClipboardLogger.logSessionComplete(CLIPBOARD_OVERLAY_SHARE_TAPPED);
- Intent shareIntent = new Intent(Intent.ACTION_SEND);
- shareIntent.setDataAndType(
- clip.getItemAt(0).getUri(), clip.getDescription().getMimeType(0));
- shareIntent.putExtra(Intent.EXTRA_TEXT, clip.getItemAt(0).getText().toString());
- if (clip.getItemAt(0).getUri() != null) {
- shareIntent.putExtra(Intent.EXTRA_STREAM, clip.getItemAt(0).getUri());
- shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- Intent chooserIntent = Intent.createChooser(shareIntent, null)
- .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
- .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- mContext.startActivity(chooserIntent);
+ mContext.startActivity(IntentCreator.getShareIntent(clip, mContext));
animateOut();
}
@@ -667,20 +642,6 @@ public class ClipboardOverlayController {
mContext.getString(R.string.clipboard_edit), null);
}
- private Intent getRemoteCopyIntent(ClipData clipData) {
- Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
-
- String remoteCopyPackage = mContext.getString(R.string.config_remoteCopyPackage);
- if (!TextUtils.isEmpty(remoteCopyPackage)) {
- nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
- }
-
- nearbyIntent.setClipData(clipData);
- nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- return nearbyIntent;
- }
-
private void animateIn() {
if (mAccessibilityManager.isEnabled()) {
mDismissButton.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
new file mode 100644
index 000000000000..3d5e601f18f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/IntentCreator.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.clipboardoverlay;
+
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.text.TextUtils;
+
+import com.android.systemui.R;
+
+class IntentCreator {
+ private static final String EXTRA_EDIT_SOURCE_CLIPBOARD = "edit_source_clipboard";
+ private static final String REMOTE_COPY_ACTION = "android.intent.action.REMOTE_COPY";
+
+ static Intent getTextEditorIntent(Context context) {
+ Intent intent = new Intent(context, EditTextActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ return intent;
+ }
+
+ static Intent getShareIntent(ClipData clipData, Context context) {
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
+
+ // From the ACTION_SEND docs:
+ // "If using EXTRA_TEXT, the MIME type should be "text/plain"; otherwise it should be the
+ // MIME type of the data in EXTRA_STREAM"
+ if (clipData.getItemAt(0).getUri() != null) {
+ shareIntent.setDataAndType(
+ clipData.getItemAt(0).getUri(), clipData.getDescription().getMimeType(0));
+ shareIntent.putExtra(Intent.EXTRA_STREAM, clipData.getItemAt(0).getUri());
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ } else {
+ shareIntent.putExtra(Intent.EXTRA_TEXT, clipData.getItemAt(0).coerceToText(context));
+ shareIntent.setType("text/plain");
+ }
+ Intent chooserIntent = Intent.createChooser(shareIntent, null)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK)
+ .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+ return chooserIntent;
+ }
+
+ static Intent getImageEditIntent(Uri uri, Context context) {
+ String editorPackage = context.getString(R.string.config_screenshotEditor);
+ Intent editIntent = new Intent(Intent.ACTION_EDIT);
+ if (!TextUtils.isEmpty(editorPackage)) {
+ editIntent.setComponent(ComponentName.unflattenFromString(editorPackage));
+ }
+ editIntent.setDataAndType(uri, "image/*");
+ editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ editIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ editIntent.putExtra(EXTRA_EDIT_SOURCE_CLIPBOARD, true);
+ return editIntent;
+ }
+
+ static Intent getRemoteCopyIntent(ClipData clipData, Context context) {
+ Intent nearbyIntent = new Intent(REMOTE_COPY_ACTION);
+
+ String remoteCopyPackage = context.getString(R.string.config_remoteCopyPackage);
+ if (!TextUtils.isEmpty(remoteCopyPackage)) {
+ nearbyIntent.setComponent(ComponentName.unflattenFromString(remoteCopyPackage));
+ }
+
+ nearbyIntent.setClipData(clipData);
+ nearbyIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ nearbyIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ return nearbyIntent;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/drawable/CircularDrawable.kt b/packages/SystemUI/src/com/android/systemui/common/ui/drawable/CircularDrawable.kt
new file mode 100644
index 000000000000..ec71c3824156
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/drawable/CircularDrawable.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.common.ui.drawable
+
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import kotlin.math.min
+
+/** Renders the wrapped [Drawable] as a circle. */
+class CircularDrawable(
+ drawable: Drawable,
+) : DrawableWrapper(drawable) {
+ private val path: Path by lazy { Path() }
+
+ override fun onBoundsChange(bounds: Rect) {
+ super.onBoundsChange(bounds)
+ updateClipPath()
+ }
+
+ override fun draw(canvas: Canvas) {
+ canvas.save()
+ canvas.clipPath(path)
+ drawable?.draw(canvas)
+ canvas.restore()
+ }
+
+ private fun updateClipPath() {
+ path.reset()
+ path.addCircle(
+ bounds.centerX().toFloat(),
+ bounds.centerY().toFloat(),
+ min(bounds.width(), bounds.height()) / 2f,
+ Path.Direction.CW
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index b8a00133c728..1f7021e514e5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -18,6 +18,7 @@ package com.android.systemui.controls.ui
import android.annotation.AnyThread
import android.annotation.MainThread
+import android.app.Activity
import android.app.AlertDialog
import android.app.Dialog
import android.app.PendingIntent
@@ -119,8 +120,16 @@ class ControlActionCoordinatorImpl @Inject constructor(
}
override fun closeDialogs() {
- dialog?.dismiss()
- dialog = null
+ val isActivityFinishing =
+ (activityContext as? Activity)?.let { it.isFinishing || it.isDestroyed }
+ if (isActivityFinishing == true) {
+ dialog = null
+ return
+ }
+ if (dialog?.isShowing == true) {
+ dialog?.dismiss()
+ dialog = null
+ }
}
override fun toggle(cvh: ControlViewHolder, templateId: String, isChecked: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
index fbfc94af683e..a99669970a34 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/ReferenceSystemUIModule.java
@@ -35,6 +35,7 @@ import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.media.dagger.MediaModule;
import com.android.systemui.navigationbar.gestural.GestureModule;
import com.android.systemui.plugins.qs.QSFactory;
@@ -126,6 +127,7 @@ public abstract class ReferenceSystemUIModule {
PowerManager powerManager,
BroadcastDispatcher broadcastDispatcher,
DemoModeController demoModeController,
+ DumpManager dumpManager,
@Main Handler mainHandler,
@Background Handler bgHandler) {
BatteryController bC = new BatteryControllerImpl(
@@ -134,6 +136,7 @@ public abstract class ReferenceSystemUIModule {
powerManager,
broadcastDispatcher,
demoModeController,
+ dumpManager,
mainHandler,
bgHandler);
bC.init();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
index 029cabb0bc0b..0d06c513d248 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SysUIComponent.java
@@ -40,7 +40,9 @@ import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
+import com.android.wm.shell.floating.FloatingTasks;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
@@ -109,6 +111,12 @@ public interface SysUIComponent {
@BindsInstance
Builder setBackAnimation(Optional<BackAnimation> b);
+ @BindsInstance
+ Builder setFloatingTasks(Optional<FloatingTasks> f);
+
+ @BindsInstance
+ Builder setDesktopMode(Optional<DesktopMode> d);
+
SysUIComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 318529b289ec..443d2774f0e0 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -30,7 +30,7 @@ import com.android.systemui.BootCompleteCacheImpl;
import com.android.systemui.appops.dagger.AppOpsModule;
import com.android.systemui.assist.AssistModule;
import com.android.systemui.biometrics.AlternateUdfpsTouchProvider;
-import com.android.systemui.biometrics.UdfpsHbmProvider;
+import com.android.systemui.biometrics.UdfpsDisplayModeProvider;
import com.android.systemui.biometrics.dagger.BiometricsModule;
import com.android.systemui.classifier.FalsingModule;
import com.android.systemui.controls.dagger.ControlsModule;
@@ -41,6 +41,7 @@ import com.android.systemui.dreams.dagger.DreamModule;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.keyguard.data.BouncerViewModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.media.dagger.MediaProjectionModule;
import com.android.systemui.model.SysUiState;
@@ -116,6 +117,7 @@ import dagger.Provides;
AppOpsModule.class,
AssistModule.class,
BiometricsModule.class,
+ BouncerViewModule.class,
ClockModule.class,
CoroutinesModule.class,
DreamModule.class,
@@ -197,7 +199,7 @@ public abstract class SystemUIModule {
abstract CentralSurfaces optionalCentralSurfaces();
@BindsOptionalOf
- abstract UdfpsHbmProvider optionalUdfpsHbmProvider();
+ abstract UdfpsDisplayModeProvider optionalUdfpsDisplayModeProvider();
@BindsOptionalOf
abstract AlternateUdfpsTouchProvider optionalUdfpsTouchProvider();
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
index b6923a867507..096f96949382 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/WMComponent.java
@@ -23,8 +23,6 @@ import androidx.annotation.Nullable;
import com.android.systemui.SystemUIInitializerFactory;
import com.android.systemui.tv.TvWMComponent;
-import com.android.wm.shell.sysui.ShellCommandHandler;
-import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.TaskViewFactory;
import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.bubbles.Bubbles;
@@ -32,7 +30,9 @@ import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.dagger.TvWMShellModule;
import com.android.wm.shell.dagger.WMShellModule;
import com.android.wm.shell.dagger.WMSingleton;
+import com.android.wm.shell.desktopmode.DesktopMode;
import com.android.wm.shell.displayareahelper.DisplayAreaHelper;
+import com.android.wm.shell.floating.FloatingTasks;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.recents.RecentTasks;
@@ -110,4 +110,13 @@ public interface WMComponent {
@WMSingleton
Optional<BackAnimation> getBackAnimation();
+
+ @WMSingleton
+ Optional<FloatingTasks> getFloatingTasks();
+
+ /**
+ * Optional {@link DesktopMode} component for interacting with desktop mode.
+ */
+ @WMSingleton
+ Optional<DesktopMode> getDesktopMode();
}
diff --git a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
index a25286438387..8b4aeefb6ed4 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/RoundedCornerResDelegate.kt
@@ -78,23 +78,18 @@ class RoundedCornerResDelegate(
reloadMeasures()
}
- private fun reloadAll(newReloadToken: Int) {
- if (reloadToken == newReloadToken) {
- return
- }
- reloadToken = newReloadToken
- reloadRes()
- reloadMeasures()
- }
-
fun updateDisplayUniqueId(newDisplayUniqueId: String?, newReloadToken: Int?) {
if (displayUniqueId != newDisplayUniqueId) {
displayUniqueId = newDisplayUniqueId
newReloadToken ?.let { reloadToken = it }
reloadRes()
reloadMeasures()
- } else {
- newReloadToken?.let { reloadAll(it) }
+ } else if (newReloadToken != null) {
+ if (reloadToken == newReloadToken) {
+ return
+ }
+ reloadToken = newReloadToken
+ reloadMeasures()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
index d7b7777559da..733a80dd7f69 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java
@@ -35,6 +35,7 @@ import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.dagger.DreamOverlayModule;
+import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -73,6 +74,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
// Main thread handler used to schedule periodic tasks (e.g. burn-in protection updates).
private final Handler mHandler;
private final int mDreamOverlayMaxTranslationY;
+ private final BouncerCallbackInteractor mBouncerCallbackInteractor;
private long mJitterStartTimeMillis;
@@ -131,7 +133,8 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
@Named(DreamOverlayModule.MAX_BURN_IN_OFFSET) int maxBurnInOffset,
@Named(DreamOverlayModule.BURN_IN_PROTECTION_UPDATE_INTERVAL) long
burnInProtectionUpdateInterval,
- @Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter) {
+ @Named(DreamOverlayModule.MILLIS_UNTIL_FULL_JITTER) long millisUntilFullJitter,
+ BouncerCallbackInteractor bouncerCallbackInteractor) {
super(containerView);
mDreamOverlayContentView = contentView;
mStatusBarViewController = statusBarViewController;
@@ -151,6 +154,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
mMaxBurnInOffset = maxBurnInOffset;
mBurnInProtectionUpdateInterval = burnInProtectionUpdateInterval;
mMillisUntilFullJitter = millisUntilFullJitter;
+ mBouncerCallbackInteractor = bouncerCallbackInteractor;
}
@Override
@@ -167,6 +171,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
if (bouncer != null) {
bouncer.addBouncerExpansionCallback(mBouncerExpansionCallback);
}
+ mBouncerCallbackInteractor.addBouncerExpansionCallback(mBouncerExpansionCallback);
}
@Override
@@ -176,6 +181,7 @@ public class DreamOverlayContainerViewController extends ViewController<DreamOve
if (bouncer != null) {
bouncer.removeBouncerExpansionCallback(mBouncerExpansionCallback);
}
+ mBouncerCallbackInteractor.removeBouncerExpansionCallback(mBouncerExpansionCallback);
}
View getContainerView() {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 96f77b3654c5..696fc7254308 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams;
+import android.content.ComponentName;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.util.Log;
@@ -26,11 +27,13 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleRegistry;
import androidx.lifecycle.ViewModelStore;
+import com.android.dream.lowlight.dagger.LowLightDreamModule;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.PhoneWindow;
@@ -44,6 +47,7 @@ import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
import java.util.concurrent.Executor;
import javax.inject.Inject;
+import javax.inject.Named;
/**
* The {@link DreamOverlayService} is responsible for placing an overlay on top of a dream. The
@@ -62,6 +66,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
// content area).
private final DreamOverlayContainerViewController mDreamOverlayContainerViewController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ @Nullable
+ private final ComponentName mLowLightDreamComponent;
private final UiEventLogger mUiEventLogger;
// A reference to the {@link Window} used to hold the dream overlay.
@@ -125,10 +131,13 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
DreamOverlayStateController stateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- UiEventLogger uiEventLogger) {
+ UiEventLogger uiEventLogger,
+ @Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT)
+ ComponentName lowLightDreamComponent) {
mContext = context;
mExecutor = executor;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+ mLowLightDreamComponent = lowLightDreamComponent;
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
mStateController = stateController;
mUiEventLogger = uiEventLogger;
@@ -155,6 +164,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
windowManager.removeView(mWindow.getDecorView());
}
mStateController.setOverlayActive(false);
+ mStateController.setLowLightActive(false);
mDestroyed = true;
super.onDestroy();
}
@@ -163,6 +173,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START);
setCurrentState(Lifecycle.State.STARTED);
+ final ComponentName dreamComponent = getDreamComponent();
+ mStateController.setLowLightActive(
+ dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent));
mExecutor.execute(() -> {
if (mDestroyed) {
// The task could still be executed after the service has been destroyed. Bail if
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index 69e41ba9b284..72feaca59ace 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -50,6 +50,7 @@ public class DreamOverlayStateController implements
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0;
+ public static final int STATE_LOW_LIGHT_ACTIVE = 1 << 1;
private static final int OP_CLEAR_STATE = 1;
private static final int OP_SET_STATE = 2;
@@ -193,6 +194,14 @@ public class DreamOverlayStateController implements
return containsState(STATE_DREAM_OVERLAY_ACTIVE);
}
+ /**
+ * Returns whether low light mode is active.
+ * @return {@code true} if in low light mode, {@code false} otherwise.
+ */
+ public boolean isLowLightActive() {
+ return containsState(STATE_LOW_LIGHT_ACTIVE);
+ }
+
private boolean containsState(int state) {
return (mState & state) != 0;
}
@@ -222,6 +231,14 @@ public class DreamOverlayStateController implements
}
/**
+ * Sets whether low light mode is active.
+ * @param active {@code true} if low light mode is active, {@code false} otherwise.
+ */
+ public void setLowLightActive(boolean active) {
+ modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_LOW_LIGHT_ACTIVE);
+ }
+
+ /**
* Returns the available complication types.
*/
@Complication.ComplicationType
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
index 823255c38a84..f244cb009ba4 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarView.java
@@ -61,6 +61,7 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
private final Map<Integer, View> mStatusIcons = new HashMap<>();
private ViewGroup mSystemStatusViewGroup;
+ private ViewGroup mExtraSystemStatusViewGroup;
public DreamOverlayStatusBarView(Context context) {
this(context, null);
@@ -98,7 +99,8 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
mStatusIcons.put(STATUS_ICON_PRIORITY_MODE_ON,
fetchStatusIconForResId(R.id.dream_overlay_priority_mode));
- mSystemStatusViewGroup = findViewById(R.id.dream_overlay_extra_items);
+ mSystemStatusViewGroup = findViewById(R.id.dream_overlay_system_status);
+ mExtraSystemStatusViewGroup = findViewById(R.id.dream_overlay_extra_items);
}
void showIcon(@StatusIconType int iconType, boolean show, @Nullable String contentDescription) {
@@ -110,11 +112,12 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
icon.setContentDescription(contentDescription);
}
icon.setVisibility(show ? View.VISIBLE : View.GONE);
+ mSystemStatusViewGroup.setVisibility(areAnyStatusIconsVisible() ? View.VISIBLE : View.GONE);
}
void setExtraStatusBarItemViews(List<View> views) {
- removeAllStatusBarItemViews();
- views.forEach(view -> mSystemStatusViewGroup.addView(view));
+ removeAllExtraStatusBarItemViews();
+ views.forEach(view -> mExtraSystemStatusViewGroup.addView(view));
}
private View fetchStatusIconForResId(int resId) {
@@ -122,7 +125,16 @@ public class DreamOverlayStatusBarView extends ConstraintLayout {
return Objects.requireNonNull(statusIcon);
}
- void removeAllStatusBarItemViews() {
- mSystemStatusViewGroup.removeAllViews();
+ void removeAllExtraStatusBarItemViews() {
+ mExtraSystemStatusViewGroup.removeAllViews();
+ }
+
+ private boolean areAnyStatusIconsVisible() {
+ for (int i = 0; i < mSystemStatusViewGroup.getChildCount(); i++) {
+ if (mSystemStatusViewGroup.getChildAt(i).getVisibility() == View.VISIBLE) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 6f505504b186..bb1c4303041a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -20,7 +20,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_HIDING;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
-import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.StatusBarManager;
import android.content.res.Resources;
@@ -36,6 +35,8 @@ import android.text.format.DateFormat;
import android.util.PluralsMessageFormatter;
import android.view.View;
+import androidx.annotation.Nullable;
+
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
@@ -73,6 +74,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
private final Optional<DreamOverlayNotificationCountProvider>
mDreamOverlayNotificationCountProvider;
private final ZenModeController mZenModeController;
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final StatusBarWindowStateController mStatusBarWindowStateController;
private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider;
private final Executor mMainExecutor;
private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems =
@@ -102,6 +105,14 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
}
};
+ private final DreamOverlayStateController.Callback mDreamOverlayStateCallback =
+ new DreamOverlayStateController.Callback() {
+ @Override
+ public void onStateChanged() {
+ updateLowLightState();
+ }
+ };
+
private final IndividualSensorPrivacyController.Callback mSensorCallback =
(sensor, blocked) -> updateMicCameraBlockedStatusIcon();
@@ -140,7 +151,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
Optional<DreamOverlayNotificationCountProvider> dreamOverlayNotificationCountProvider,
ZenModeController zenModeController,
StatusBarWindowStateController statusBarWindowStateController,
- DreamOverlayStatusBarItemsProvider statusBarItemsProvider) {
+ DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
+ DreamOverlayStateController dreamOverlayStateController) {
super(view);
mResources = resources;
mMainExecutor = mainExecutor;
@@ -151,8 +163,10 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mDateFormatUtil = dateFormatUtil;
mSensorPrivacyController = sensorPrivacyController;
mDreamOverlayNotificationCountProvider = dreamOverlayNotificationCountProvider;
+ mStatusBarWindowStateController = statusBarWindowStateController;
mStatusBarItemsProvider = statusBarItemsProvider;
mZenModeController = zenModeController;
+ mDreamOverlayStateController = dreamOverlayStateController;
// Register to receive show/hide updates for the system status bar. Our custom status bar
// needs to hide when the system status bar is showing to ovoid overlapping status bars.
@@ -180,6 +194,9 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mStatusBarItemsProvider.addCallback(mStatusBarItemsProviderCallback);
+ mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
+ updateLowLightState();
+
mTouchInsetSession.addViewToTracking(mView);
}
@@ -192,7 +209,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mDreamOverlayNotificationCountProvider.ifPresent(
provider -> provider.removeCallback(mNotificationCountCallback));
mStatusBarItemsProvider.removeCallback(mStatusBarItemsProviderCallback);
- mView.removeAllStatusBarItemViews();
+ mView.removeAllExtraStatusBarItemViews();
+ mDreamOverlayStateController.removeCallback(mDreamOverlayStateCallback);
mTouchInsetSession.clear();
mIsAttached = false;
@@ -217,6 +235,15 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
hasAlarm ? buildAlarmContentDescription(alarm) : null);
}
+ private void updateLowLightState() {
+ int visibility = View.VISIBLE;
+ if (mDreamOverlayStateController.isLowLightActive()
+ || mStatusBarWindowStateController.windowIsShowing()) {
+ visibility = View.INVISIBLE;
+ }
+ mView.setVisibility(visibility);
+ }
+
private String buildAlarmContentDescription(AlarmManager.AlarmClockInfo alarm) {
final String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma";
final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
@@ -272,7 +299,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
private void onSystemStatusBarStateChanged(@StatusBarManager.WindowVisibleState int state) {
mMainExecutor.execute(() -> {
- if (!mIsAttached) {
+ if (!mIsAttached || mDreamOverlayStateController.isLowLightActive()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java
deleted file mode 100644
index 653f4dc66200..000000000000
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DoubleShadowTextClock.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.dreams.complication;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.util.AttributeSet;
-import android.widget.TextClock;
-
-import com.android.systemui.R;
-
-/**
- * Extension of {@link TextClock} which draws two shadows on the text (ambient and key shadows)
- */
-public class DoubleShadowTextClock extends TextClock {
- private final float mAmbientShadowBlur;
- private final int mAmbientShadowColor;
- private final float mKeyShadowBlur;
- private final float mKeyShadowOffsetX;
- private final float mKeyShadowOffsetY;
- private final int mKeyShadowColor;
- private final float mAmbientShadowOffsetX;
- private final float mAmbientShadowOffsetY;
-
- public DoubleShadowTextClock(Context context) {
- this(context, null);
- }
-
- public DoubleShadowTextClock(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public DoubleShadowTextClock(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- mKeyShadowBlur = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_radius);
- mKeyShadowOffsetX = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dx);
- mKeyShadowOffsetY = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_key_text_shadow_dy);
- mKeyShadowColor = context.getResources().getColor(
- R.color.dream_overlay_clock_key_text_shadow_color);
- mAmbientShadowBlur = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_radius);
- mAmbientShadowColor = context.getResources().getColor(
- R.color.dream_overlay_clock_ambient_text_shadow_color);
- mAmbientShadowOffsetX = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dx);
- mAmbientShadowOffsetY = context.getResources()
- .getDimensionPixelSize(R.dimen.dream_overlay_clock_ambient_text_shadow_dy);
- }
-
- @Override
- public void onDraw(Canvas canvas) {
- // We enhance the shadow by drawing the shadow twice
- getPaint().setShadowLayer(mAmbientShadowBlur, mAmbientShadowOffsetX, mAmbientShadowOffsetY,
- mAmbientShadowColor);
- super.onDraw(canvas);
- canvas.save();
- canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
- getScrollX() + getWidth(),
- getScrollY() + getHeight());
-
- getPaint().setShadowLayer(
- mKeyShadowBlur, mKeyShadowOffsetX, mKeyShadowOffsetY, mKeyShadowColor);
- super.onDraw(canvas);
- canvas.restore();
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java
index 21a51d1096d5..c07d4022df76 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamMediaEntryComplication.java
@@ -18,13 +18,21 @@ package com.android.systemui.dreams.complication;
import static com.android.systemui.dreams.complication.dagger.DreamMediaEntryComplicationComponent.DreamMediaEntryModule.DREAM_MEDIA_ENTRY_VIEW;
import static com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule.DREAM_MEDIA_ENTRY_LAYOUT_PARAMS;
+import static com.android.systemui.flags.Flags.DREAM_MEDIA_TAP_TO_OPEN;
+import android.app.PendingIntent;
import android.util.Log;
import android.view.View;
+import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.complication.dagger.DreamMediaEntryComplicationComponent;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.dream.MediaDreamComplication;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
@@ -87,6 +95,15 @@ public class DreamMediaEntryComplication implements Complication {
private final DreamOverlayStateController mDreamOverlayStateController;
private final MediaDreamComplication mMediaComplication;
+ private final MediaCarouselController mMediaCarouselController;
+
+ private final ActivityStarter mActivityStarter;
+ private final ActivityIntentHelper mActivityIntentHelper;
+ private final KeyguardStateController mKeyguardStateController;
+ private final NotificationLockscreenUserManager mLockscreenUserManager;
+
+ private final FeatureFlags mFeatureFlags;
+ private boolean mIsTapToOpenEnabled;
private boolean mMediaComplicationAdded;
@@ -94,15 +111,28 @@ public class DreamMediaEntryComplication implements Complication {
DreamMediaEntryViewController(
@Named(DREAM_MEDIA_ENTRY_VIEW) View view,
DreamOverlayStateController dreamOverlayStateController,
- MediaDreamComplication mediaComplication) {
+ MediaDreamComplication mediaComplication,
+ MediaCarouselController mediaCarouselController,
+ ActivityStarter activityStarter,
+ ActivityIntentHelper activityIntentHelper,
+ KeyguardStateController keyguardStateController,
+ NotificationLockscreenUserManager lockscreenUserManager,
+ FeatureFlags featureFlags) {
super(view);
mDreamOverlayStateController = dreamOverlayStateController;
mMediaComplication = mediaComplication;
+ mMediaCarouselController = mediaCarouselController;
+ mActivityStarter = activityStarter;
+ mActivityIntentHelper = activityIntentHelper;
+ mKeyguardStateController = keyguardStateController;
+ mLockscreenUserManager = lockscreenUserManager;
+ mFeatureFlags = featureFlags;
mView.setOnClickListener(this::onClickMediaEntry);
}
@Override
protected void onViewAttached() {
+ mIsTapToOpenEnabled = mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN);
}
@Override
@@ -113,6 +143,31 @@ public class DreamMediaEntryComplication implements Complication {
private void onClickMediaEntry(View v) {
if (DEBUG) Log.d(TAG, "media entry complication tapped");
+ if (mIsTapToOpenEnabled) {
+ final PendingIntent clickIntent =
+ mMediaCarouselController.getCurrentVisibleMediaContentIntent();
+
+ if (clickIntent == null) {
+ return;
+ }
+
+ // See StatusBarNotificationActivityStarter#onNotificationClicked
+ final boolean showOverLockscreen = mKeyguardStateController.isShowing()
+ && mActivityIntentHelper.wouldShowOverLockscreen(clickIntent.getIntent(),
+ mLockscreenUserManager.getCurrentUserId());
+
+ if (showOverLockscreen) {
+ mActivityStarter.startActivity(clickIntent.getIntent(),
+ /* dismissShade */ true,
+ /* animationController */ null,
+ /* showOverLockscreenWhenLocked */ true);
+ } else {
+ mActivityStarter.postStartActivityDismissingKeyguard(clickIntent, null);
+ }
+
+ return;
+ }
+
if (!mMediaComplicationAdded) {
addMediaComplication();
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 2dd2098a78b9..f9dca08ae14f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -19,6 +19,7 @@ package com.android.systemui.dreams.dagger;
import android.content.Context;
import android.content.res.Resources;
+import com.android.dream.lowlight.dagger.LowLightDreamModule;
import com.android.settingslib.dream.DreamBackend;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -37,6 +38,7 @@ import dagger.Provides;
*/
@Module(includes = {
RegisteredComplicationsModule.class,
+ LowLightDreamModule.class,
},
subcomponents = {
DreamOverlayComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index 78099d1fd12f..38d9d0210378 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -98,11 +98,15 @@ public class Flags {
* Flag to enable the usage of the new bouncer data source. This is a refactor of and
* eventual replacement of KeyguardBouncer.java.
*/
- public static final ReleasedFlag MODERN_BOUNCER = new ReleasedFlag(208);
+ public static final UnreleasedFlag MODERN_BOUNCER = new UnreleasedFlag(208);
/** Whether UserSwitcherActivity should use modern architecture. */
- public static final UnreleasedFlag MODERN_USER_SWITCHER_ACTIVITY =
- new UnreleasedFlag(209, true);
+ public static final ReleasedFlag MODERN_USER_SWITCHER_ACTIVITY =
+ new ReleasedFlag(209, true);
+
+ /** Whether the new implementation of UserSwitcherController should be used. */
+ public static final UnreleasedFlag REFACTORED_USER_SWITCHER_CONTROLLER =
+ new UnreleasedFlag(210, false);
/***************************************/
// 300 - power menu
@@ -187,13 +191,17 @@ public class Flags {
// 802 - wallpaper rendering
public static final UnreleasedFlag USE_CANVAS_RENDERER = new UnreleasedFlag(802);
+ // 803 - screen contents translation
+ public static final UnreleasedFlag SCREEN_CONTENTS_TRANSLATION = new UnreleasedFlag(803);
+
/***************************************/
// 900 - media
- public static final ReleasedFlag MEDIA_TAP_TO_TRANSFER = new ReleasedFlag(900);
+ public static final UnreleasedFlag MEDIA_TAP_TO_TRANSFER = new UnreleasedFlag(900);
public static final UnreleasedFlag MEDIA_SESSION_ACTIONS = new UnreleasedFlag(901);
public static final ReleasedFlag MEDIA_NEARBY_DEVICES = new ReleasedFlag(903);
public static final ReleasedFlag MEDIA_MUTE_AWAIT = new ReleasedFlag(904);
- public static final UnreleasedFlag MEDIA_DREAM_COMPLICATION = new UnreleasedFlag(905);
+ public static final UnreleasedFlag DREAM_MEDIA_COMPLICATION = new UnreleasedFlag(905);
+ public static final UnreleasedFlag DREAM_MEDIA_TAP_TO_OPEN = new UnreleasedFlag(906);
// 1000 - dock
public static final ReleasedFlag SIMULATE_DOCK_THROUGH_CHARGING =
@@ -231,6 +239,14 @@ public class Flags {
public static final SysPropBooleanFlag WM_CAPTION_ON_SHELL =
new SysPropBooleanFlag(1105, "persist.wm.debug.caption_on_shell", false);
+ @Keep
+ public static final SysPropBooleanFlag FLOATING_TASKS_ENABLED =
+ new SysPropBooleanFlag(1106, "persist.wm.debug.floating_tasks", false);
+
+ @Keep
+ public static final SysPropBooleanFlag SHOW_FLOATING_TASKS_AS_BUBBLES =
+ new SysPropBooleanFlag(1107, "persist.wm.debug.floating_tasks_as_bubbles", false);
+
// 1200 - predictive back
@Keep
public static final SysPropBooleanFlag WM_ENABLE_PREDICTIVE_BACK = new SysPropBooleanFlag(
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
index a3dc77993d30..568143c8df71 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/KeyboardUI.java
@@ -42,6 +42,8 @@ import android.util.Pair;
import android.util.Slog;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -601,13 +603,14 @@ public class KeyboardUI extends CoreStartable implements InputManager.OnTabletMo
private final class BluetoothCallbackHandler implements BluetoothCallback {
@Override
- public void onBluetoothStateChanged(int bluetoothState) {
+ public void onBluetoothStateChanged(@BluetoothCallback.AdapterState int bluetoothState) {
mHandler.obtainMessage(MSG_ON_BLUETOOTH_STATE_CHANGED,
bluetoothState, 0).sendToTarget();
}
@Override
- public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
+ public void onDeviceBondStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int bondState) {
mHandler.obtainMessage(MSG_ON_DEVICE_BOND_STATE_CHANGED,
bondState, 0, cachedDevice).sendToTarget();
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index ee5ef9905819..1c6cec22ffca 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -173,7 +173,8 @@ public class KeyguardService extends Service {
}
}
- // Wrap Keyguard going away animation
+ // Wrap Keyguard going away animation.
+ // Note: Also used for wrapping occlude by Dream animation. It works (with some redundancy).
private static IRemoteTransition wrap(IRemoteAnimationRunner runner) {
return new IRemoteTransition.Stub() {
final ArrayMap<IBinder, IRemoteTransitionFinishedCallback> mFinishCallbacks =
@@ -353,6 +354,27 @@ public class KeyguardService extends Service {
f = new TransitionFilter();
f.mTypeSet = new int[]{TRANSIT_KEYGUARD_UNOCCLUDE};
mShellTransitions.registerRemote(f, unoccludeTransition);
+
+ Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_OCCLUDE for DREAM");
+ // Register for occluding by Dream
+ 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 of type DREAM showing that occludes.
+ f.mRequirements[0].mActivityType = WindowConfiguration.ACTIVITY_TYPE_DREAM;
+ 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};
+ mShellTransitions.registerRemote(f, new RemoteTransition(
+ wrap(mKeyguardViewMediator.getOccludeByDreamAnimationRunner()),
+ getIApplicationThread()));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 8eca1bad213b..c135b3c36841 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -241,9 +241,8 @@ class KeyguardUnlockAnimationController @Inject constructor(
*/
@VisibleForTesting
var surfaceTransactionApplier: SyncRtSurfaceTransactionApplier? = null
- private var surfaceBehindRemoteAnimationTarget: RemoteAnimationTarget? = null
+ private var surfaceBehindRemoteAnimationTargets: Array<RemoteAnimationTarget>? = null
private var surfaceBehindRemoteAnimationStartTime: Long = 0
- private var surfaceBehindParams: SyncRtSurfaceTransactionApplier.SurfaceParams? = null
/**
* Alpha value applied to [surfaceBehindRemoteAnimationTarget], which is the surface of the
@@ -458,7 +457,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
* (fingerprint, tap, etc.) and the keyguard is going away.
*/
fun notifyStartSurfaceBehindRemoteAnimation(
- target: RemoteAnimationTarget,
+ targets: Array<RemoteAnimationTarget>,
startTime: Long,
requestedShowSurfaceBehindKeyguard: Boolean
) {
@@ -467,10 +466,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
keyguardViewController.viewRootImpl.view)
}
- // New animation, new params.
- surfaceBehindParams = null
-
- surfaceBehindRemoteAnimationTarget = target
+ surfaceBehindRemoteAnimationTargets = targets
surfaceBehindRemoteAnimationStartTime = startTime
// If we specifically requested that the surface behind be made visible (vs. it being made
@@ -597,7 +593,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
* keyguard dismiss amount and the method of dismissal.
*/
private fun updateSurfaceBehindAppearAmount() {
- if (surfaceBehindRemoteAnimationTarget == null) {
+ if (surfaceBehindRemoteAnimationTargets == null) {
return
}
@@ -710,63 +706,60 @@ class KeyguardUnlockAnimationController @Inject constructor(
* cancelled).
*/
fun setSurfaceBehindAppearAmount(amount: Float) {
- if (surfaceBehindRemoteAnimationTarget == null) {
- return
- }
+ surfaceBehindRemoteAnimationTargets?.forEach { surfaceBehindRemoteAnimationTarget ->
+ val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget.screenSpaceBounds.height()
+
+ var scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
+ (1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
+ MathUtils.clamp(amount, 0f, 1f))
- // Otherwise, animate in the surface's scale/transltion.
- val surfaceHeight: Int = surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.height()
-
- var scaleFactor = (SURFACE_BEHIND_START_SCALE_FACTOR +
- (1f - SURFACE_BEHIND_START_SCALE_FACTOR) *
- MathUtils.clamp(amount, 0f, 1f))
-
- // If we're dismissing via swipe to the Launcher, we'll play in-window scale animations, so
- // don't also scale the window.
- if (keyguardStateController.isDismissingFromSwipe &&
- willUnlockWithInWindowLauncherAnimations) {
- scaleFactor = 1f
- }
-
- // Scale up from a point at the center-bottom of the surface.
- surfaceBehindMatrix.setScale(
- scaleFactor,
- scaleFactor,
- surfaceBehindRemoteAnimationTarget!!.screenSpaceBounds.width() / 2f,
- surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
- )
-
- // Translate up from the bottom.
- surfaceBehindMatrix.postTranslate(
- 0f,
- surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
- )
-
- // If we're snapping the keyguard back, immediately begin fading it out.
- val animationAlpha =
- if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount
- else surfaceBehindAlpha
-
- // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is unable
- // to draw
- val sc: SurfaceControl? = surfaceBehindRemoteAnimationTarget?.leash
- if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
- sc?.isValid == true) {
- with(SurfaceControl.Transaction()) {
- setMatrix(sc, surfaceBehindMatrix, tmpFloat)
- setCornerRadius(sc, roundedCornerRadius)
- setAlpha(sc, animationAlpha)
- apply()
+ // If we're dismissing via swipe to the Launcher, we'll play in-window scale animations,
+ // so don't also scale the window.
+ if (keyguardStateController.isDismissingFromSwipe &&
+ willUnlockWithInWindowLauncherAnimations) {
+ scaleFactor = 1f
}
- } else {
- applyParamsToSurface(
- SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
- surfaceBehindRemoteAnimationTarget!!.leash)
- .withMatrix(surfaceBehindMatrix)
- .withCornerRadius(roundedCornerRadius)
- .withAlpha(animationAlpha)
- .build()
+
+ // Translate up from the bottom.
+ surfaceBehindMatrix.setTranslate(
+ surfaceBehindRemoteAnimationTarget.screenSpaceBounds.left.toFloat(),
+ surfaceHeight * SURFACE_BEHIND_START_TRANSLATION_Y * (1f - amount)
)
+
+ // Scale up from a point at the center-bottom of the surface.
+ surfaceBehindMatrix.postScale(
+ scaleFactor,
+ scaleFactor,
+ keyguardViewController.viewRootImpl.width / 2f,
+ surfaceHeight * SURFACE_BEHIND_SCALE_PIVOT_Y
+ )
+
+ // If we're snapping the keyguard back, immediately begin fading it out.
+ val animationAlpha =
+ if (keyguardStateController.isSnappingKeyguardBackAfterSwipe) amount
+ else surfaceBehindAlpha
+
+ // SyncRtSurfaceTransactionApplier cannot apply transaction when the target view is
+ // unable to draw
+ val sc: SurfaceControl? = surfaceBehindRemoteAnimationTarget.leash
+ if (keyguardViewController.viewRootImpl.view?.visibility != View.VISIBLE &&
+ sc?.isValid == true) {
+ with(SurfaceControl.Transaction()) {
+ setMatrix(sc, surfaceBehindMatrix, tmpFloat)
+ setCornerRadius(sc, roundedCornerRadius)
+ setAlpha(sc, animationAlpha)
+ apply()
+ }
+ } else {
+ applyParamsToSurface(
+ SyncRtSurfaceTransactionApplier.SurfaceParams.Builder(
+ surfaceBehindRemoteAnimationTarget.leash)
+ .withMatrix(surfaceBehindMatrix)
+ .withCornerRadius(roundedCornerRadius)
+ .withAlpha(animationAlpha)
+ .build()
+ )
+ }
}
}
@@ -791,8 +784,7 @@ class KeyguardUnlockAnimationController @Inject constructor(
launcherUnlockController?.setUnlockAmount(1f, false /* forceIfAnimating */)
// That target is no longer valid since the animation finished, null it out.
- surfaceBehindRemoteAnimationTarget = null
- surfaceBehindParams = null
+ surfaceBehindRemoteAnimationTargets = null
playingCannedUnlockAnimation = false
willUnlockWithInWindowLauncherAnimations = false
@@ -824,7 +816,6 @@ class KeyguardUnlockAnimationController @Inject constructor(
private fun applyParamsToSurface(params: SyncRtSurfaceTransactionApplier.SurfaceParams) {
surfaceTransactionApplier!!.scheduleApply(params)
- surfaceBehindParams = params
}
private fun fadeInSurfaceBehind() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index dc034360ad09..6a7c3901551c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -22,8 +22,10 @@ import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN;
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_OCCLUSION;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_TRANSITION_FROM_AOD;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_LOCKSCREEN_UNLOCK_ANIMATION;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
@@ -802,6 +804,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
} else if (trustAgentsEnabled
&& (strongAuth & SOME_AUTH_REQUIRED_AFTER_USER_REQUEST) != 0) {
return KeyguardSecurityView.PROMPT_REASON_USER_REQUEST;
+ } else if (trustAgentsEnabled
+ && (strongAuth & SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_TRUSTAGENT_EXPIRED;
} else if (any && ((strongAuth & STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) != 0
|| mUpdateMonitor.isFingerprintLockedOut())) {
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
@@ -831,7 +836,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
public void onLaunchAnimationStart(boolean isExpandingFullyAbove) {}
@Override
- public void onLaunchAnimationCancelled() {
+ public void onLaunchAnimationCancelled(@Nullable Boolean newKeyguardOccludedState) {
Log.d(TAG, "Occlude launch animation cancelled. Occluded state is now: "
+ mOccluded);
}
@@ -841,6 +846,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
if (launchIsFullScreen) {
mCentralSurfaces.instantCollapseNotificationPanel();
}
+
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION);
}
@NonNull
@@ -987,6 +994,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */);
Log.d(TAG, "Unocclude animation cancelled. Occluded state is now: "
+ mOccluded);
+
+ mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_OCCLUSION);
}
@Override
@@ -995,6 +1004,9 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
RemoteAnimationTarget[] nonApps,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
Log.d(TAG, "UnoccludeAnimator#onAnimationStart. Set occluded = false.");
+ mInteractionJankMonitor.begin(
+ createInteractionJankMonitorConf(CUJ_LOCKSCREEN_OCCLUSION)
+ .setTag("UNOCCLUDE"));
setOccluded(false /* isOccluded */, true /* animate */);
if (apps == null || apps.length == 0 || apps[0] == null) {
@@ -1053,6 +1065,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
try {
finishedCallback.onAnimationFinished();
mUnoccludeAnimator = null;
+
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION);
} catch (RemoteException e) {
e.printStackTrace();
}
@@ -2563,7 +2577,8 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
};
try {
mInteractionJankMonitor.begin(
- createInteractionJankMonitorConf("RunRemoteAnimation"));
+ createInteractionJankMonitorConf(
+ CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "RunRemoteAnimation"));
runner.onAnimationStart(WindowManager.TRANSIT_KEYGUARD_GOING_AWAY, apps,
wallpapers, nonApps, callback);
} catch (RemoteException e) {
@@ -2578,23 +2593,17 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
mSurfaceBehindRemoteAnimationRunning = true;
mInteractionJankMonitor.begin(
- createInteractionJankMonitorConf("DismissPanel"));
-
- // Apply the opening animation on root task if exists
- RemoteAnimationTarget aniTarget = apps[0];
- for (RemoteAnimationTarget tmpTarget : apps) {
- if (tmpTarget.taskId != -1 && !tmpTarget.hasAnimatingParent) {
- aniTarget = tmpTarget;
- break;
- }
- }
+ createInteractionJankMonitorConf(
+ CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "DismissPanel"));
+
// Pass the surface and metadata to the unlock animation controller.
mKeyguardUnlockAnimationControllerLazy.get()
.notifyStartSurfaceBehindRemoteAnimation(
- aniTarget, startTime, mSurfaceBehindRemoteAnimationRequested);
+ apps, startTime, mSurfaceBehindRemoteAnimationRequested);
} else {
mInteractionJankMonitor.begin(
- createInteractionJankMonitorConf("RemoteAnimationDisabled"));
+ createInteractionJankMonitorConf(
+ CUJ_LOCKSCREEN_UNLOCK_ANIMATION, "RemoteAnimationDisabled"));
mKeyguardViewControllerLazy.get().hide(startTime, fadeoutDuration);
@@ -2674,10 +2683,15 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
sendUserPresentBroadcast();
}
- private Configuration.Builder createInteractionJankMonitorConf(String tag) {
- return Configuration.Builder.withView(CUJ_LOCKSCREEN_UNLOCK_ANIMATION,
- mKeyguardViewControllerLazy.get().getViewRootImpl().getView())
- .setTag(tag);
+ private Configuration.Builder createInteractionJankMonitorConf(int cuj) {
+ return createInteractionJankMonitorConf(cuj, null /* tag */);
+ }
+
+ private Configuration.Builder createInteractionJankMonitorConf(int cuj, @Nullable String tag) {
+ final Configuration.Builder builder = Configuration.Builder.withView(
+ cuj, mKeyguardViewControllerLazy.get().getViewRootImpl().getView());
+
+ return tag != null ? builder.setTag(tag) : builder;
}
/**
@@ -3288,6 +3302,10 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
IRemoteAnimationFinishedCallback finishedCallback) throws RemoteException {
super.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback);
+ mInteractionJankMonitor.begin(
+ createInteractionJankMonitorConf(CUJ_LOCKSCREEN_OCCLUSION)
+ .setTag("OCCLUDE"));
+
// This is the first signal we have from WM that we're going to be occluded. Set our
// internal state to reflect that immediately, vs. waiting for the launch animator to
// begin. Otherwise, calls to setShowingLocked, etc. will not know that we're about to
@@ -3304,6 +3322,7 @@ public class KeyguardViewMediator extends CoreStartable implements Dumpable,
+ "Setting occluded state to: " + isKeyguardOccluded);
setOccluded(isKeyguardOccluded /* occluded */, false /* animate */);
+ mInteractionJankMonitor.cancel(CUJ_LOCKSCREEN_OCCLUSION);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
new file mode 100644
index 000000000000..99ae85d7a548
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.data
+
+import android.view.KeyEvent
+import com.android.systemui.dagger.SysUISingleton
+import java.lang.ref.WeakReference
+import javax.inject.Inject
+
+/** An abstraction to interface with the ui layer, without changing state. */
+interface BouncerView {
+ var delegate: BouncerViewDelegate?
+}
+
+/** A lightweight class to hold reference to the ui delegate. */
+@SysUISingleton
+class BouncerViewImpl @Inject constructor() : BouncerView {
+ private var _delegate: WeakReference<BouncerViewDelegate?> = WeakReference(null)
+ override var delegate: BouncerViewDelegate?
+ get() = _delegate.get()
+ set(value) {
+ _delegate = WeakReference(value)
+ }
+}
+
+/** An abstraction that implements view logic. */
+interface BouncerViewDelegate {
+ fun isFullScreenBouncer(): Boolean
+ fun shouldDismissOnMenuPressed(): Boolean
+ fun interceptMediaKey(event: KeyEvent?): Boolean
+ fun dispatchBackKeyEventPreIme(): Boolean
+ fun showNextSecurityScreenOrFinish(): Boolean
+ fun resume()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt
new file mode 100644
index 000000000000..390c54e0350a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.data
+
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface BouncerViewModule {
+ /** Binds BouncerView to BouncerViewImpl and makes it injectable. */
+ @Binds fun bindBouncerView(bouncerViewImpl: BouncerViewImpl): BouncerView
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
new file mode 100644
index 000000000000..543389e0a7cd
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.data.repository
+
+import android.hardware.biometrics.BiometricSourceType
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.keyguard.ViewMediatorCallback
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN
+import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.asStateFlow
+
+/** Encapsulates app state for the lock screen bouncer. */
+@SysUISingleton
+class KeyguardBouncerRepository
+@Inject
+constructor(
+ private val viewMediatorCallback: ViewMediatorCallback,
+ keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) {
+ var bouncerPromptReason: Int? = null
+ /** Determines if we want to instantaneously show the bouncer instead of translating. */
+ private val _isScrimmed = MutableStateFlow(false)
+ val isScrimmed = _isScrimmed.asStateFlow()
+ /** Set amount of how much of the bouncer is showing on the screen */
+ private val _expansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
+ val expansionAmount = _expansionAmount.asStateFlow()
+ private val _isVisible = MutableStateFlow(false)
+ val isVisible = _isVisible.asStateFlow()
+ private val _show = MutableStateFlow<KeyguardBouncerModel?>(null)
+ val show = _show.asStateFlow()
+ private val _showingSoon = MutableStateFlow(false)
+ val showingSoon = _showingSoon.asStateFlow()
+ private val _hide = MutableStateFlow(false)
+ val hide = _hide.asStateFlow()
+ private val _startingToHide = MutableStateFlow(false)
+ val startingToHide = _startingToHide.asStateFlow()
+ private val _onDismissAction = MutableStateFlow<BouncerCallbackActionsModel?>(null)
+ val onDismissAction = _onDismissAction.asStateFlow()
+ private val _disappearAnimation = MutableStateFlow<Runnable?>(null)
+ val startingDisappearAnimation = _disappearAnimation.asStateFlow()
+ private val _keyguardPosition = MutableStateFlow(0f)
+ val keyguardPosition = _keyguardPosition.asStateFlow()
+ private val _resourceUpdateRequests = MutableStateFlow(false)
+ val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
+ private val _showMessage = MutableStateFlow<BouncerShowMessageModel?>(null)
+ val showMessage = _showMessage.asStateFlow()
+ private val _keyguardAuthenticated = MutableStateFlow<Boolean?>(null)
+ /** Determines if user is already unlocked */
+ val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow()
+ private val _isBackButtonEnabled = MutableStateFlow<Boolean?>(null)
+ val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow()
+ private val _onScreenTurnedOff = MutableStateFlow(false)
+ val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow()
+
+ val bouncerErrorMessage: CharSequence?
+ get() = viewMediatorCallback.consumeCustomMessage()
+
+ init {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onStrongAuthStateChanged(userId: Int) {
+ bouncerPromptReason = viewMediatorCallback.bouncerPromptReason
+ }
+
+ override fun onLockedOutStateChanged(type: BiometricSourceType) {
+ if (type == BiometricSourceType.FINGERPRINT) {
+ bouncerPromptReason = viewMediatorCallback.bouncerPromptReason
+ }
+ }
+ }
+
+ keyguardUpdateMonitor.registerCallback(callback)
+ }
+
+ fun setScrimmed(isScrimmed: Boolean) {
+ _isScrimmed.value = isScrimmed
+ }
+
+ fun setExpansion(expansion: Float) {
+ _expansionAmount.value = expansion
+ }
+
+ fun setVisible(isVisible: Boolean) {
+ _isVisible.value = isVisible
+ }
+
+ fun setShow(keyguardBouncerModel: KeyguardBouncerModel?) {
+ _show.value = keyguardBouncerModel
+ }
+
+ fun setShowingSoon(showingSoon: Boolean) {
+ _showingSoon.value = showingSoon
+ }
+
+ fun setHide(hide: Boolean) {
+ _hide.value = hide
+ }
+
+ fun setStartingToHide(startingToHide: Boolean) {
+ _startingToHide.value = startingToHide
+ }
+
+ fun setOnDismissAction(bouncerCallbackActionsModel: BouncerCallbackActionsModel?) {
+ _onDismissAction.value = bouncerCallbackActionsModel
+ }
+
+ fun setStartDisappearAnimation(runnable: Runnable?) {
+ _disappearAnimation.value = runnable
+ }
+
+ fun setKeyguardPosition(keyguardPosition: Float) {
+ _keyguardPosition.value = keyguardPosition
+ }
+
+ fun setResourceUpdateRequests(willUpdateResources: Boolean) {
+ _resourceUpdateRequests.value = willUpdateResources
+ }
+
+ fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) {
+ _showMessage.value = bouncerShowMessageModel
+ }
+
+ fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) {
+ _keyguardAuthenticated.value = keyguardAuthenticated
+ }
+
+ fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) {
+ _isBackButtonEnabled.value = isBackButtonEnabled
+ }
+
+ fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) {
+ _onScreenTurnedOff.value = onScreenTurnedOff
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt
new file mode 100644
index 000000000000..10c7a3774e09
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import android.view.View
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.phone.KeyguardBouncer
+import com.android.systemui.util.ListenerSet
+import javax.inject.Inject
+
+/** Interactor to add and remove callbacks for the bouncer. */
+@SysUISingleton
+class BouncerCallbackInteractor @Inject constructor() {
+ private var resetCallbacks = ListenerSet<KeyguardBouncer.KeyguardResetCallback>()
+ private var expansionCallbacks = ArrayList<KeyguardBouncer.BouncerExpansionCallback>()
+ /** Add a KeyguardResetCallback. */
+ fun addKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) {
+ resetCallbacks.addIfAbsent(callback)
+ }
+
+ /** Remove a KeyguardResetCallback. */
+ fun removeKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) {
+ resetCallbacks.remove(callback)
+ }
+
+ /** Adds a callback to listen to bouncer expansion updates. */
+ fun addBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) {
+ if (!expansionCallbacks.contains(callback)) {
+ expansionCallbacks.add(callback)
+ }
+ }
+
+ /**
+ * Removes a previously added callback. If the callback was never added, this method does
+ * nothing.
+ */
+ fun removeBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) {
+ expansionCallbacks.remove(callback)
+ }
+
+ /** Propagate fully shown to bouncer expansion callbacks. */
+ fun dispatchFullyShown() {
+ for (callback in expansionCallbacks) {
+ callback.onFullyShown()
+ }
+ }
+
+ /** Propagate starting to hide to bouncer expansion callbacks. */
+ fun dispatchStartingToHide() {
+ for (callback in expansionCallbacks) {
+ callback.onStartingToHide()
+ }
+ }
+
+ /** Propagate starting to show to bouncer expansion callbacks. */
+ fun dispatchStartingToShow() {
+ for (callback in expansionCallbacks) {
+ callback.onStartingToShow()
+ }
+ }
+
+ /** Propagate fully hidden to bouncer expansion callbacks. */
+ fun dispatchFullyHidden() {
+ for (callback in expansionCallbacks) {
+ callback.onFullyHidden()
+ }
+ }
+
+ /** Propagate expansion changes to bouncer expansion callbacks. */
+ fun dispatchExpansionChanged(expansion: Float) {
+ for (callback in expansionCallbacks) {
+ callback.onExpansionChanged(expansion)
+ }
+ }
+ /** Propagate visibility changes to bouncer expansion callbacks. */
+ fun dispatchVisibilityChanged(visibility: Int) {
+ for (callback in expansionCallbacks) {
+ callback.onVisibilityChanged(visibility == View.VISIBLE)
+ }
+ }
+
+ /** Propagate keyguard reset. */
+ fun dispatchReset() {
+ for (callback in resetCallbacks) {
+ callback.onKeyguardReset()
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt
new file mode 100644
index 000000000000..7d4db37c6b0f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import android.content.res.ColorStateList
+import android.os.Handler
+import android.os.Trace
+import android.os.UserHandle
+import android.os.UserManager
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.DejankUtils
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.statusbar.phone.KeyguardBouncer
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+/** Encapsulates business logic for interacting with the lock-screen bouncer. */
+@SysUISingleton
+class BouncerInteractor
+@Inject
+constructor(
+ private val repository: KeyguardBouncerRepository,
+ private val bouncerView: BouncerView,
+ @Main private val mainHandler: Handler,
+ private val keyguardStateController: KeyguardStateController,
+ private val keyguardSecurityModel: KeyguardSecurityModel,
+ private val callbackInteractor: BouncerCallbackInteractor,
+ private val falsingCollector: FalsingCollector,
+ private val dismissCallbackRegistry: DismissCallbackRegistry,
+ keyguardBypassController: KeyguardBypassController,
+ keyguardUpdateMonitor: KeyguardUpdateMonitor,
+) {
+ /** Whether we want to wait for face auth. */
+ private val bouncerFaceDelay =
+ keyguardStateController.isFaceAuthEnabled &&
+ !keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
+ KeyguardUpdateMonitor.getCurrentUser()
+ ) &&
+ !needsFullscreenBouncer() &&
+ !keyguardUpdateMonitor.userNeedsStrongAuth() &&
+ !keyguardBypassController.bypassEnabled
+
+ /** Runnable to show the bouncer. */
+ val showRunnable = Runnable {
+ repository.setVisible(true)
+ repository.setShow(
+ KeyguardBouncerModel(
+ promptReason = repository.bouncerPromptReason ?: 0,
+ errorMessage = repository.bouncerErrorMessage,
+ expansionAmount = repository.expansionAmount.value
+ )
+ )
+ repository.setShowingSoon(false)
+ }
+
+ val keyguardAuthenticated: Flow<Boolean> = repository.keyguardAuthenticated.filterNotNull()
+ val screenTurnedOff: Flow<Unit> = repository.onScreenTurnedOff.filter { it }.map {}
+ val show: Flow<KeyguardBouncerModel> = repository.show.filterNotNull()
+ val hide: Flow<Unit> = repository.hide.filter { it }.map {}
+ val startingToHide: Flow<Unit> = repository.startingToHide.filter { it }.map {}
+ val isVisible: Flow<Boolean> = repository.isVisible
+ val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull()
+ val expansionAmount: Flow<Float> = repository.expansionAmount
+ val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull()
+ val startingDisappearAnimation: Flow<Runnable> =
+ repository.startingDisappearAnimation.filterNotNull()
+ val onDismissAction: Flow<BouncerCallbackActionsModel> =
+ repository.onDismissAction.filterNotNull()
+ val resourceUpdateRequests: Flow<Boolean> = repository.resourceUpdateRequests.filter { it }
+ val keyguardPosition: Flow<Float> = repository.keyguardPosition
+
+ // TODO(b/243685699): Move isScrimmed logic to data layer.
+ // TODO(b/243695312): Encapsulate all of the show logic for the bouncer.
+ /** Show the bouncer if necessary and set the relevant states. */
+ @JvmOverloads
+ fun show(isScrimmed: Boolean) {
+ // Reset some states as we show the bouncer.
+ repository.setShowMessage(null)
+ repository.setOnScreenTurnedOff(false)
+ repository.setKeyguardAuthenticated(null)
+ repository.setHide(false)
+ repository.setStartingToHide(false)
+
+ val resumeBouncer =
+ (repository.isVisible.value || repository.showingSoon.value) && needsFullscreenBouncer()
+
+ if (!resumeBouncer && repository.show.value != null) {
+ // If bouncer is visible, the bouncer is already showing.
+ return
+ }
+
+ val keyguardUserId = KeyguardUpdateMonitor.getCurrentUser()
+ if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) {
+ // In split system user mode, we never unlock system user.
+ return
+ }
+
+ Trace.beginSection("KeyguardBouncer#show")
+ repository.setScrimmed(isScrimmed)
+ if (isScrimmed) {
+ setExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
+ }
+
+ if (resumeBouncer) {
+ bouncerView.delegate?.resume()
+ // Bouncer is showing the next security screen and we just need to prompt a resume.
+ return
+ }
+ if (bouncerView.delegate?.showNextSecurityScreenOrFinish() == true) {
+ // Keyguard is done.
+ return
+ }
+
+ repository.setShowingSoon(true)
+ if (bouncerFaceDelay) {
+ mainHandler.postDelayed(showRunnable, 1200L)
+ } else {
+ DejankUtils.postAfterTraversal(showRunnable)
+ }
+ keyguardStateController.notifyBouncerShowing(true)
+ callbackInteractor.dispatchStartingToShow()
+
+ Trace.endSection()
+ }
+
+ /** Sets the correct bouncer states to hide the bouncer. */
+ fun hide() {
+ Trace.beginSection("KeyguardBouncer#hide")
+ if (isFullyShowing()) {
+ SysUiStatsLog.write(
+ SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED,
+ SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN
+ )
+ dismissCallbackRegistry.notifyDismissCancelled()
+ }
+
+ falsingCollector.onBouncerHidden()
+ keyguardStateController.notifyBouncerShowing(false /* showing */)
+ cancelShowRunnable()
+ repository.setShowingSoon(false)
+ repository.setOnDismissAction(null)
+ repository.setVisible(false)
+ repository.setHide(true)
+ repository.setShow(null)
+ Trace.endSection()
+ }
+
+ /**
+ * Sets the panel expansion which is calculated further upstream. Expansion is from 0f to 1f
+ * where 0f => showing and 1f => hiding
+ */
+ fun setExpansion(expansion: Float) {
+ val oldExpansion = repository.expansionAmount.value
+ val expansionChanged = oldExpansion != expansion
+ if (repository.startingDisappearAnimation.value == null) {
+ repository.setExpansion(expansion)
+ }
+
+ if (
+ expansion == KeyguardBouncer.EXPANSION_VISIBLE &&
+ oldExpansion != KeyguardBouncer.EXPANSION_VISIBLE
+ ) {
+ falsingCollector.onBouncerShown()
+ callbackInteractor.dispatchFullyShown()
+ } else if (
+ expansion == KeyguardBouncer.EXPANSION_HIDDEN &&
+ oldExpansion != KeyguardBouncer.EXPANSION_HIDDEN
+ ) {
+ repository.setVisible(false)
+ repository.setShow(null)
+ falsingCollector.onBouncerHidden()
+ DejankUtils.postAfterTraversal { callbackInteractor.dispatchReset() }
+ callbackInteractor.dispatchFullyHidden()
+ } else if (
+ expansion != KeyguardBouncer.EXPANSION_VISIBLE &&
+ oldExpansion == KeyguardBouncer.EXPANSION_VISIBLE
+ ) {
+ callbackInteractor.dispatchStartingToHide()
+ repository.setStartingToHide(true)
+ }
+ if (expansionChanged) {
+ callbackInteractor.dispatchExpansionChanged(expansion)
+ }
+ }
+
+ /** Set the initial keyguard message to show when bouncer is shown. */
+ fun showMessage(message: String?, colorStateList: ColorStateList?) {
+ repository.setShowMessage(BouncerShowMessageModel(message, colorStateList))
+ }
+
+ /**
+ * Sets actions to the bouncer based on how the bouncer is dismissed. If the bouncer is
+ * unlocked, we will run the onDismissAction. If the bouncer is existed before unlocking, we
+ * call cancelAction.
+ */
+ fun setDismissAction(
+ onDismissAction: ActivityStarter.OnDismissAction?,
+ cancelAction: Runnable?
+ ) {
+ repository.setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction))
+ }
+
+ /** Update the resources of the views. */
+ fun updateResources() {
+ repository.setResourceUpdateRequests(true)
+ }
+
+ /** Tell the bouncer that keyguard is authenticated. */
+ fun notifyKeyguardAuthenticated(strongAuth: Boolean) {
+ repository.setKeyguardAuthenticated(strongAuth)
+ }
+
+ /** Tell the bouncer the screen has turned off. */
+ fun onScreenTurnedOff() {
+ repository.setOnScreenTurnedOff(true)
+ }
+
+ /** Update the position of the bouncer when showing. */
+ fun setKeyguardPosition(position: Float) {
+ repository.setKeyguardPosition(position)
+ }
+
+ /** Notifies that the state change was handled. */
+ fun notifyKeyguardAuthenticatedHandled() {
+ repository.setKeyguardAuthenticated(null)
+ }
+
+ /** Notify that view visibility has changed. */
+ fun notifyBouncerVisibilityHasChanged(visibility: Int) {
+ callbackInteractor.dispatchVisibilityChanged(visibility)
+ }
+
+ /** Notify that the resources have been updated */
+ fun notifyUpdatedResources() {
+ repository.setResourceUpdateRequests(false)
+ }
+
+ /** Set whether back button is enabled when on the bouncer screen. */
+ fun setBackButtonEnabled(enabled: Boolean) {
+ repository.setIsBackButtonEnabled(enabled)
+ }
+
+ /** Tell the bouncer to start the pre hide animation. */
+ fun startDisappearAnimation(runnable: Runnable) {
+ val finishRunnable = Runnable {
+ repository.setStartDisappearAnimation(null)
+ runnable.run()
+ }
+ repository.setStartDisappearAnimation(finishRunnable)
+ }
+
+ /** Returns whether bouncer is fully showing. */
+ fun isFullyShowing(): Boolean {
+ return (repository.showingSoon.value || repository.isVisible.value) &&
+ repository.expansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE &&
+ repository.startingDisappearAnimation.value == null
+ }
+
+ /** Returns whether bouncer is scrimmed. */
+ fun isScrimmed(): Boolean {
+ return repository.isScrimmed.value
+ }
+
+ /** If bouncer expansion is between 0f and 1f non-inclusive. */
+ fun isInTransit(): Boolean {
+ return repository.showingSoon.value ||
+ repository.expansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN &&
+ repository.expansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE
+ }
+
+ /** Return whether bouncer is animating away. */
+ fun isAnimatingAway(): Boolean {
+ return repository.startingDisappearAnimation.value != null
+ }
+
+ /** Return whether bouncer will dismiss with actions */
+ fun willDismissWithAction(): Boolean {
+ return repository.onDismissAction.value?.onDismissAction != null
+ }
+
+ /** Returns whether the bouncer should be full screen. */
+ private fun needsFullscreenBouncer(): Boolean {
+ val mode: KeyguardSecurityModel.SecurityMode =
+ keyguardSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser())
+ return mode == KeyguardSecurityModel.SecurityMode.SimPin ||
+ mode == KeyguardSecurityModel.SecurityMode.SimPuk
+ }
+
+ /** Remove the show runnable from the main handler queue to improve performance. */
+ private fun cancelShowRunnable() {
+ DejankUtils.removeCallbacks(showRunnable)
+ mainHandler.removeCallbacks(showRunnable)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt
new file mode 100644
index 000000000000..81cf5b41ea71
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.shared.model
+
+import com.android.systemui.plugins.ActivityStarter
+
+/** Encapsulates callbacks to be invoked by the bouncer logic. */
+// TODO(b/243683121): Move dismiss logic from view controllers
+data class BouncerCallbackActionsModel(
+ val onDismissAction: ActivityStarter.OnDismissAction?,
+ val cancelAction: Runnable?
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt
new file mode 100644
index 000000000000..05cdeaaa106d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.shared.model
+
+import android.content.res.ColorStateList
+
+/** Show a keyguard message to the bouncer. */
+data class BouncerShowMessageModel(val message: String?, val colorStateList: ColorStateList?)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt
new file mode 100644
index 000000000000..ad783da7f304
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.shared.model
+
+/** Models the state of the lock-screen bouncer */
+data class KeyguardBouncerModel(
+ val promptReason: Int = 0,
+ val errorMessage: CharSequence? = null,
+ val expansionAmount: Float = 0f,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
new file mode 100644
index 000000000000..df260148751c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ui.binder
+
+import android.view.KeyEvent
+import android.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.internal.policy.SystemBarUtils
+import com.android.keyguard.KeyguardHostViewController
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.keyguard.dagger.KeyguardBouncerComponent
+import com.android.systemui.keyguard.data.BouncerViewDelegate
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.launch
+
+/** Binds the bouncer container to its view model. */
+object KeyguardBouncerViewBinder {
+ @JvmStatic
+ fun bind(
+ view: ViewGroup,
+ viewModel: KeyguardBouncerViewModel,
+ componentFactory: KeyguardBouncerComponent.Factory
+ ) {
+ // Builds the KeyguardHostViewController from bouncer view group.
+ val hostViewController: KeyguardHostViewController =
+ componentFactory.create(view).keyguardHostViewController
+ hostViewController.init()
+ val delegate =
+ object : BouncerViewDelegate {
+ override fun isFullScreenBouncer(): Boolean {
+ val mode = hostViewController.currentSecurityMode
+ return mode == KeyguardSecurityModel.SecurityMode.SimPin ||
+ mode == KeyguardSecurityModel.SecurityMode.SimPuk
+ }
+
+ override fun shouldDismissOnMenuPressed(): Boolean {
+ return hostViewController.shouldEnableMenuKey()
+ }
+
+ override fun interceptMediaKey(event: KeyEvent?): Boolean {
+ return hostViewController.interceptMediaKey(event)
+ }
+
+ override fun dispatchBackKeyEventPreIme(): Boolean {
+ return hostViewController.dispatchBackKeyEventPreIme()
+ }
+
+ override fun showNextSecurityScreenOrFinish(): Boolean {
+ return hostViewController.dismiss(KeyguardUpdateMonitor.getCurrentUser())
+ }
+
+ override fun resume() {
+ hostViewController.showPrimarySecurityScreen()
+ hostViewController.onResume()
+ }
+ }
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ try {
+ viewModel.setBouncerViewDelegate(delegate)
+ launch {
+ viewModel.show.collect {
+ hostViewController.showPrimarySecurityScreen()
+ hostViewController.appear(
+ SystemBarUtils.getStatusBarHeight(view.context)
+ )
+ }
+ }
+
+ launch {
+ viewModel.showPromptReason.collect { prompt ->
+ hostViewController.showPromptReason(prompt)
+ }
+ }
+
+ launch {
+ viewModel.showBouncerErrorMessage.collect { errorMessage ->
+ hostViewController.showErrorMessage(errorMessage)
+ }
+ }
+
+ launch {
+ viewModel.showWithFullExpansion.collect { model ->
+ hostViewController.resetSecurityContainer()
+ hostViewController.showPromptReason(model.promptReason)
+ hostViewController.onResume()
+ }
+ }
+
+ launch {
+ viewModel.hide.collect {
+ hostViewController.cancelDismissAction()
+ hostViewController.cleanUp()
+ hostViewController.resetSecurityContainer()
+ }
+ }
+
+ launch {
+ viewModel.startingToHide.collect { hostViewController.onStartingToHide() }
+ }
+
+ launch {
+ viewModel.setDismissAction.collect {
+ hostViewController.setOnDismissAction(
+ it.onDismissAction,
+ it.cancelAction
+ )
+ }
+ }
+
+ launch {
+ viewModel.startDisappearAnimation.collect {
+ hostViewController.startDisappearAnimation(it)
+ }
+ }
+
+ launch {
+ viewModel.bouncerExpansionAmount.collect { expansion ->
+ hostViewController.setExpansion(expansion)
+ }
+ }
+
+ launch {
+ viewModel.bouncerExpansionAmount
+ .filter { it == EXPANSION_VISIBLE }
+ .collect {
+ hostViewController.onResume()
+ view.announceForAccessibility(
+ hostViewController.accessibilityTitleForCurrentMode
+ )
+ }
+ }
+
+ launch {
+ viewModel.isBouncerVisible.collect { isVisible ->
+ val visibility = if (isVisible) View.VISIBLE else View.INVISIBLE
+ view.visibility = visibility
+ hostViewController.onBouncerVisibilityChanged(visibility)
+ viewModel.notifyBouncerVisibilityHasChanged(visibility)
+ }
+ }
+
+ launch {
+ viewModel.isBouncerVisible
+ .filter { !it }
+ .collect {
+ // Remove existing input for security reasons.
+ hostViewController.resetSecurityContainer()
+ }
+ }
+
+ launch {
+ viewModel.keyguardPosition.collect { position ->
+ hostViewController.updateKeyguardPosition(position)
+ }
+ }
+
+ launch {
+ viewModel.updateResources.collect {
+ hostViewController.updateResources()
+ viewModel.notifyUpdateResources()
+ }
+ }
+
+ launch {
+ viewModel.bouncerShowMessage.collect {
+ hostViewController.showMessage(it.message, it.colorStateList)
+ }
+ }
+
+ launch {
+ viewModel.keyguardAuthenticated.collect {
+ hostViewController.finish(it, KeyguardUpdateMonitor.getCurrentUser())
+ viewModel.notifyKeyguardAuthenticated()
+ }
+ }
+
+ launch {
+ viewModel
+ .observeOnIsBackButtonEnabled { view.systemUiVisibility }
+ .collect { view.systemUiVisibility = it }
+ }
+
+ launch {
+ viewModel.screenTurnedOff.collect {
+ if (view.visibility == View.VISIBLE) {
+ hostViewController.onPause()
+ }
+ }
+ }
+ awaitCancellation()
+ } finally {
+ viewModel.setBouncerViewDelegate(null)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
new file mode 100644
index 000000000000..9ad52117bfc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.ui.viewmodel
+
+import android.view.View
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.data.BouncerViewDelegate
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
+
+/** Models UI state for the lock screen bouncer; handles user input. */
+class KeyguardBouncerViewModel
+@Inject
+constructor(
+ private val view: BouncerView,
+ private val interactor: BouncerInteractor,
+) {
+ /** Observe on bouncer expansion amount. */
+ val bouncerExpansionAmount: Flow<Float> = interactor.expansionAmount
+
+ /** Observe on bouncer visibility. */
+ val isBouncerVisible: Flow<Boolean> = interactor.isVisible
+
+ /** Observe whether bouncer is showing. */
+ val show: Flow<KeyguardBouncerModel> = interactor.show
+
+ /** Observe bouncer prompt when bouncer is showing. */
+ val showPromptReason: Flow<Int> = interactor.show.map { it.promptReason }
+
+ /** Observe bouncer error message when bouncer is showing. */
+ val showBouncerErrorMessage: Flow<CharSequence> =
+ interactor.show.map { it.errorMessage }.filterNotNull()
+
+ /** Observe visible expansion when bouncer is showing. */
+ val showWithFullExpansion: Flow<KeyguardBouncerModel> =
+ interactor.show.filter { it.expansionAmount == EXPANSION_VISIBLE }
+
+ /** Observe whether bouncer is hiding. */
+ val hide: Flow<Unit> = interactor.hide
+
+ /** Observe whether bouncer is starting to hide. */
+ val startingToHide: Flow<Unit> = interactor.startingToHide
+
+ /** Observe whether we want to set the dismiss action to the bouncer. */
+ val setDismissAction: Flow<BouncerCallbackActionsModel> = interactor.onDismissAction
+
+ /** Observe whether we want to start the disappear animation. */
+ val startDisappearAnimation: Flow<Runnable> = interactor.startingDisappearAnimation
+
+ /** Observe whether we want to update keyguard position. */
+ val keyguardPosition: Flow<Float> = interactor.keyguardPosition
+
+ /** Observe whether we want to update resources. */
+ val updateResources: Flow<Boolean> = interactor.resourceUpdateRequests
+
+ /** Observe whether we want to set a keyguard message when the bouncer shows. */
+ val bouncerShowMessage: Flow<BouncerShowMessageModel> = interactor.showMessage
+
+ /** Observe whether keyguard is authenticated already. */
+ val keyguardAuthenticated: Flow<Boolean> = interactor.keyguardAuthenticated
+
+ /** Observe whether screen is turned off. */
+ val screenTurnedOff: Flow<Unit> = interactor.screenTurnedOff
+
+ /** Notify that view visibility has changed. */
+ fun notifyBouncerVisibilityHasChanged(visibility: Int) {
+ return interactor.notifyBouncerVisibilityHasChanged(visibility)
+ }
+ /** Observe whether we want to update resources. */
+ fun notifyUpdateResources() {
+ interactor.notifyUpdatedResources()
+ }
+
+ /** Notify that keyguard authenticated was handled */
+ fun notifyKeyguardAuthenticated() {
+ interactor.notifyKeyguardAuthenticatedHandled()
+ }
+
+ /** Observe whether back button is enabled. */
+ fun observeOnIsBackButtonEnabled(systemUiVisibility: () -> Int): Flow<Int> {
+ return interactor.isBackButtonEnabled.map { enabled ->
+ var vis: Int = systemUiVisibility()
+ vis =
+ if (enabled) {
+ vis and View.STATUS_BAR_DISABLE_BACK.inv()
+ } else {
+ vis or View.STATUS_BAR_DISABLE_BACK
+ }
+ vis
+ }
+ }
+
+ /** Set an abstraction that will hold reference to the ui delegate for the bouncer view. */
+ fun setBouncerViewDelegate(delegate: BouncerViewDelegate?) {
+ view.delegate = delegate
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BiometricMessagesLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/BiometricMessagesLog.java
new file mode 100644
index 000000000000..7f1ad6d20c16
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BiometricMessagesLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+import javax.inject.Qualifier;
+
+/**
+ * A {@link com.android.systemui.log.LogBuffer} for BiometricMessages processing such as
+ * {@link com.android.systemui.biometrics.FaceHelpMessageDeferral}
+ */
+@Qualifier
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+public @interface BiometricMessagesLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt
new file mode 100644
index 000000000000..4887b6a14658
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/BluetoothLog.kt
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger
+
+import javax.inject.Qualifier
+
+/** A [com.android.systemui.log.LogBuffer] for bluetooth. */
+@Qualifier @MustBeDocumented @Retention(AnnotationRetention.RUNTIME) annotation class BluetoothLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
index 323ee21953ea..b551125fccc7 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/KeyguardUpdateMonitorLog.kt
@@ -1,4 +1,9 @@
package com.android.systemui.log.dagger
+import javax.inject.Qualifier
+
/** A [com.android.systemui.log.LogBuffer] for KeyguardUpdateMonitor. */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
annotation class KeyguardUpdateMonitorLog
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index c2a87649adef..29e2c1cd8900 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -76,6 +76,14 @@ public class LogModule {
return factory.create("NotifInterruptLog", 100);
}
+ /** Provides a logging buffer for notification rendering events. */
+ @Provides
+ @SysUISingleton
+ @NotificationRenderLog
+ public static LogBuffer provideNotificationRenderLogBuffer(LogBufferFactory factory) {
+ return factory.create("NotifRenderLog", 100);
+ }
+
/** Provides a logging buffer for all logs for lockscreen to shade transition events. */
@Provides
@SysUISingleton
@@ -287,6 +295,17 @@ public class LogModule {
}
/**
+ * Provides a {@link LogBuffer} for use by
+ * {@link com.android.systemui.biometrics.FaceHelpMessageDeferral}.
+ */
+ @Provides
+ @SysUISingleton
+ @BiometricMessagesLog
+ public static LogBuffer provideBiometricMessagesLogBuffer(LogBufferFactory factory) {
+ return factory.create("BiometricMessagesLog", 150);
+ }
+
+ /**
* Provides a {@link LogBuffer} for use by the status bar network controller.
*/
@Provides
@@ -305,4 +324,14 @@ public class LogModule {
public static LogBuffer provideKeyguardUpdateMonitorLogBuffer(LogBufferFactory factory) {
return factory.create("KeyguardUpdateMonitorLog", 200);
}
+
+ /**
+ * Provides a {@link LogBuffer} for bluetooth-related logs.
+ */
+ @Provides
+ @SysUISingleton
+ @BluetoothLog
+ public static LogBuffer providerBluetoothLogBuffer(LogBufferFactory factory) {
+ return factory.create("BluetoothLog", 50);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationRenderLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationRenderLog.java
new file mode 100644
index 000000000000..8c8753a07339
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationRenderLog.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import com.android.systemui.log.LogBuffer;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+/** A {@link LogBuffer} for notification rendering logging. */
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface NotificationRenderLog {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java
index f03fbcba41b7..b237f2d74483 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/StatusBarConnectivityLog.java
@@ -19,7 +19,6 @@ package com.android.systemui.log.dagger;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.systemui.log.LogBuffer;
-import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -27,7 +26,7 @@ import java.lang.annotation.Retention;
import javax.inject.Qualifier;
/**
- * A {@link LogBuffer} for events processed by {@link ConnectivityInfoProcessor}
+ * A {@link LogBuffer} for status bar connectivity events.
*/
@Qualifier
@Documented
diff --git a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
index d0826553ad2c..556560c3534c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/ColorSchemeTransition.kt
@@ -34,7 +34,7 @@ import com.android.systemui.monet.ColorScheme
* is triggered.
*/
interface ColorTransition {
- fun updateColorScheme(scheme: ColorScheme?)
+ fun updateColorScheme(scheme: ColorScheme?): Boolean
}
/**
@@ -64,14 +64,16 @@ open class AnimatingColorTransition(
applyColor(currentColor)
}
- override fun updateColorScheme(scheme: ColorScheme?) {
+ override fun updateColorScheme(scheme: ColorScheme?): Boolean {
val newTargetColor = if (scheme == null) defaultColor else extractColor(scheme)
if (newTargetColor != targetColor) {
sourceColor = currentColor
targetColor = newTargetColor
valueAnimator.cancel()
valueAnimator.start()
+ return true
}
+ return false
}
init {
@@ -198,8 +200,10 @@ class ColorSchemeTransition internal constructor(
return Utils.getColorAttr(context, id).defaultColor
}
- fun updateColorScheme(colorScheme: ColorScheme?) {
- colorTransitions.forEach { it.updateColorScheme(colorScheme) }
+ fun updateColorScheme(colorScheme: ColorScheme?): Boolean {
+ var anyChanged = false
+ colorTransitions.forEach { anyChanged = it.updateColorScheme(colorScheme) || anyChanged }
colorScheme?.let { mediaViewHolder.gutsViewHolder.colorScheme = colorScheme }
+ return anyChanged
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index cc77ed15b5f0..2cd564ff4e32 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -1,5 +1,6 @@
package com.android.systemui.media
+import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.content.res.ColorStateList
@@ -148,32 +149,6 @@ class MediaCarouselController @Inject constructor(
}
}
}
-
- companion object {
- private const val SQUISHINESS_SCALE_START = 0.5
- private const val SQUISHINESS_SCALE_FACTOR = 0.5
- private fun getSquishinessScale(squishinessFraction: Float): Double {
- return SQUISHINESS_SCALE_START + SQUISHINESS_SCALE_FACTOR * squishinessFraction
- }
- }
-
- var squishinessFraction: Float = 1f
- set(value) {
- if (field == value) {
- return
- }
- field = value
-
- val scale = getSquishinessScale(field)
- for (mediaPlayer in MediaPlayerData.players()) {
- mediaPlayer.mediaViewHolder?.let {
- it.player.bottom = it.player.top + (scale * it.player.measuredHeight).toInt()
- } ?: mediaPlayer.recommendationViewHolder?.let {
- it.recommendations.bottom = it.recommendations.top +
- (scale * it.recommendations.measuredHeight).toInt()
- }
- }
- }
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
// System font changes should only happen when UMO is offscreen or a flicker may occur
@@ -202,6 +177,7 @@ class MediaCarouselController @Inject constructor(
* It will be called when the container is out of view.
*/
lateinit var updateUserVisibility: () -> Unit
+ lateinit var updateHostVisibility: () -> Unit
private val isReorderingAllowed: Boolean
get() = visualStabilityProvider.isReorderingAllowed
@@ -225,7 +201,13 @@ class MediaCarouselController @Inject constructor(
reorderAllPlayers(previousVisiblePlayerKey = null)
}
- keysNeedRemoval.forEach { removePlayer(it) }
+ keysNeedRemoval.forEach {
+ removePlayer(it)
+ }
+ if (keysNeedRemoval.size > 0) {
+ // Carousel visibility may need to be updated after late removals
+ updateHostVisibility()
+ }
keysNeedRemoval.clear()
// Update user visibility so that no extra impression will be logged when
@@ -247,6 +229,7 @@ class MediaCarouselController @Inject constructor(
receivedSmartspaceCardLatency: Int,
isSsReactivated: Boolean
) {
+ debugLogger.logMediaLoaded(key)
if (addOrUpdatePlayer(key, oldKey, data, isSsReactivated)) {
// Log card received if a new resumable media card is added
MediaPlayerData.getMediaPlayer(key)?.let {
@@ -315,7 +298,7 @@ class MediaCarouselController @Inject constructor(
data: SmartspaceMediaData,
shouldPrioritize: Boolean
) {
- if (DEBUG) Log.d(TAG, "Loading Smartspace media update")
+ debugLogger.logRecommendationLoaded(key)
// Log the case where the hidden media carousel with the existed inactive resume
// media is shown by the Smartspace signal.
if (data.isActive) {
@@ -370,13 +353,21 @@ class MediaCarouselController @Inject constructor(
}
override fun onMediaDataRemoved(key: String) {
+ debugLogger.logMediaRemoved(key)
removePlayer(key)
}
override fun onSmartspaceMediaDataRemoved(key: String, immediately: Boolean) {
- if (DEBUG) Log.d(TAG, "My Smartspace media removal request is received")
+ debugLogger.logRecommendationRemoved(key, immediately)
if (immediately || isReorderingAllowed) {
- onMediaDataRemoved(key)
+ removePlayer(key)
+ if (!immediately) {
+ // Although it wasn't requested, we were able to process the removal
+ // immediately since reordering is allowed. So, notify hosts to update
+ if (this@MediaCarouselController::updateHostVisibility.isInitialized) {
+ updateHostVisibility()
+ }
+ }
} else {
keysNeedRemoval.add(key)
}
@@ -458,7 +449,7 @@ class MediaCarouselController @Inject constructor(
val existingPlayer = MediaPlayerData.getMediaPlayer(key)
val curVisibleMediaKey = MediaPlayerData.playerKeys()
.elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)
- val isCurVisibleMediaPlaying = MediaPlayerData.getMediaData(curVisibleMediaKey)?.isPlaying
+ val isCurVisibleMediaPlaying = curVisibleMediaKey?.data?.isPlaying
if (existingPlayer == null) {
val newPlayer = mediaControlPanelFactory.get()
newPlayer.attachPlayer(MediaViewHolder.create(
@@ -929,6 +920,11 @@ class MediaCarouselController @Inject constructor(
mediaManager.onSwipeToDismiss()
}
+ fun getCurrentVisibleMediaContentIntent(): PendingIntent? {
+ return MediaPlayerData.playerKeys()
+ .elementAtOrNull(mediaCarouselScrollHandler.visibleMediaIndex)?.data?.clickIntent
+ }
+
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.apply {
println("keysNeedRemoval: $keysNeedRemoval")
@@ -1046,15 +1042,6 @@ internal object MediaPlayerData {
}
}
- fun getMediaData(mediaSortKey: MediaSortKey?): MediaData? {
- mediaData.forEach { (key, value) ->
- if (value == mediaSortKey) {
- return mediaData[key]?.data
- }
- }
- return null
- }
-
fun getMediaPlayer(key: String): MediaControlPanel? {
return mediaData.get(key)?.let { mediaPlayers.get(it) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselControllerLogger.kt
index 04ebd5a71137..b1018f9544c0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselControllerLogger.kt
@@ -40,6 +40,37 @@ class MediaCarouselControllerLogger @Inject constructor(
"Removing control panel for $str1 from map without calling #onDestroy"
}
)
+
+ fun logMediaLoaded(key: String) = buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = key },
+ { "add player $str1" }
+ )
+
+ fun logMediaRemoved(key: String) = buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = key },
+ { "removing player $str1" }
+ )
+
+ fun logRecommendationLoaded(key: String) = buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { str1 = key },
+ { "add recommendation $str1" }
+ )
+
+ fun logRecommendationRemoved(key: String, immediately: Boolean) = buffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = key
+ bool1 = immediately
+ },
+ { "removing recommendation $str1, immediate=$bool1" }
+ )
}
private const val TAG = "MediaCarouselCtlrLog"
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index b02393b4f73a..759795f84963 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -741,10 +741,14 @@ public class MediaControlPanel {
}
mArtworkBoundId = reqId;
+ // Transition Colors to current color scheme
+ boolean colorSchemeChanged = mColorSchemeTransition.updateColorScheme(colorScheme);
+
// Bind the album view to the artwork or a transition drawable
ImageView albumView = mMediaViewHolder.getAlbumView();
albumView.setPadding(0, 0, 0, 0);
- if (updateBackground || (!mIsArtworkBound && isArtworkBound)) {
+ if (updateBackground || colorSchemeChanged
+ || (!mIsArtworkBound && isArtworkBound)) {
if (mPrevArtwork == null) {
albumView.setImageDrawable(artwork);
} else {
@@ -767,9 +771,6 @@ public class MediaControlPanel {
mIsArtworkBound = isArtworkBound;
}
- // Transition Colors to current color scheme
- mColorSchemeTransition.updateColorScheme(colorScheme);
-
// App icon - use notification icon
ImageView appIconView = mMediaViewHolder.getAppIcon();
appIconView.clearColorFilter();
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index c48271e0348a..896fb4765c57 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -1322,6 +1322,7 @@ class MediaDataManager(
println("externalListeners: ${mediaDataFilter.listeners}")
println("mediaEntries: $mediaEntries")
println("useMediaResumption: $useMediaResumption")
+ println("allowMediaRecommendations: $allowMediaRecommendations")
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 6baf6e137ab4..e0b6d1f9de7b 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -546,6 +546,11 @@ class MediaHierarchyManager @Inject constructor(
mediaCarouselController.updateUserVisibility = {
mediaCarouselController.mediaCarouselScrollHandler.visibleToUser = isVisibleToUser()
}
+ mediaCarouselController.updateHostVisibility = {
+ mediaHosts.forEach {
+ it?.updateViewVisibility()
+ }
+ }
panelEventsEvents.registerListener(object : NotifPanelEvents.Listener {
override fun onExpandImmediateChanged(isExpandImmediateEnabled: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
index de2b5c9a4739..864592238b73 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHost.kt
@@ -167,7 +167,11 @@ class MediaHost constructor(
}
}
- private fun updateViewVisibility() {
+ /**
+ * Updates this host's state based on the current media data's status, and invokes listeners if
+ * the visibility has changed
+ */
+ fun updateViewVisibility() {
state.visible = if (showsOnlyActiveMedia) {
mediaDataManager.hasActiveMediaOrRecommendation()
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
index 0f1ee31e066e..c6bd777fbd7a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaProjectionAppSelectorActivity.kt
@@ -24,22 +24,45 @@ import android.os.Bundle
import android.os.IBinder
import android.os.ResultReceiver
import android.os.UserHandle
-import android.widget.ImageView
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.android.internal.annotations.VisibleForTesting
import com.android.internal.app.ChooserActivity
import com.android.internal.app.ResolverListController
import com.android.internal.app.chooser.NotSelectableTargetInfo
import com.android.internal.app.chooser.TargetInfo
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.systemui.util.AsyncActivityLauncher
import com.android.systemui.R
-import com.android.internal.R as AndroidR
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorView
+import com.android.systemui.mediaprojection.appselector.data.RecentTask
+import com.android.systemui.mediaprojection.appselector.view.RecentTasksAdapter
+import com.android.systemui.mediaprojection.appselector.view.RecentTasksAdapter.RecentTaskClickListener
+import com.android.systemui.util.AsyncActivityLauncher
+import com.android.systemui.util.recycler.HorizontalSpacerItemDecoration
+import javax.inject.Inject
-class MediaProjectionAppSelectorActivity constructor(
+class MediaProjectionAppSelectorActivity(
private val activityLauncher: AsyncActivityLauncher,
+ private val controller: MediaProjectionAppSelectorController,
+ private val recentTasksAdapterFactory: RecentTasksAdapter.Factory,
/** This is used to override the dependency in a screenshot test */
@VisibleForTesting
- private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)? = null
-) : ChooserActivity() {
+ private val listControllerFactory: ((userHandle: UserHandle) -> ResolverListController)?
+) : ChooserActivity(), MediaProjectionAppSelectorView, RecentTaskClickListener {
+
+ @Inject
+ constructor(
+ activityLauncher: AsyncActivityLauncher,
+ controller: MediaProjectionAppSelectorController,
+ recentTasksAdapterFactory: RecentTasksAdapter.Factory,
+ ) : this(activityLauncher, controller, recentTasksAdapterFactory, null)
+
+ private var recentsRoot: ViewGroup? = null
+ private var recentsProgress: View? = null
+ private var recentsRecycler: RecyclerView? = null
override fun getLayoutResource() =
R.layout.media_projection_app_selector
@@ -52,10 +75,30 @@ class MediaProjectionAppSelectorActivity constructor(
// TODO(b/240939253): update copies
val title = getString(R.string.media_projection_dialog_service_title)
intent.putExtra(Intent.EXTRA_TITLE, title)
-
super.onCreate(bundle)
+ controller.init(this)
+ }
- requireViewById<ImageView>(AndroidR.id.icon).setImageResource(R.drawable.ic_present_to_all)
+ private fun createRecentsView(parent: ViewGroup): ViewGroup {
+ val recentsRoot = LayoutInflater.from(this)
+ .inflate(R.layout.media_projection_recent_tasks, parent,
+ /* attachToRoot= */ false) as ViewGroup
+
+ recentsProgress = recentsRoot.requireViewById(R.id.media_projection_recent_tasks_loader)
+ recentsRecycler = recentsRoot.requireViewById(R.id.media_projection_recent_tasks_recycler)
+ recentsRecycler?.layoutManager = LinearLayoutManager(
+ this, LinearLayoutManager.HORIZONTAL,
+ /* reverseLayout= */false
+ )
+
+ val itemDecoration = HorizontalSpacerItemDecoration(
+ resources.getDimensionPixelOffset(
+ R.dimen.media_projection_app_selector_recents_padding
+ )
+ )
+ recentsRecycler?.addItemDecoration(itemDecoration)
+
+ return recentsRoot
}
override fun appliedThemeResId(): Int =
@@ -108,6 +151,7 @@ class MediaProjectionAppSelectorActivity constructor(
override fun onDestroy() {
activityLauncher.destroy()
+ controller.destroy()
super.onDestroy()
}
@@ -115,6 +159,27 @@ class MediaProjectionAppSelectorActivity constructor(
// do nothing
}
+ override fun bind(recentTasks: List<RecentTask>) {
+ val recents = recentsRoot ?: return
+ val progress = recentsProgress ?: return
+ val recycler = recentsRecycler ?: return
+
+ if (recentTasks.isEmpty()) {
+ recents.visibility = View.GONE
+ return
+ }
+
+ progress.visibility = View.GONE
+ recycler.visibility = View.VISIBLE
+ recents.visibility = View.VISIBLE
+
+ recycler.adapter = recentTasksAdapterFactory.create(recentTasks, this)
+ }
+
+ override fun onRecentClicked(task: RecentTask, view: View) {
+ // TODO(b/240924732) Handle clicking on a recent task
+ }
+
private fun onTargetActivityLaunched(launchToken: IBinder) {
if (intent.hasExtra(EXTRA_CAPTURE_REGION_RESULT_RECEIVER)) {
// The client requested to return the result in the result receiver instead of
@@ -145,6 +210,14 @@ class MediaProjectionAppSelectorActivity constructor(
override fun shouldGetOnlyDefaultActivities() = false
+ // TODO(b/240924732) flip the flag when the recents selector is ready
+ override fun shouldShowContentPreview() = false
+
+ override fun createContentPreviewView(parent: ViewGroup): ViewGroup =
+ recentsRoot ?: createRecentsView(parent).also {
+ recentsRoot = it
+ }
+
companion object {
/**
* When EXTRA_CAPTURE_REGION_RESULT_RECEIVER is passed as intent extra
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 8e4ca5a98778..731e348edca4 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -380,7 +380,7 @@ class MediaViewController @Inject constructor(
type: TYPE
) = traceSection("MediaViewController#attach") {
updateMediaViewControllerType(type)
- logger.logMediaLocation("attach", currentStartLocation, currentEndLocation)
+ logger.logMediaLocation("attach $type", currentStartLocation, currentEndLocation)
this.transitionLayout = transitionLayout
layoutController.attach(transitionLayout)
if (currentEndLocation == -1) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt b/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
index 88f6f3dd9d0e..6bc94cd5f525 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SquigglyProgress.kt
@@ -60,6 +60,8 @@ class SquigglyProgress : Drawable() {
linePaint.strokeWidth = value
}
+ // Enables a transition region where the amplitude
+ // of the wave is reduced linearly across it.
var transitionEnabled = true
set(value) {
field = value
@@ -116,44 +118,40 @@ class SquigglyProgress : Drawable() {
}
val progress = level / 10_000f
- val totalProgressPx = bounds.width() * progress
- val waveProgressPx = bounds.width() * (
+ val totalWidth = bounds.width().toFloat()
+ val totalProgressPx = totalWidth * progress
+ val waveProgressPx = totalWidth * (
if (!transitionEnabled || progress > matchedWaveEndpoint) progress else
lerp(minWaveEndpoint, matchedWaveEndpoint, lerpInv(0f, matchedWaveEndpoint, progress)))
// Build Wiggly Path
- val waveStart = -phaseOffset
- val waveEnd = waveProgressPx
- val transitionLength = if (transitionEnabled) transitionPeriods * waveLength else 0.01f
+ val waveStart = -phaseOffset - waveLength / 2f
+ val waveEnd = if (transitionEnabled) totalWidth else waveProgressPx
// helper function, computes amplitude for wave segment
val computeAmplitude: (Float, Float) -> Float = { x, sign ->
- sign * heightFraction * lineAmplitude *
- lerpInvSat(waveEnd, waveEnd - transitionLength, x)
+ if (transitionEnabled) {
+ val length = transitionPeriods * waveLength
+ val coeff = lerpInvSat(
+ waveProgressPx + length / 2f,
+ waveProgressPx - length / 2f,
+ x)
+ sign * heightFraction * lineAmplitude * coeff
+ } else {
+ sign * heightFraction * lineAmplitude
+ }
}
- var currentX = waveEnd
- var waveSign = if (phaseOffset < waveLength / 2) 1f else -1f
+ // Reset path object to the start
path.rewind()
+ path.moveTo(waveStart, 0f)
- // Draw flat line from end to wave endpoint
- path.moveTo(bounds.width().toFloat(), 0f)
- path.lineTo(waveEnd, 0f)
-
- // First wave has shortened wavelength
- // approx quarter wave gets us to first wave peak
- // shouldn't be big enough to notice it's not a sin wave
- currentX -= phaseOffset % (waveLength / 2)
- val controlRatio = 0.25f
+ // Build the wave, incrementing by half the wavelength each time
+ var currentX = waveStart
+ var waveSign = 1f
var currentAmp = computeAmplitude(currentX, waveSign)
- path.cubicTo(
- waveEnd, currentAmp * controlRatio,
- lerp(currentX, waveEnd, controlRatio), currentAmp,
- currentX, currentAmp)
-
- // Other waves have full wavelength
- val dist = -1 * waveLength / 2f
- while (currentX > waveStart) {
+ val dist = waveLength / 2f
+ while (currentX < waveEnd) {
waveSign = -waveSign
val nextX = currentX + dist
val midX = currentX + dist / 2
@@ -166,34 +164,35 @@ class SquigglyProgress : Drawable() {
currentX = nextX
}
- // Draw path; clip to progress position
+ // translate to the start position of the progress bar for all draw commands
+ val clipTop = lineAmplitude + strokeWidth
canvas.save()
canvas.translate(bounds.left.toFloat(), bounds.centerY().toFloat())
- canvas.clipRect(
- 0f,
- -lineAmplitude - strokeWidth,
- totalProgressPx,
- lineAmplitude + strokeWidth)
- canvas.drawPath(path, wavePaint)
- canvas.restore()
- // Draw path; clip between progression position & far edge
+ // Draw path up to progress position
canvas.save()
- canvas.translate(bounds.left.toFloat(), bounds.centerY().toFloat())
- canvas.clipRect(
- totalProgressPx,
- -lineAmplitude - strokeWidth,
- bounds.width().toFloat(),
- lineAmplitude + strokeWidth)
- canvas.drawPath(path, linePaint)
+ canvas.clipRect(0f, -1f * clipTop, totalProgressPx, clipTop)
+ canvas.drawPath(path, wavePaint)
canvas.restore()
+ if (transitionEnabled) {
+ // If there's a smooth transition, we draw the rest of the
+ // path in a different color (using different clip params)
+ canvas.save()
+ canvas.clipRect(totalProgressPx, -1f * clipTop, totalWidth, clipTop)
+ canvas.drawPath(path, linePaint)
+ canvas.restore()
+ } else {
+ // No transition, just draw a flat line to the end of the region.
+ // The discontinuity is hidden by the progress bar thumb shape.
+ canvas.drawLine(totalProgressPx, 0f, totalWidth, 0f, linePaint)
+ }
+
// Draw round line cap at the beginning of the wave
- val startAmp = cos(abs(waveEnd - phaseOffset) / waveLength * TWO_PI)
- canvas.drawPoint(
- bounds.left.toFloat(),
- bounds.centerY() + startAmp * lineAmplitude * heightFraction,
- wavePaint)
+ val startAmp = cos(abs(waveStart) / waveLength * TWO_PI)
+ canvas.drawPoint(0f, startAmp * lineAmplitude * heightFraction, wavePaint)
+
+ canvas.restore()
}
override fun getOpacity(): Int {
@@ -233,4 +232,4 @@ class SquigglyProgress : Drawable() {
linePaint.color = ColorUtils.setAlphaComponent(tintColor,
(DISABLED_ALPHA * (alpha / 255f)).toInt())
}
-} \ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt
index 969699834024..185b4fca87d0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaProjectionModule.kt
@@ -17,13 +17,29 @@
package com.android.systemui.media.dagger
import android.app.Activity
+import android.content.ComponentName
+import android.content.Context
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.MediaProjectionAppSelectorActivity
-import com.android.systemui.util.AsyncActivityLauncher
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorController
+import com.android.systemui.mediaprojection.appselector.data.ActivityTaskManagerThumbnailLoader
+import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
+import com.android.systemui.mediaprojection.appselector.data.IconLoaderLibAppIconLoader
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
+import com.android.systemui.mediaprojection.appselector.data.ShellRecentTaskListProvider
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.SupervisorJob
+import javax.inject.Qualifier
+
+@Qualifier
+@Retention(AnnotationRetention.BINARY)
+annotation class MediaProjectionAppSelector
@Module
abstract class MediaProjectionModule {
@@ -31,17 +47,43 @@ abstract class MediaProjectionModule {
@Binds
@IntoMap
@ClassKey(MediaProjectionAppSelectorActivity::class)
- abstract fun bindMediaProjectionAppSelectorActivity(
- activity: MediaProjectionAppSelectorActivity): Activity
+ abstract fun provideMediaProjectionAppSelectorActivity(
+ activity: MediaProjectionAppSelectorActivity
+ ): Activity
+
+ @Binds
+ abstract fun bindRecentTaskThumbnailLoader(
+ impl: ActivityTaskManagerThumbnailLoader
+ ): RecentTaskThumbnailLoader
+
+ @Binds
+ abstract fun bindRecentTaskListProvider(
+ impl: ShellRecentTaskListProvider
+ ): RecentTaskListProvider
+
+ @Binds
+ abstract fun bindAppIconLoader(impl: IconLoaderLibAppIconLoader): AppIconLoader
companion object {
@Provides
- fun provideMediaProjectionAppSelectorActivity(
- activityLauncher: AsyncActivityLauncher
- ): MediaProjectionAppSelectorActivity {
- return MediaProjectionAppSelectorActivity(
- activityLauncher
+ fun provideController(
+ recentTaskListProvider: RecentTaskListProvider,
+ context: Context,
+ @MediaProjectionAppSelector scope: CoroutineScope
+ ): MediaProjectionAppSelectorController {
+ val appSelectorComponentName =
+ ComponentName(context, MediaProjectionAppSelectorActivity::class.java)
+
+ return MediaProjectionAppSelectorController(
+ recentTaskListProvider,
+ scope,
+ appSelectorComponentName
)
}
+
+ @MediaProjectionAppSelector
+ @Provides
+ fun provideCoroutineScope(@Application applicationScope: CoroutineScope): CoroutineScope =
+ CoroutineScope(applicationScope.coroutineContext + SupervisorJob())
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index b39770d302be..a9e1a4d325d2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -99,6 +99,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
private Button mStopButton;
private Button mAppButton;
private int mListMaxHeight;
+ private int mItemHeight;
private WallpaperColors mWallpaperColors;
private Executor mExecutor;
private boolean mShouldLaunchLeBroadcastDialog;
@@ -106,10 +107,12 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
MediaOutputBaseAdapter mAdapter;
private final ViewTreeObserver.OnGlobalLayoutListener mDeviceListLayoutListener = () -> {
+ ViewGroup.LayoutParams params = mDeviceListLayout.getLayoutParams();
+ int totalItemsHeight = mAdapter.getItemCount() * mItemHeight;
+ int correctHeight = Math.min(totalItemsHeight, mListMaxHeight);
// Set max height for list
- if (mDeviceListLayout.getHeight() > mListMaxHeight) {
- ViewGroup.LayoutParams params = mDeviceListLayout.getLayoutParams();
- params.height = mListMaxHeight;
+ if (correctHeight != params.height) {
+ params.height = correctHeight;
mDeviceListLayout.setLayoutParams(params);
}
};
@@ -212,6 +215,8 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
mLayoutManager = new LayoutManagerWrapper(mContext);
mListMaxHeight = context.getResources().getDimensionPixelSize(
R.dimen.media_output_dialog_list_max_height);
+ mItemHeight = context.getResources().getDimensionPixelSize(
+ R.dimen.media_output_dialog_list_item_height);
mExecutor = Executors.newSingleThreadExecutor();
}
@@ -246,8 +251,10 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
mDeviceListLayout.getViewTreeObserver().addOnGlobalLayoutListener(
mDeviceListLayoutListener);
// Init device list
+ mLayoutManager.setAutoMeasureEnabled(true);
mDevicesRecyclerView.setLayoutManager(mLayoutManager);
mDevicesRecyclerView.setAdapter(mAdapter);
+ mDevicesRecyclerView.setHasFixedSize(false);
// Init header icon
mHeaderIcon.setOnClickListener(v -> onHeaderIconClick());
// Init bottom buttons
@@ -310,6 +317,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
if (icon.getType() != Icon.TYPE_BITMAP && icon.getType() != Icon.TYPE_ADAPTIVE_BITMAP) {
// icon doesn't support getBitmap, use default value for color scheme
updateButtonBackgroundColorFilter();
+ updateDialogBackgroundColor();
} else {
Configuration config = mContext.getResources().getConfiguration();
int currentNightMode = config.uiMode & Configuration.UI_MODE_NIGHT_MASK;
@@ -319,11 +327,14 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
if (colorSetUpdated) {
mAdapter.updateColorScheme(wallpaperColors, isDarkThemeOn);
updateButtonBackgroundColorFilter();
+ updateDialogBackgroundColor();
}
}
mHeaderIcon.setVisibility(View.VISIBLE);
mHeaderIcon.setImageIcon(icon);
} else {
+ updateButtonBackgroundColorFilter();
+ updateDialogBackgroundColor();
mHeaderIcon.setVisibility(View.GONE);
}
if (appSourceIcon != null) {
@@ -381,11 +392,16 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
private void updateButtonBackgroundColorFilter() {
ColorFilter buttonColorFilter = new PorterDuffColorFilter(
- mAdapter.getController().getColorButtonBackground(),
+ mMediaOutputController.getColorButtonBackground(),
PorterDuff.Mode.SRC_IN);
mDoneButton.getBackground().setColorFilter(buttonColorFilter);
mStopButton.getBackground().setColorFilter(buttonColorFilter);
- mDoneButton.setTextColor(mAdapter.getController().getColorPositiveButtonText());
+ mDoneButton.setTextColor(mMediaOutputController.getColorPositiveButtonText());
+ }
+
+ private void updateDialogBackgroundColor() {
+ getDialogView().getBackground().setTint(mMediaOutputController.getColorDialogBackground());
+ mDeviceListLayout.setBackgroundColor(mMediaOutputController.getColorDialogBackground());
}
private Drawable resizeDrawable(Drawable drawable, int size) {
@@ -500,7 +516,7 @@ public abstract class MediaOutputBaseDialog extends SystemUIDialog implements
abstract int getStopButtonVisibility();
public CharSequence getStopButtonText() {
- return mContext.getText(R.string.media_output_dialog_button_stop_casting);
+ return mContext.getText(R.string.keyboard_key_media_stop);
}
public void onStopButtonClick() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 96817c9bf32d..19b401d80600 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -125,14 +125,18 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
private final NearbyMediaDevicesManager mNearbyMediaDevicesManager;
private final Map<String, Integer> mNearbyDeviceInfoMap = new ConcurrentHashMap<>();
- private boolean mIsRefreshing = false;
- private boolean mNeedRefresh = false;
+ @VisibleForTesting
+ boolean mIsRefreshing = false;
+ @VisibleForTesting
+ boolean mNeedRefresh = false;
private MediaController mMediaController;
@VisibleForTesting
Callback mCallback;
@VisibleForTesting
LocalMediaManager mLocalMediaManager;
+ @VisibleForTesting
private MediaOutputMetricLogger mMetricLogger;
+ private int mCurrentState;
private int mColorItemContent;
private int mColorSeekbarProgress;
@@ -140,6 +144,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
private int mColorItemBackground;
private int mColorConnectedItemBackground;
private int mColorPositiveButtonText;
+ private int mColorDialogBackground;
private float mInactiveRadius;
private float mActiveRadius;
@@ -188,6 +193,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
R.dimen.media_output_dialog_background_radius);
mActiveRadius = mContext.getResources().getDimension(
R.dimen.media_output_dialog_active_background_radius);
+ mColorDialogBackground = Utils.getColorStateListDefaultColor(mContext,
+ R.color.media_dialog_background);
}
void start(@NonNull Callback cb) {
@@ -204,6 +211,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
if (TextUtils.equals(controller.getPackageName(), mPackageName)) {
mMediaController = controller;
mMediaController.unregisterCallback(mCb);
+ if (mMediaController.getPlaybackState() != null) {
+ mCurrentState = mMediaController.getPlaybackState().getState();
+ }
mMediaController.registerCallback(mCb);
break;
}
@@ -461,6 +471,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
mColorItemBackground = mCurrentColorScheme.getNeutral2().get(9); // N2-800
mColorConnectedItemBackground = mCurrentColorScheme.getAccent2().get(9); // A2-800
mColorPositiveButtonText = mCurrentColorScheme.getAccent2().get(9); // A2-800
+ mColorDialogBackground = mCurrentColorScheme.getNeutral1().get(10); // N1-900
} else {
mColorItemContent = mCurrentColorScheme.getAccent1().get(9); // A1-800
mColorSeekbarProgress = mCurrentColorScheme.getAccent1().get(4); // A1-300
@@ -468,6 +479,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
mColorItemBackground = mCurrentColorScheme.getAccent2().get(1); // A2-50
mColorConnectedItemBackground = mCurrentColorScheme.getAccent1().get(2); // A1-100
mColorPositiveButtonText = mCurrentColorScheme.getNeutral1().get(1); // N1-50
+ mColorDialogBackground = mCurrentColorScheme.getBackgroundColor();
}
}
@@ -487,6 +499,10 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
return mColorPositiveButtonText;
}
+ public int getColorDialogBackground() {
+ return mColorDialogBackground;
+ }
+
public int getColorItemContent() {
return mColorItemContent;
}
@@ -976,10 +992,16 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
@Override
public void onPlaybackStateChanged(PlaybackState playbackState) {
- final int state = playbackState.getState();
- if (state == PlaybackState.STATE_STOPPED || state == PlaybackState.STATE_PAUSED) {
+ final int newState =
+ playbackState == null ? PlaybackState.STATE_STOPPED : playbackState.getState();
+ if (mCurrentState == newState) {
+ return;
+ }
+
+ if (newState == PlaybackState.STATE_STOPPED) {
mCallback.onMediaStoppedOrPaused();
}
+ mCurrentState = newState;
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
index cb6f5a78ec30..fbd0079f8dc0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputDialog.java
@@ -108,7 +108,7 @@ public class MediaOutputDialog extends MediaOutputBaseDialog {
@Override
public CharSequence getStopButtonText() {
- int resId = R.string.media_output_dialog_button_stop_casting;
+ int resId = R.string.keyboard_key_media_stop;
if (isBroadcastSupported() && mMediaOutputController.isPlaying()
&& !mMediaOutputController.isBluetoothLeBroadcastEnabled()) {
resId = R.string.media_output_broadcast;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
index 6fe06e085556..7d3e82c9d47f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputMetricLogger.java
@@ -107,7 +107,8 @@ public class MediaOutputMetricLogger {
SysUiStatsLog.write(
SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__ADJUST_VOLUME,
- getInteractionDeviceType(source));
+ getInteractionDeviceType(source),
+ getLoggingPackageName());
}
/**
@@ -121,7 +122,8 @@ public class MediaOutputMetricLogger {
SysUiStatsLog.write(
SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__STOP_CASTING,
- SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE);
+ SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__TARGET__UNKNOWN_TYPE,
+ getLoggingPackageName());
}
/**
@@ -135,7 +137,8 @@ public class MediaOutputMetricLogger {
SysUiStatsLog.write(
SysUiStatsLog.MEDIAOUTPUT_OP_INTERACTION_REPORT,
SysUiStatsLog.MEDIA_OUTPUT_OP_INTERACTION_REPORTED__INTERACTION_TYPE__EXPANSION,
- getInteractionDeviceType(source));
+ getInteractionDeviceType(source),
+ getLoggingPackageName());
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
index dc1488eefc4e..53b4d434bfcb 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamSentinel.java
@@ -16,7 +16,7 @@
package com.android.systemui.media.dream;
-import static com.android.systemui.flags.Flags.MEDIA_DREAM_COMPLICATION;
+import static com.android.systemui.flags.Flags.DREAM_MEDIA_COMPLICATION;
import android.content.Context;
import android.util.Log;
@@ -77,7 +77,7 @@ public class MediaDreamSentinel extends CoreStartable {
public void onMediaDataLoaded(@NonNull String key, @Nullable String oldKey,
@NonNull MediaData data, boolean immediately, int receivedSmartspaceCardLatency,
boolean isSsReactivated) {
- if (!mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)) {
+ if (!mFeatureFlags.isEnabled(DREAM_MEDIA_COMPLICATION)) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
index aa10f7e2738f..b565f3c22f24 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLogger.kt
@@ -18,38 +18,57 @@ package com.android.systemui.media.taptotransfer.common
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogLevel
+import com.android.systemui.temporarydisplay.TemporaryViewLogger
/**
* A logger for media tap-to-transfer events.
*
- * @property deviceTypeTag the type of device triggering the logs -- "Sender" or "Receiver".
+ * @param deviceTypeTag the type of device triggering the logs -- "Sender" or "Receiver".
*/
class MediaTttLogger(
- private val deviceTypeTag: String,
- private val buffer: LogBuffer
-){
+ deviceTypeTag: String,
+ buffer: LogBuffer
+) : TemporaryViewLogger(buffer, BASE_TAG + deviceTypeTag) {
/** Logs a change in the chip state for the given [mediaRouteId]. */
- fun logStateChange(stateName: String, mediaRouteId: String) {
+ fun logStateChange(stateName: String, mediaRouteId: String, packageName: String?) {
buffer.log(
- BASE_TAG + deviceTypeTag,
+ tag,
LogLevel.DEBUG,
{
str1 = stateName
str2 = mediaRouteId
+ str3 = packageName
},
- { "State changed to $str1 for ID=$str2" }
+ { "State changed to $str1 for ID=$str2 package=$str3" }
)
}
- /** Logs that we removed the chip for the given [reason]. */
- fun logChipRemoval(reason: String) {
+ /** Logs that we couldn't find information for [packageName]. */
+ fun logPackageNotFound(packageName: String) {
buffer.log(
- BASE_TAG + deviceTypeTag,
+ tag,
LogLevel.DEBUG,
- { str1 = reason },
- { "Chip removed due to $str1" }
+ { str1 = packageName },
+ { "Package $str1 could not be found" }
)
}
+
+ /**
+ * Logs that a removal request has been bypassed (ignored).
+ *
+ * @param removalReason the reason that the chip removal was requested.
+ * @param bypassReason the reason that the request was bypassed.
+ */
+ fun logRemovalBypass(removalReason: String, bypassReason: String) {
+ buffer.log(
+ tag,
+ LogLevel.DEBUG,
+ {
+ str1 = removalReason
+ str2 = bypassReason
+ },
+ { "Chip removal requested due to $str1; however, removal was ignored because $str2" })
+ }
}
private const val BASE_TAG = "MediaTtt"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
new file mode 100644
index 000000000000..792ae7ca6049
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttUtils.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.media.taptotransfer.common
+
+import android.content.Context
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+import com.android.internal.widget.CachingIconView
+import com.android.settingslib.Utils
+import com.android.systemui.R
+
+/** Utility methods for media tap-to-transfer. */
+class MediaTttUtils {
+ companion object {
+ // Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
+ // UpdateMediaTapToTransferReceiverDisplayTest
+ const val WINDOW_TITLE = "Media Transfer Chip View"
+ const val WAKE_REASON = "MEDIA_TRANSFER_ACTIVATED"
+
+ /**
+ * Returns the information needed to display the icon.
+ *
+ * The information will either contain app name and icon of the app playing media, or a
+ * default name and icon if we can't find the app name/icon.
+ *
+ * @param appPackageName the package name of the app playing the media.
+ * @param logger the logger to use for any errors.
+ */
+ fun getIconInfoFromPackageName(
+ context: Context,
+ appPackageName: String?,
+ logger: MediaTttLogger
+ ): IconInfo {
+ if (appPackageName != null) {
+ try {
+ val contentDescription =
+ context.packageManager
+ .getApplicationInfo(
+ appPackageName,
+ PackageManager.ApplicationInfoFlags.of(0)
+ )
+ .loadLabel(context.packageManager)
+ .toString()
+ return IconInfo(
+ contentDescription,
+ drawable = context.packageManager.getApplicationIcon(appPackageName),
+ isAppIcon = true
+ )
+ } catch (e: PackageManager.NameNotFoundException) {
+ logger.logPackageNotFound(appPackageName)
+ }
+ }
+ return IconInfo(
+ contentDescription =
+ context.getString(R.string.media_output_dialog_unknown_launch_app_name),
+ drawable =
+ context.resources.getDrawable(R.drawable.ic_cast).apply {
+ this.setTint(
+ Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+ )
+ },
+ isAppIcon = false
+ )
+ }
+
+ /**
+ * Sets an icon to be displayed by the given view.
+ *
+ * @param iconSize the size in pixels that the icon should be. If null, the size of
+ * [appIconView] will not be adjusted.
+ */
+ fun setIcon(
+ appIconView: CachingIconView,
+ icon: Drawable,
+ iconContentDescription: CharSequence,
+ iconSize: Int? = null,
+ ) {
+ iconSize?.let { size ->
+ val lp = appIconView.layoutParams
+ lp.width = size
+ lp.height = size
+ appIconView.layoutParams = lp
+ }
+
+ appIconView.contentDescription = iconContentDescription
+ appIconView.setImageDrawable(icon)
+ }
+ }
+}
+
+data class IconInfo(
+ val contentDescription: String,
+ val drawable: Drawable,
+ /**
+ * True if [drawable] is the app's icon, and false if [drawable] is some generic default icon.
+ */
+ val isAppIcon: Boolean
+)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 5d6d683f93f6..dfd9e22c14b1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -35,6 +35,7 @@ import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
+import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
@@ -61,7 +62,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
powerManager: PowerManager,
@Main private val mainHandler: Handler,
private val uiEventLogger: MediaTttReceiverUiEventLogger,
-) : TemporaryViewDisplayController<ChipReceiverInfo>(
+) : TemporaryViewDisplayController<ChipReceiverInfo, MediaTttLogger>(
context,
logger,
windowManager,
@@ -70,6 +71,8 @@ class MediaTttChipControllerReceiver @Inject constructor(
configurationController,
powerManager,
R.layout.media_ttt_chip_receiver,
+ MediaTttUtils.WINDOW_TITLE,
+ MediaTttUtils.WAKE_REASON,
) {
@SuppressLint("WrongConstant") // We're allowed to use LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
override val windowLayoutParams = commonWindowLayoutParams.apply {
@@ -107,7 +110,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
) {
val chipState: ChipStateReceiver? = ChipStateReceiver.getReceiverStateFromId(displayState)
val stateName = chipState?.name ?: "Invalid"
- logger.logStateChange(stateName, routeInfo.id)
+ logger.logStateChange(stateName, routeInfo.id, routeInfo.clientPackageName)
if (chipState == null) {
Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState")
@@ -137,13 +140,26 @@ class MediaTttChipControllerReceiver @Inject constructor(
override fun updateView(newInfo: ChipReceiverInfo, currentView: ViewGroup) {
super.updateView(newInfo, currentView)
- val iconName = setIcon(
- currentView,
- newInfo.routeInfo.clientPackageName,
- newInfo.appIconDrawableOverride,
- newInfo.appNameOverride
+
+ val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
+ context, newInfo.routeInfo.clientPackageName, logger
+ )
+ val iconDrawable = newInfo.appIconDrawableOverride ?: iconInfo.drawable
+ val iconContentDescription = newInfo.appNameOverride ?: iconInfo.contentDescription
+ val iconSize = context.resources.getDimensionPixelSize(
+ if (iconInfo.isAppIcon) {
+ R.dimen.media_ttt_icon_size_receiver
+ } else {
+ R.dimen.media_ttt_generic_icon_size_receiver
+ }
+ )
+
+ MediaTttUtils.setIcon(
+ currentView.requireViewById(R.id.app_icon),
+ iconDrawable,
+ iconContentDescription,
+ iconSize,
)
- currentView.contentDescription = iconName
}
override fun animateViewIn(view: ViewGroup) {
@@ -161,15 +177,6 @@ class MediaTttChipControllerReceiver @Inject constructor(
startRipple(view.requireViewById(R.id.ripple))
}
- override fun getIconSize(isAppIcon: Boolean): Int? =
- context.resources.getDimensionPixelSize(
- if (isAppIcon) {
- R.dimen.media_ttt_icon_size_receiver
- } else {
- R.dimen.media_ttt_generic_icon_size_receiver
- }
- )
-
/** Returns the amount that the chip will be translated by in its intro animation. */
private fun getTranslationAmount(): Int {
return context.resources.getDimensionPixelSize(R.dimen.media_ttt_receiver_vert_translation)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index bde588c14fc8..4379d25406bf 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -34,8 +34,7 @@ import com.android.systemui.temporarydisplay.DEFAULT_TIMEOUT_MILLIS
* @property stateInt the integer from [StatusBarManager] corresponding with this state.
* @property stringResId the res ID of the string that should be displayed in the chip. Null if the
* state should not have the chip be displayed.
- * @property isMidTransfer true if the state represents that a transfer is currently ongoing.
- * @property isTransferFailure true if the state represents that the transfer has failed.
+ * @property transferStatus the transfer status that the chip state represents.
* @property timeout the amount of time this chip should display on the screen before it times out
* and disappears.
*/
@@ -43,8 +42,7 @@ enum class ChipStateSender(
@StatusBarManager.MediaTransferSenderState val stateInt: Int,
val uiEvent: UiEventLogger.UiEventEnum,
@StringRes val stringResId: Int?,
- val isMidTransfer: Boolean = false,
- val isTransferFailure: Boolean = false,
+ val transferStatus: TransferStatus,
val timeout: Long = DEFAULT_TIMEOUT_MILLIS
) {
/**
@@ -56,6 +54,7 @@ enum class ChipStateSender(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_START_CAST,
R.string.media_move_closer_to_start_cast,
+ transferStatus = TransferStatus.NOT_STARTED,
),
/**
@@ -68,6 +67,7 @@ enum class ChipStateSender(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_ALMOST_CLOSE_TO_END_CAST,
R.string.media_move_closer_to_end_cast,
+ transferStatus = TransferStatus.NOT_STARTED,
),
/**
@@ -78,7 +78,7 @@ enum class ChipStateSender(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_TRIGGERED,
R.string.media_transfer_playing_different_device,
- isMidTransfer = true,
+ transferStatus = TransferStatus.IN_PROGRESS,
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
),
@@ -90,7 +90,7 @@ enum class ChipStateSender(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
R.string.media_transfer_playing_this_device,
- isMidTransfer = true,
+ transferStatus = TransferStatus.IN_PROGRESS,
timeout = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
),
@@ -100,7 +100,8 @@ enum class ChipStateSender(
TRANSFER_TO_RECEIVER_SUCCEEDED(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_SUCCEEDED,
- R.string.media_transfer_playing_different_device
+ R.string.media_transfer_playing_different_device,
+ transferStatus = TransferStatus.SUCCEEDED,
) {
override fun undoClickListener(
controllerSender: MediaTttChipControllerSender,
@@ -135,7 +136,8 @@ enum class ChipStateSender(
TRANSFER_TO_THIS_DEVICE_SUCCEEDED(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
- R.string.media_transfer_playing_this_device
+ R.string.media_transfer_playing_this_device,
+ transferStatus = TransferStatus.SUCCEEDED,
) {
override fun undoClickListener(
controllerSender: MediaTttChipControllerSender,
@@ -169,7 +171,7 @@ enum class ChipStateSender(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_RECEIVER_FAILED,
R.string.media_transfer_failed,
- isTransferFailure = true
+ transferStatus = TransferStatus.FAILED,
),
/** A state representing that a transfer back to this device has failed. */
@@ -177,14 +179,15 @@ enum class ChipStateSender(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_TRANSFER_TO_THIS_DEVICE_FAILED,
R.string.media_transfer_failed,
- isTransferFailure = true
+ transferStatus = TransferStatus.FAILED,
),
/** A state representing that this device is far away from any receiver device. */
FAR_FROM_RECEIVER(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
MediaTttSenderUiEvents.MEDIA_TTT_SENDER_FAR_FROM_RECEIVER,
- stringResId = null
+ stringResId = null,
+ transferStatus = TransferStatus.TOO_FAR,
);
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 0c1ebd70c572..e539f3fd842d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -34,6 +34,7 @@ import com.android.systemui.animation.ViewHierarchyAnimator
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
+import com.android.systemui.media.taptotransfer.common.MediaTttUtils
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
@@ -57,7 +58,7 @@ class MediaTttChipControllerSender @Inject constructor(
configurationController: ConfigurationController,
powerManager: PowerManager,
private val uiEventLogger: MediaTttSenderUiEventLogger
-) : TemporaryViewDisplayController<ChipSenderInfo>(
+) : TemporaryViewDisplayController<ChipSenderInfo, MediaTttLogger>(
context,
logger,
windowManager,
@@ -66,6 +67,8 @@ class MediaTttChipControllerSender @Inject constructor(
configurationController,
powerManager,
R.layout.media_ttt_chip,
+ MediaTttUtils.WINDOW_TITLE,
+ MediaTttUtils.WAKE_REASON,
) {
override val windowLayoutParams = commonWindowLayoutParams.apply {
gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
@@ -94,7 +97,7 @@ class MediaTttChipControllerSender @Inject constructor(
) {
val chipState: ChipStateSender? = ChipStateSender.getSenderStateFromId(displayState)
val stateName = chipState?.name ?: "Invalid"
- logger.logStateChange(stateName, routeInfo.id)
+ logger.logStateChange(stateName, routeInfo.id, routeInfo.clientPackageName)
if (chipState == null) {
Log.e(SENDER_TAG, "Unhandled MediaTransferSenderState $displayState")
@@ -103,7 +106,7 @@ class MediaTttChipControllerSender @Inject constructor(
uiEventLogger.logSenderStateChange(chipState)
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
- removeView(removalReason = ChipStateSender.FAR_FROM_RECEIVER::class.simpleName!!)
+ removeView(removalReason = ChipStateSender.FAR_FROM_RECEIVER.name)
} else {
displayView(ChipSenderInfo(chipState, routeInfo, undoCallback))
}
@@ -118,7 +121,14 @@ class MediaTttChipControllerSender @Inject constructor(
val chipState = newInfo.state
// App icon
- val iconName = setIcon(currentView, newInfo.routeInfo.clientPackageName)
+ val iconInfo = MediaTttUtils.getIconInfoFromPackageName(
+ context, newInfo.routeInfo.clientPackageName, logger
+ )
+ MediaTttUtils.setIcon(
+ currentView.requireViewById(R.id.app_icon),
+ iconInfo.drawable,
+ iconInfo.contentDescription
+ )
// Text
val otherDeviceName = newInfo.routeInfo.name.toString()
@@ -127,7 +137,7 @@ class MediaTttChipControllerSender @Inject constructor(
// Loading
currentView.requireViewById<View>(R.id.loading).visibility =
- chipState.isMidTransfer.visibleIfTrue()
+ (chipState.transferStatus == TransferStatus.IN_PROGRESS).visibleIfTrue()
// Undo
val undoView = currentView.requireViewById<View>(R.id.undo)
@@ -139,12 +149,12 @@ class MediaTttChipControllerSender @Inject constructor(
// Failure
currentView.requireViewById<View>(R.id.failure_icon).visibility =
- chipState.isTransferFailure.visibleIfTrue()
+ (chipState.transferStatus == TransferStatus.FAILED).visibleIfTrue()
// For accessibility
currentView.requireViewById<ViewGroup>(
R.id.media_ttt_sender_chip_inner
- ).contentDescription = "$iconName $chipText"
+ ).contentDescription = "${iconInfo.contentDescription} $chipText"
}
override fun animateViewIn(view: ViewGroup) {
@@ -162,10 +172,17 @@ class MediaTttChipControllerSender @Inject constructor(
}
override fun removeView(removalReason: String) {
- // Don't remove the chip if we're mid-transfer since the user should still be able to
- // see the status of the transfer. (But do remove it if it's finally timed out.)
- if (info?.state?.isMidTransfer == true &&
- removalReason != TemporaryDisplayRemovalReason.REASON_TIMEOUT) {
+ // Don't remove the chip if we're in progress or succeeded, since the user should still be
+ // able to see the status of the transfer. (But do remove it if it's finally timed out.)
+ val transferStatus = info?.state?.transferStatus
+ if (
+ (transferStatus == TransferStatus.IN_PROGRESS ||
+ transferStatus == TransferStatus.SUCCEEDED) &&
+ removalReason != TemporaryDisplayRemovalReason.REASON_TIMEOUT
+ ) {
+ logger.logRemovalBypass(
+ removalReason, bypassReason = "transferStatus=${transferStatus.name}"
+ )
return
}
super.removeView(removalReason)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/TransferStatus.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/TransferStatus.kt
new file mode 100644
index 000000000000..f15720df5245
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/TransferStatus.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.media.taptotransfer.sender
+
+/** Represents the different possible transfer states that we could be in. */
+enum class TransferStatus {
+ /** The transfer hasn't started yet. */
+ NOT_STARTED,
+ /** The transfer is currently ongoing but hasn't completed yet. */
+ IN_PROGRESS,
+ /** The transfer has completed successfully. */
+ SUCCEEDED,
+ /** The transfer has completed with a failure. */
+ FAILED,
+ /** The device is too far away to do a transfer. */
+ TOO_FAR,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
new file mode 100644
index 000000000000..2b381a954e27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorController.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mediaprojection.appselector
+
+import android.content.ComponentName
+import com.android.systemui.media.dagger.MediaProjectionAppSelector
+import com.android.systemui.mediaprojection.appselector.data.RecentTask
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.launch
+
+class MediaProjectionAppSelectorController(
+ private val recentTaskListProvider: RecentTaskListProvider,
+ @MediaProjectionAppSelector private val scope: CoroutineScope,
+ private val appSelectorComponentName: ComponentName
+) {
+
+ fun init(view: MediaProjectionAppSelectorView) {
+ scope.launch {
+ val tasks = recentTaskListProvider.loadRecentTasks().sortTasks()
+ view.bind(tasks)
+ }
+ }
+
+ fun destroy() {
+ scope.cancel()
+ }
+
+ private fun List<RecentTask>.sortTasks(): List<RecentTask> =
+ sortedBy {
+ // Show normal tasks first and only then tasks with opened app selector
+ it.topActivityComponent == appSelectorComponentName
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorView.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorView.kt
new file mode 100644
index 000000000000..6550aa5569e4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorView.kt
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mediaprojection.appselector
+
+import com.android.systemui.mediaprojection.appselector.data.RecentTask
+
+interface MediaProjectionAppSelectorView {
+ fun bind(recentTasks: List<RecentTask>)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
new file mode 100644
index 000000000000..0927f3b00724
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/AppIconLoader.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mediaprojection.appselector.data
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.pm.PackageManager.ComponentInfoFlags
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import com.android.launcher3.icons.BaseIconFactory.IconOptions
+import com.android.launcher3.icons.IconFactory
+import com.android.systemui.dagger.qualifiers.Background
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+interface AppIconLoader {
+ suspend fun loadIcon(userId: Int, component: ComponentName): Drawable?
+}
+
+class IconLoaderLibAppIconLoader
+@Inject
+constructor(
+ @Background private val backgroundDispatcher: CoroutineDispatcher,
+ private val context: Context,
+ private val packageManager: PackageManager
+) : AppIconLoader {
+
+ override suspend fun loadIcon(userId: Int, component: ComponentName): Drawable? =
+ withContext(backgroundDispatcher) {
+ IconFactory.obtain(context).use<IconFactory, Drawable?> { iconFactory ->
+ val activityInfo = packageManager
+ .getActivityInfo(component, ComponentInfoFlags.of(0))
+ val icon = activityInfo.loadIcon(packageManager) ?: return@withContext null
+ val userHandler = UserHandle.of(userId)
+ val options = IconOptions().apply { setUser(userHandler) }
+ val badgedIcon = iconFactory.createBadgedIconBitmap(icon, options)
+ badgedIcon.newIcon(context)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
new file mode 100644
index 000000000000..cd994b857e95
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTask.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mediaprojection.appselector.data
+
+import android.annotation.ColorInt
+import android.content.ComponentName
+
+data class RecentTask(
+ val taskId: Int,
+ val userId: Int,
+ val topActivityComponent: ComponentName?,
+ val baseIntentComponent: ComponentName?,
+ @ColorInt val colorBackground: Int?
+)
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
new file mode 100644
index 000000000000..e8b49cd8ec1c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskListProvider.kt
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mediaprojection.appselector.data
+
+import android.app.ActivityManager
+import android.app.ActivityManager.RECENT_IGNORE_UNAVAILABLE
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.util.kotlin.getOrNull
+import com.android.wm.shell.recents.RecentTasks
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import java.util.Optional
+import javax.inject.Inject
+import kotlin.coroutines.resume
+import kotlin.coroutines.suspendCoroutine
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+import java.util.concurrent.Executor
+
+interface RecentTaskListProvider {
+ /** Loads recent tasks, the returned task list is from the most-recent to least-recent order */
+ suspend fun loadRecentTasks(): List<RecentTask>
+}
+
+class ShellRecentTaskListProvider
+@Inject
+constructor(
+ @Background private val coroutineDispatcher: CoroutineDispatcher,
+ @Background private val backgroundExecutor: Executor,
+ private val recentTasks: Optional<RecentTasks>
+) : RecentTaskListProvider {
+
+ private val recents by lazy { recentTasks.getOrNull() }
+
+ override suspend fun loadRecentTasks(): List<RecentTask> =
+ withContext(coroutineDispatcher) {
+ val rawRecentTasks: List<GroupedRecentTaskInfo> = recents?.getTasks() ?: emptyList()
+
+ rawRecentTasks
+ .flatMap { listOfNotNull(it.taskInfo1, it.taskInfo2) }
+ .map {
+ RecentTask(
+ it.taskId,
+ it.userId,
+ it.topActivity,
+ it.baseIntent?.component,
+ it.taskDescription?.backgroundColor
+ )
+ }
+ }
+
+ private suspend fun RecentTasks.getTasks(): List<GroupedRecentTaskInfo> =
+ suspendCoroutine { continuation ->
+ getRecentTasks(
+ Integer.MAX_VALUE,
+ RECENT_IGNORE_UNAVAILABLE,
+ ActivityManager.getCurrentUser(),
+ backgroundExecutor
+ ) { tasks ->
+ continuation.resume(tasks)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
new file mode 100644
index 000000000000..47faaed10302
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/data/RecentTaskThumbnailLoader.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mediaprojection.appselector.data
+
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.shared.recents.model.ThumbnailData
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+interface RecentTaskThumbnailLoader {
+ suspend fun loadThumbnail(taskId: Int): ThumbnailData?
+}
+
+class ActivityTaskManagerThumbnailLoader
+@Inject
+constructor(
+ @Background private val coroutineDispatcher: CoroutineDispatcher,
+ private val activityManager: ActivityManagerWrapper
+) : RecentTaskThumbnailLoader {
+
+ override suspend fun loadThumbnail(taskId: Int): ThumbnailData? =
+ withContext(coroutineDispatcher) {
+ val thumbnailData =
+ activityManager.getTaskThumbnail(taskId, /* isLowResolution= */ false)
+ if (thumbnailData.thumbnail == null) null else thumbnailData
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
new file mode 100644
index 000000000000..ec5abc7a12f4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTaskViewHolder.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mediaprojection.appselector.view
+
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.R
+import com.android.systemui.media.dagger.MediaProjectionAppSelector
+import com.android.systemui.mediaprojection.appselector.data.AppIconLoader
+import com.android.systemui.mediaprojection.appselector.data.RecentTask
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskThumbnailLoader
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+
+class RecentTaskViewHolder @AssistedInject constructor(
+ @Assisted root: ViewGroup,
+ private val iconLoader: AppIconLoader,
+ private val thumbnailLoader: RecentTaskThumbnailLoader,
+ @MediaProjectionAppSelector private val scope: CoroutineScope
+) : RecyclerView.ViewHolder(root) {
+
+ private val iconView: ImageView = root.requireViewById(R.id.task_icon)
+ private val thumbnailView: ImageView = root.requireViewById(R.id.task_thumbnail)
+
+ private var job: Job? = null
+
+ fun bind(task: RecentTask, onClick: (View) -> Unit) {
+ job?.cancel()
+
+ job =
+ scope.launch {
+ task.baseIntentComponent?.let { component ->
+ launch {
+ val icon = iconLoader.loadIcon(task.userId, component)
+ iconView.setImageDrawable(icon)
+ }
+ }
+ launch {
+ val thumbnail = thumbnailLoader.loadThumbnail(task.taskId)
+ thumbnailView.setImageBitmap(thumbnail?.thumbnail)
+ }
+ }
+
+ thumbnailView.setOnClickListener(onClick)
+ }
+
+ fun onRecycled() {
+ iconView.setImageDrawable(null)
+ thumbnailView.setImageBitmap(null)
+ job?.cancel()
+ job = null
+ }
+
+ @AssistedFactory
+ fun interface Factory {
+ fun create(root: ViewGroup): RecentTaskViewHolder
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt
new file mode 100644
index 000000000000..ec9cfa88f34d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/appselector/view/RecentTasksAdapter.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.mediaprojection.appselector.view
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.android.systemui.R
+import com.android.systemui.mediaprojection.appselector.data.RecentTask
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+class RecentTasksAdapter @AssistedInject constructor(
+ @Assisted private val items: List<RecentTask>,
+ @Assisted private val listener: RecentTaskClickListener,
+ private val viewHolderFactory: RecentTaskViewHolder.Factory
+) : RecyclerView.Adapter<RecentTaskViewHolder>() {
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecentTaskViewHolder {
+ val taskItem =
+ LayoutInflater.from(parent.context)
+ .inflate(R.layout.media_projection_task_item, null) as ViewGroup
+
+ return viewHolderFactory.create(taskItem)
+ }
+
+ override fun onBindViewHolder(holder: RecentTaskViewHolder, position: Int) {
+ val task = items[position]
+ holder.bind(task, onClick = {
+ listener.onRecentClicked(task, holder.itemView)
+ })
+ }
+
+ override fun getItemCount(): Int = items.size
+
+ override fun onViewRecycled(holder: RecentTaskViewHolder) {
+ holder.onRecycled()
+ }
+
+ interface RecentTaskClickListener {
+ fun onRecentClicked(task: RecentTask, view: View)
+ }
+
+ @AssistedFactory
+ fun interface Factory {
+ fun create(items: List<RecentTask>, listener: RecentTaskClickListener): RecentTasksAdapter
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 30947e839f0a..50a10bc0b15a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -541,10 +541,12 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
if (!mImeVisible) {
// IME not showing, take all touches
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ return;
}
if (!mView.isImeRenderingNavButtons()) {
// IME showing but not drawing any buttons, take all touches
info.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ return;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 7c4c64c20089..d605c1a42ec0 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -955,9 +955,9 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
updateDisabledForQuickstep(newConfig);
}
- if (DEBUG_MISSING_GESTURE) {
- Log.d(DEBUG_MISSING_GESTURE_TAG, "Config changed: config=" + newConfig);
- }
+ // TODO(b/243765256): Disable this logging once b/243765256 is fixed.
+ Log.d(DEBUG_MISSING_GESTURE_TAG, "Config changed: newConfig=" + newConfig
+ + " lastReportedConfig=" + mLastReportedConfig);
mLastReportedConfig.updateFrom(newConfig);
updateDisplaySize();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
index cd3609108c28..1d058744ac94 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/NonInterceptingScrollView.java
@@ -28,21 +28,29 @@ import android.widget.ScrollView;
public class NonInterceptingScrollView extends ScrollView {
private final int mTouchSlop;
+
private float mDownY;
private boolean mScrollEnabled = true;
+ private boolean mPreventingIntercept;
public NonInterceptingScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
+ public boolean isPreventingIntercept() {
+ return mPreventingIntercept;
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
int action = ev.getActionMasked();
switch (action) {
case MotionEvent.ACTION_DOWN:
+ mPreventingIntercept = false;
if (canScrollVertically(1)) {
// If we can scroll down, make sure we're not intercepted by the parent
+ mPreventingIntercept = true;
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
@@ -62,10 +70,13 @@ public class NonInterceptingScrollView extends ScrollView {
public boolean onInterceptTouchEvent(MotionEvent ev) {
// If there's a touch on this view and we can scroll down, we don't want to be intercepted
int action = ev.getActionMasked();
+
switch (action) {
case MotionEvent.ACTION_DOWN:
- // If we can scroll down, make sure non of our parents intercepts us.
+ mPreventingIntercept = false;
+ // If we can scroll down, make sure none of our parents intercepts us.
if (canScrollVertically(1)) {
+ mPreventingIntercept = true;
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 920f4634abe2..e1289a61d45d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -99,7 +99,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
// slider, as well as animating the alpha of the QS tile layout (as we are tracking QQS tiles)
@Nullable
private TouchAnimator mFirstPageAnimator;
- // TranslationX animator for QQS/QS tiles
+ // TranslationX animator for QQS/QS tiles. Only used on the first page!
private TouchAnimator mTranslationXAnimator;
// TranslationY animator for QS tiles (and their components) in the first page
private TouchAnimator mTranslationYAnimator;
@@ -107,13 +107,14 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
private TouchAnimator mQQSTranslationYAnimator;
// Animates alpha of permanent views (QS tile layout, QQS tiles) when not in first page
private TouchAnimator mNonfirstPageAlphaAnimator;
- // TranslatesY the QS Tile layout using QS.getHeightDiff()
- private TouchAnimator mQSTileLayoutTranslatorAnimator;
// This animates fading of media player
private TouchAnimator mAllPagesDelayedAnimator;
- // Animator for brightness slider(s)
+ // Brightness slider translation driver, uses mQSExpansionPathInterpolator.yInterpolator
@Nullable
- private TouchAnimator mBrightnessAnimator;
+ private TouchAnimator mBrightnessTranslationAnimator;
+ // Brightness slider opacity driver. Uses linear interpolator.
+ @Nullable
+ private TouchAnimator mBrightnessOpacityAnimator;
// Animator for Footer actions in QQS
private TouchAnimator mQQSFooterActionsAnimator;
// Height animator for QQS tiles (height changing from QQS size to QS size)
@@ -137,7 +138,6 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
private final QSTileHost mHost;
private final Executor mExecutor;
private boolean mShowCollapsedOnKeyguard;
- private boolean mTranslateWhileExpanding;
private int mQQSTop;
private int[] mTmpLoc1 = new int[2];
@@ -298,13 +298,6 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
QSTileLayout tileLayout = mQsPanelController.getTileLayout();
mAllViews.add((View) tileLayout);
- int heightDiff = mQs.getHeightDiff();
- if (!mTranslateWhileExpanding) {
- heightDiff *= SHORT_PARALLAX_AMOUNT;
- }
- mQSTileLayoutTranslatorAnimator = new Builder()
- .addFloat(tileLayout, "translationY", heightDiff, 0)
- .build();
mLastQQSTileHeight = 0;
@@ -407,12 +400,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
mAnimatedQsViews.add(tileView);
mAllViews.add(quickTileView);
mAllViews.add(quickTileView.getSecondaryLabel());
- } else if (isIconInAnimatedRow(count)) {
-
- firstPageBuilder.addFloat(tileView, "translationY", -heightDiff, 0);
-
- mAllViews.add(tileIcon);
- } else {
+ } else if (!isIconInAnimatedRow(count)) {
// Pretend there's a corresponding QQS tile (for the position) that we are
// expanding from.
SideLabelTileLayout qqsLayout =
@@ -442,7 +430,7 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
}
}
- animateBrightnessSlider(firstPageBuilder);
+ animateBrightnessSlider();
mFirstPageAnimator = firstPageBuilder
// Fade in the tiles/labels as we reach the final position.
@@ -568,7 +556,9 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
return new Pair<>(animator, builder.build());
}
- private void animateBrightnessSlider(Builder firstPageBuilder) {
+ private void animateBrightnessSlider() {
+ mBrightnessTranslationAnimator = null;
+ mBrightnessOpacityAnimator = null;
View qsBrightness = mQsPanelController.getBrightnessView();
View qqsBrightness = mQuickQSPanelController.getBrightnessView();
if (qqsBrightness != null && qqsBrightness.getVisibility() == View.VISIBLE) {
@@ -576,25 +566,45 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
mAnimatedQsViews.add(qsBrightness);
mAllViews.add(qqsBrightness);
int translationY = getRelativeTranslationY(qsBrightness, qqsBrightness);
- mBrightnessAnimator = new Builder()
+ mBrightnessTranslationAnimator = new Builder()
// we need to animate qs brightness even if animation will not be visible,
// as we might start from sliderScaleY set to 0.3 if device was in collapsed QS
// portrait orientation before
.addFloat(qsBrightness, "sliderScaleY", 0.3f, 1)
.addFloat(qqsBrightness, "translationY", 0, translationY)
+ .setInterpolator(mQSExpansionPathInterpolator.getYInterpolator())
.build();
} else if (qsBrightness != null) {
- firstPageBuilder.addFloat(qsBrightness, "translationY",
- qsBrightness.getMeasuredHeight() * 0.5f, 0);
- mBrightnessAnimator = new Builder()
+ // The brightness slider's visible bottom edge must maintain a constant margin from the
+ // QS tiles during transition. Thus the slider must (1) perform the same vertical
+ // translation as the tiles, and (2) compensate for the slider scaling.
+
+ // For (1), compute the distance via the vertical distance between QQS and QS tile
+ // layout top.
+ View quickSettingsRootView = mQs.getView();
+ View qsTileLayout = (View) mQsPanelController.getTileLayout();
+ View qqsTileLayout = (View) mQuickQSPanelController.getTileLayout();
+ getRelativePosition(mTmpLoc1, qsTileLayout, quickSettingsRootView);
+ getRelativePosition(mTmpLoc2, qqsTileLayout, quickSettingsRootView);
+ int tileMovement = mTmpLoc2[1] - mTmpLoc1[1];
+
+ // For (2), the slider scales to the vertical center, so compensate with half the
+ // height at full collapse.
+ float scaleCompensation = qsBrightness.getMeasuredHeight() * 0.5f;
+ mBrightnessTranslationAnimator = new Builder()
+ .addFloat(qsBrightness, "translationY", scaleCompensation + tileMovement, 0)
+ .addFloat(qsBrightness, "sliderScaleY", 0, 1)
+ .setInterpolator(mQSExpansionPathInterpolator.getYInterpolator())
+ .build();
+
+ // While the slider's position and unfurl is animated throughouth the motion, the
+ // fade in happens independently.
+ mBrightnessOpacityAnimator = new Builder()
.addFloat(qsBrightness, "alpha", 0, 1)
- .addFloat(qsBrightness, "sliderScaleY", 0.3f, 1)
- .setInterpolator(Interpolators.ALPHA_IN)
- .setStartDelay(0.3f)
+ .setStartDelay(0.2f)
+ .setEndDelay(1 - 0.5f)
.build();
mAllViews.add(qsBrightness);
- } else {
- mBrightnessAnimator = null;
}
}
@@ -676,11 +686,13 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
if (mQQSTileHeightAnimator != null) {
mQQSTileHeightAnimator.setPosition(position);
}
- mQSTileLayoutTranslatorAnimator.setPosition(position);
mQQSTranslationYAnimator.setPosition(position);
mAllPagesDelayedAnimator.setPosition(position);
- if (mBrightnessAnimator != null) {
- mBrightnessAnimator.setPosition(position);
+ if (mBrightnessOpacityAnimator != null) {
+ mBrightnessOpacityAnimator.setPosition(position);
+ }
+ if (mBrightnessTranslationAnimator != null) {
+ mBrightnessTranslationAnimator.setPosition(position);
}
if (mQQSFooterActionsAnimator != null) {
mQQSFooterActionsAnimator.setPosition(position);
@@ -774,13 +786,6 @@ public class QSAnimator implements QSHost.Callback, PagedTileLayout.PageListener
setCurrentPosition();
};
- /**
- * True whe QS will be pulled from the top, false when it will be clipped.
- */
- public void setTranslateWhileExpanding(boolean shouldTranslate) {
- mTranslateWhileExpanding = shouldTranslate;
- }
-
private static class HeightExpansionAnimator {
private final List<View> mViews = new ArrayList<>();
private final ValueAnimator mAnimator;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7b1ddd62ec6e..ef87fb49752d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -131,6 +131,10 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
updateClippingPath();
}
+ public NonInterceptingScrollView getQSPanelContainer() {
+ return mQSPanelContainer;
+ }
+
public void disable(int state1, int state2, boolean animate) {
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
if (disabled == mQsDisabled) return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
index 7d61991c910a..dea7bb5abd71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImplController.java
@@ -16,8 +16,13 @@
package com.android.systemui.qs;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED;
+
import android.content.res.Configuration;
+import android.view.MotionEvent;
+import android.view.View;
+import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;
@@ -30,6 +35,8 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
private final QSPanelController mQsPanelController;
private final QuickStatusBarHeaderController mQuickStatusBarHeaderController;
private final ConfigurationController mConfigurationController;
+ private final FalsingManager mFalsingManager;
+ private final NonInterceptingScrollView mQSPanelContainer;
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -39,14 +46,32 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
}
};
+ private final View.OnTouchListener mContainerTouchHandler = new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ if (mQSPanelContainer.isPreventingIntercept()) {
+ // There's really no action here to take, but we need to tell the FalsingManager
+ mFalsingManager.isFalseTouch(QS_SWIPE_NESTED);
+ }
+ }
+ return false;
+ }
+ };
+
@Inject
- QSContainerImplController(QSContainerImpl view, QSPanelController qsPanelController,
+ QSContainerImplController(
+ QSContainerImpl view,
+ QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController,
- ConfigurationController configurationController) {
+ ConfigurationController configurationController,
+ FalsingManager falsingManager) {
super(view);
mQsPanelController = qsPanelController;
mQuickStatusBarHeaderController = quickStatusBarHeaderController;
mConfigurationController = configurationController;
+ mFalsingManager = falsingManager;
+ mQSPanelContainer = mView.getQSPanelContainer();
}
@Override
@@ -62,11 +87,13 @@ public class QSContainerImplController extends ViewController<QSContainerImpl> {
protected void onViewAttached() {
mView.updateResources(mQsPanelController, mQuickStatusBarHeaderController);
mConfigurationController.addCallback(mConfigurationListener);
+ mQSPanelContainer.setOnTouchListener(mContainerTouchHandler);
}
@Override
protected void onViewDetached() {
mConfigurationController.removeCallback(mConfigurationListener);
+ mQSPanelContainer.setOnTouchListener(null);
}
public QSContainerImpl getView() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 072c32f693d9..3820500c6de3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -573,7 +573,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
@Override
public void setInSplitShade(boolean inSplitShade) {
mInSplitShade = inSplitShade;
- mQSAnimator.setTranslateWhileExpanding(inSplitShade);
updateShowCollapsedOnKeyguard();
updateQsState();
}
@@ -669,7 +668,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
mQSPanelController.setRevealExpansion(expansion);
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
- mQSPanelScrollView.setTranslationY(translationScaleY * heightDiff);
+
+ float qsScrollViewTranslation =
+ onKeyguard && !mShowCollapsedOnKeyguard ? panelTranslationY : 0;
+ mQSPanelScrollView.setTranslationY(qsScrollViewTranslation);
+
if (fullyCollapsed) {
mQSPanelScrollView.setScrollY(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 448e1807f7af..184089f7eef4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -473,6 +473,8 @@ public class QSPanel extends LinearLayout implements Tunable {
? Math.max(mMediaTotalBottomMargin - getPaddingBottom(), 0) : 0;
layoutParams.topMargin = mediaNeedsTopMargin() && !horizontal
? mMediaTopMargin : 0;
+ // Call setLayoutParams explicitly to ensure that requestLayout happens
+ hostView.setLayoutParams(layoutParams);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 59b871cc41bb..18bd6b7b3c32 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -16,19 +16,17 @@
package com.android.systemui.qs;
-import static com.android.systemui.classifier.Classifier.QS_SWIPE;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.media.dagger.MediaModule.QS_PANEL;
import static com.android.systemui.qs.QSPanel.QS_SHOW_BRIGHTNESS;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
-import android.content.res.Configuration;
import android.view.MotionEvent;
import android.view.View;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.media.MediaHostState;
@@ -61,22 +59,11 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
- new QSPanel.OnConfigurationChangedListener() {
- @Override
- public void onConfigurationChange(Configuration newConfig) {
- mView.updateResources();
- if (mView.isListening()) {
- refreshAllTiles();
- }
- }
- };
-
private View.OnTouchListener mTileLayoutTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- mFalsingManager.isFalseTouch(QS_SWIPE);
+ mFalsingManager.isFalseTouch(QS_SWIPE_SIDE);
}
return false;
}
@@ -88,14 +75,13 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QS_PANEL) MediaHost mediaHost,
QSTileRevealController.Factory qsTileRevealControllerFactory,
- DumpManager dumpManager, MediaCarouselController mediaCarouselController,
- MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
+ DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSliderController.Factory brightnessSliderFactory,
FalsingManager falsingManager,
StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
- metricsLogger, uiEventLogger, qsLogger, dumpManager, mediaCarouselController);
+ metricsLogger, uiEventLogger, qsLogger, dumpManager);
mTunerService = tunerService;
mQsCustomizerController = qsCustomizerController;
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
@@ -130,7 +116,6 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
if (mView.isListening()) {
refreshAllTiles();
}
- mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
switchTileLayout(true);
mBrightnessMirrorHandler.onQsPanelAttached();
@@ -147,11 +132,18 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
@Override
protected void onViewDetached() {
mTunerService.removeTunable(mView);
- mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
mBrightnessMirrorHandler.onQsPanelDettached();
super.onViewDetached();
}
+ @Override
+ protected void onConfigurationChanged() {
+ mView.updateResources();
+ if (mView.isListening()) {
+ refreshAllTiles();
+ }
+ }
+
/** */
public void setVisibility(int visibility) {
mView.setVisibility(visibility);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index 57bea67b7678..ded466a0cb25 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -24,6 +24,7 @@ import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.res.Configuration;
import android.metrics.LogMaker;
+import android.util.Log;
import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
@@ -31,7 +32,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
@@ -61,6 +61,7 @@ import kotlin.jvm.functions.Function1;
*/
public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewController<T>
implements Dumpable{
+ private static final String TAG = "QSPanelControllerBase";
protected final QSTileHost mHost;
private final QSCustomizerController mQsCustomizerController;
private final boolean mUsingMediaPlayer;
@@ -69,7 +70,6 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
private final UiEventLogger mUiEventLogger;
private final QSLogger mQSLogger;
private final DumpManager mDumpManager;
- private final MediaCarouselController mMediaCarouselController;
protected final ArrayList<TileRecord> mRecords = new ArrayList<>();
protected boolean mShouldUseSplitNotificationShade;
@@ -90,13 +90,20 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
public void onConfigurationChange(Configuration newConfig) {
mShouldUseSplitNotificationShade =
LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+ // Logging to aid the investigation of b/216244185.
+ Log.d(TAG,
+ "onConfigurationChange: "
+ + "mShouldUseSplitNotificationShade="
+ + mShouldUseSplitNotificationShade + ", "
+ + "newConfig.windowConfiguration="
+ + newConfig.windowConfiguration);
mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation,
mView.getDumpableTag());
- onConfigurationChanged();
if (newConfig.orientation != mLastOrientation) {
mLastOrientation = newConfig.orientation;
switchTileLayout(false);
}
+ onConfigurationChanged();
}
};
@@ -124,8 +131,7 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
MetricsLogger metricsLogger,
UiEventLogger uiEventLogger,
QSLogger qsLogger,
- DumpManager dumpManager,
- MediaCarouselController mediaCarouselController
+ DumpManager dumpManager
) {
super(view);
mHost = host;
@@ -138,7 +144,6 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
mDumpManager = dumpManager;
mShouldUseSplitNotificationShade =
LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
- mMediaCarouselController = mediaCarouselController;
}
@Override
@@ -156,7 +161,6 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
public void setSquishinessFraction(float squishinessFraction) {
mView.setSquishinessFraction(squishinessFraction);
- mMediaCarouselController.setSquishinessFraction(squishinessFraction);
}
@Override
@@ -413,6 +417,8 @@ public abstract class QSPanelControllerBase<T extends QSPanel> extends ViewContr
}
if (mMediaHost != null) {
pw.println(" media bounds: " + mMediaHost.getCurrentBounds());
+ pw.println(" horizontal layout: " + mUsingHorizontalLayout);
+ pw.println(" last orientation: " + mLastOrientation);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index c86e6e855c3d..9739974256f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -26,7 +26,6 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
@@ -46,14 +45,6 @@ import javax.inject.Provider;
@QSScope
public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel> {
- private final QSPanel.OnConfigurationChangedListener mOnConfigurationChangedListener =
- newConfig -> {
- int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles);
- if (newMaxTiles != mView.getNumQuickTiles()) {
- setMaxTiles(newMaxTiles);
- }
- };
-
private final Provider<Boolean> mUsingCollapsedLandscapeMediaProvider;
@Inject
@@ -64,10 +55,10 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
@Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
Provider<Boolean> usingCollapsedLandscapeMediaProvider,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager, MediaCarouselController mediaCarouselController
+ DumpManager dumpManager
) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
- uiEventLogger, qsLogger, dumpManager, mediaCarouselController);
+ uiEventLogger, qsLogger, dumpManager);
mUsingCollapsedLandscapeMediaProvider = usingCollapsedLandscapeMediaProvider;
}
@@ -99,13 +90,11 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
@Override
protected void onViewAttached() {
super.onViewAttached();
- mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
}
@Override
protected void onViewDetached() {
super.onViewDetached();
- mView.removeOnConfigurationChangedListener(mOnConfigurationChangedListener);
}
private void setMaxTiles(int parseNumTiles) {
@@ -115,6 +104,10 @@ public class QuickQSPanelController extends QSPanelControllerBase<QuickQSPanel>
@Override
protected void onConfigurationChanged() {
+ int newMaxTiles = getResources().getInteger(R.integer.quick_qs_panel_max_tiles);
+ if (newMaxTiles != mView.getNumQuickTiles()) {
+ setMaxTiles(newMaxTiles);
+ }
updateMediaExpansion();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index 58426656e4bf..e9a6c25c0e6d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -50,6 +50,7 @@ public class QSIconViewImpl extends QSIconView {
protected int mIconSizePx;
private boolean mAnimationEnabled = true;
private int mState = -1;
+ private boolean mDisabledByPolicy = false;
private int mTint;
@Nullable
private QSTile.Icon mLastIcon;
@@ -159,14 +160,10 @@ public class QSIconViewImpl extends QSIconView {
}
protected void setIcon(ImageView iv, QSTile.State state, boolean allowAnimations) {
- if (state.disabledByPolicy) {
- iv.setColorFilter(getContext().getColor(R.color.qs_tile_disabled_color));
- } else {
- iv.clearColorFilter();
- }
- if (state.state != mState) {
+ if (state.state != mState || state.disabledByPolicy != mDisabledByPolicy) {
int color = getColor(state);
mState = state.state;
+ mDisabledByPolicy = state.disabledByPolicy;
if (mTint != 0 && allowAnimations && shouldAnimate(iv)) {
animateGrayScale(mTint, color, iv, () -> updateIcon(iv, state, allowAnimations));
} else {
@@ -241,8 +238,8 @@ public class QSIconViewImpl extends QSIconView {
*/
private static int getIconColorForState(Context context, QSTile.State state) {
if (state.disabledByPolicy || state.state == Tile.STATE_UNAVAILABLE) {
- return Utils.applyAlpha(QSTileViewImpl.UNAVAILABLE_ALPHA,
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary));
+ return Utils.getColorAttrDefaultColor(
+ context, com.android.internal.R.attr.textColorTertiary);
} else if (state.state == Tile.STATE_INACTIVE) {
return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
} else if (state.state == Tile.STATE_ACTIVE) {
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 163ee2af600c..972b24343d10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -100,14 +100,15 @@ open class QSTileViewImpl @JvmOverloads constructor(
Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorOnAccent)
private val colorLabelInactive =
Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
- private val colorLabelUnavailable = Utils.applyAlpha(UNAVAILABLE_ALPHA, colorLabelInactive)
+ private val colorLabelUnavailable =
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorTertiary)
private val colorSecondaryLabelActive =
Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondaryInverse)
private val colorSecondaryLabelInactive =
Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary)
private val colorSecondaryLabelUnavailable =
- Utils.applyAlpha(UNAVAILABLE_ALPHA, colorSecondaryLabelInactive)
+ Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorTertiary)
private lateinit var label: TextView
protected lateinit var secondaryLabel: TextView
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index c2a82a76d3e8..a31500c6bb18 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -49,7 +49,6 @@ import javax.inject.Inject;
/** Quick settings tile: Invert colors **/
public class ColorInversionTile extends QSTileImpl<BooleanState> {
- private final Icon mIcon = ResourceIcon.get(drawable.ic_invert_colors);
private final SettingObserver mSetting;
@Inject
@@ -123,7 +122,9 @@ public class ColorInversionTile extends QSTileImpl<BooleanState> {
state.value = enabled;
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.label = mContext.getString(R.string.quick_settings_inversion_label);
- state.icon = mIcon;
+ state.icon = ResourceIcon.get(state.value
+ ? drawable.qs_invert_colors_icon_on
+ : drawable.qs_invert_colors_icon_off);
state.expandedAccessibilityClassName = Switch.class.getName();
state.contentDescription = state.label;
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
index 9fdf5940c392..2fc99f323611 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DataSaverTile.java
@@ -128,13 +128,13 @@ public class DataSaverTile extends QSTileImpl<BooleanState> implements
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- state.value = arg instanceof Boolean ? (Boolean) arg
+ state.value = arg instanceof Boolean ? ((Boolean) arg).booleanValue()
: mDataSaverController.isDataSaverEnabled();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.label = mContext.getString(R.string.data_saver);
state.contentDescription = state.label;
- state.icon = ResourceIcon.get(state.value ? R.drawable.ic_data_saver
- : R.drawable.ic_data_saver_off);
+ state.icon = ResourceIcon.get(state.value ? R.drawable.qs_data_saver_icon_on
+ : R.drawable.qs_data_saver_icon_off);
state.expandedAccessibilityClassName = Switch.class.getName();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 73b0896b06a5..a74792687289 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -43,11 +43,12 @@ import com.android.systemui.statusbar.policy.FlashlightController;
import javax.inject.Inject;
-/** Quick settings tile: Control flashlight **/
+/**
+ * Quick settings tile: Control flashlight
+ **/
public class FlashlightTile extends QSTileImpl<BooleanState> implements
FlashlightController.FlashlightListener {
- private final Icon mIcon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_flashlight);
private final FlashlightController mFlashlightController;
@Inject
@@ -116,19 +117,15 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
- if (state.slash == null) {
- state.slash = new SlashState();
- }
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
state.secondaryLabel = "";
state.stateDescription = "";
if (!mFlashlightController.isAvailable()) {
- state.icon = mIcon;
- state.slash.isSlashed = true;
state.secondaryLabel = mContext.getString(
R.string.quick_settings_flashlight_camera_in_use);
state.stateDescription = state.secondaryLabel;
state.state = Tile.STATE_UNAVAILABLE;
+ state.icon = ResourceIcon.get(R.drawable.qs_flashlight_icon_off);
return;
}
if (arg instanceof Boolean) {
@@ -140,11 +137,11 @@ public class FlashlightTile extends QSTileImpl<BooleanState> implements
} else {
state.value = mFlashlightController.isEnabled();
}
- state.icon = mIcon;
- state.slash.isSlashed = !state.value;
state.contentDescription = mContext.getString(R.string.quick_settings_flashlight_label);
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.icon = ResourceIcon.get(state.value
+ ? R.drawable.qs_flashlight_icon_on : R.drawable.qs_flashlight_icon_off);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index b6f6e933bf84..624def60276b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -51,8 +51,6 @@ import javax.inject.Inject;
/** Quick settings tile: Hotspot **/
public class HotspotTile extends QSTileImpl<BooleanState> {
- private final Icon mEnabledStatic = ResourceIcon.get(R.drawable.ic_hotspot);
-
private final HotspotController mHotspotController;
private final DataSaverController mDataSaverController;
@@ -129,9 +127,6 @@ public class HotspotTile extends QSTileImpl<BooleanState> {
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
final boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
- if (state.slash == null) {
- state.slash = new SlashState();
- }
final int numConnectedDevices;
final boolean isTransient = transientEnabling || mHotspotController.isHotspotTransient();
@@ -150,13 +145,14 @@ public class HotspotTile extends QSTileImpl<BooleanState> {
isDataSaverEnabled = mDataSaverController.isDataSaverEnabled();
}
- state.icon = mEnabledStatic;
state.label = mContext.getString(R.string.quick_settings_hotspot_label);
state.isTransient = isTransient;
- state.slash.isSlashed = !state.value && !state.isTransient;
if (state.isTransient) {
state.icon = ResourceIcon.get(
- com.android.internal.R.drawable.ic_hotspot_transient_animation);
+ R.drawable.qs_hotspot_icon_search);
+ } else {
+ state.icon = ResourceIcon.get(state.value
+ ? R.drawable.qs_hotspot_icon_on : R.drawable.qs_hotspot_icon_off);
}
state.expandedAccessibilityClassName = Switch.class.getName();
state.contentDescription = state.label;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
index 81813db5abfd..0e9f6599522f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NightDisplayTile.java
@@ -144,9 +144,10 @@ public class NightDisplayTile extends QSTileImpl<BooleanState> implements
protected void handleUpdateState(BooleanState state, Object arg) {
state.value = mManager.isNightDisplayActivated();
state.label = mContext.getString(R.string.quick_settings_night_display_label);
- state.icon = ResourceIcon.get(com.android.internal.R.drawable.ic_qs_night_display_on);
state.expandedAccessibilityClassName = Switch.class.getName();
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
+ state.icon = ResourceIcon.get(state.value ? R.drawable.qs_nightlight_icon_on
+ : R.drawable.qs_nightlight_icon_off);
state.secondaryLabel = getSecondaryLabel(state.value);
state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
? state.label
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
index 6921ad549626..1dac33909ba7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ReduceBrightColorsTile.java
@@ -49,7 +49,6 @@ import javax.inject.Named;
public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState>
implements ReduceBrightColorsController.Listener{
- private final Icon mIcon = ResourceIcon.get(drawable.ic_reduce_bright_colors);
private final boolean mIsAvailable;
private final ReduceBrightColorsController mReduceBrightColorsController;
private boolean mIsListening;
@@ -111,7 +110,9 @@ public class ReduceBrightColorsTile extends QSTileImpl<QSTile.BooleanState>
state.label = mContext.getString(R.string.reduce_bright_colors_feature_name);
state.expandedAccessibilityClassName = Switch.class.getName();
state.contentDescription = state.label;
- state.icon = mIcon;
+ state.icon = ResourceIcon.get(state.value
+ ? drawable.qs_extra_dim_icon_on
+ : drawable.qs_extra_dim_icon_off);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 75fb393d84b5..f63f0444210b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -120,7 +120,9 @@ public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
state.value = isRecording || isStarting;
state.state = (isRecording || isStarting) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.label = mContext.getString(R.string.quick_settings_screen_record_label);
- state.icon = ResourceIcon.get(R.drawable.ic_screenrecord);
+ state.icon = ResourceIcon.get(state.value
+ ? R.drawable.qs_screen_record_icon_on
+ : R.drawable.qs_screen_record_icon_off);
// Show expand icon when clicking will open a dialog
state.forceExpandIcon = state.state == Tile.STATE_INACTIVE;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index e2964eaf2a23..92f6690a13e7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -17,6 +17,7 @@
package com.android.systemui.qs.tiles;
import android.app.UiModeManager;
+import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Handler;
@@ -60,9 +61,7 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
ConfigurationController.ConfigurationListener,
BatteryController.BatteryStateChangeCallback {
public static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("hh:mm a");
- private final Icon mIcon = ResourceIcon.get(
- com.android.internal.R.drawable.ic_qs_ui_mode_night);
- private UiModeManager mUiModeManager;
+ private final UiModeManager mUiModeManager;
private final BatteryController mBatteryController;
private final LocationController mLocationController;
@Inject
@@ -155,7 +154,6 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
}
state.value = nightMode;
state.label = mContext.getString(R.string.quick_settings_ui_mode_night_label);
- state.icon = mIcon;
state.contentDescription = TextUtils.isEmpty(state.secondaryLabel)
? state.label
: TextUtils.concat(state.label, ", ", state.secondaryLabel);
@@ -164,6 +162,9 @@ public class UiModeNightTile extends QSTileImpl<QSTile.BooleanState> implements
} else {
state.state = state.value ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
}
+ state.icon = ResourceIcon.get(state.state == Tile.STATE_ACTIVE
+ ? R.drawable.qs_light_dark_theme_icon_on
+ : R.drawable.qs_light_dark_theme_icon_off);
state.showRippleEffect = false;
state.expandedAccessibilityClassName = Switch.class.getName();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index 0ec4eef1e551..97476b2d1cde 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -16,9 +16,6 @@
package com.android.systemui.qs.tiles;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
-
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
@@ -42,6 +39,7 @@ import com.android.systemui.qs.PseudoGridView;
import com.android.systemui.qs.QSUserSwitcherEvent;
import com.android.systemui.qs.user.UserSwitchDialogController;
import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.user.data.source.UserRecord;
@@ -73,7 +71,8 @@ public class UserDetailView extends PseudoGridView {
mAdapter.refresh();
}
- public static class Adapter extends UserSwitcherController.BaseUserAdapter
+ /** Provides views for user detail items. */
+ public static class Adapter extends BaseUserSwitcherAdapter
implements OnClickListener {
private final Context mContext;
@@ -137,7 +136,7 @@ public class UserDetailView extends PseudoGridView {
v.setActivated(item.isCurrent);
v.setDisabledByAdmin(mController.isDisabledByAdmin(item));
v.setEnabled(item.isSwitchToEnabled);
- v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA);
+ UserSwitcherController.setSelectableAlpha(v);
if (item.isCurrent) {
mCurrentUserView = v;
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
index 2861ed7edf37..24c4723d8b50 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -198,6 +198,8 @@ public class InternetDialog extends SystemUIDialog implements
mUiEventLogger.log(InternetDialogEvent.INTERNET_DIALOG_SHOW);
mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog,
null);
+ mDialogView.setAccessibilityPaneTitle(
+ mContext.getText(R.string.accessibility_desc_quick_settings));
final Window window = getWindow();
window.setContentView(mDialogView);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 3788ad98092f..7e2a5c51786d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -27,6 +27,8 @@ import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_DESKTOP_MODE;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_FLOATING_TASKS;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -109,6 +111,8 @@ import com.android.systemui.statusbar.phone.CentralSurfaces;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
import com.android.wm.shell.back.BackAnimation;
+import com.android.wm.shell.desktopmode.DesktopMode;
+import com.android.wm.shell.floating.FloatingTasks;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
@@ -150,6 +154,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final Optional<Pip> mPipOptional;
private final Lazy<Optional<CentralSurfaces>> mCentralSurfacesOptionalLazy;
private final Optional<SplitScreen> mSplitScreenOptional;
+ private final Optional<FloatingTasks> mFloatingTasksOptional;
private SysUiState mSysUiState;
private final Handler mHandler;
private final Lazy<NavigationBarController> mNavBarControllerLazy;
@@ -166,6 +171,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
private final Optional<RecentTasks> mRecentTasks;
private final Optional<BackAnimation> mBackAnimation;
+ private final Optional<DesktopMode> mDesktopModeOptional;
private final UiEventLogger mUiEventLogger;
private Region mActiveNavBarRegion;
@@ -382,7 +388,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
mCentralSurfacesOptionalLazy.get().ifPresent(CentralSurfaces::togglePanel));
}
-
private boolean verifyCaller(String reason) {
final int callerId = Binder.getCallingUserHandle().getIdentifier();
if (callerId != mCurrentBoundedUserId) {
@@ -466,6 +471,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
mSplitScreenOptional.ifPresent((splitscreen) -> params.putBinder(
KEY_EXTRA_SHELL_SPLIT_SCREEN,
splitscreen.createExternalInterface().asBinder()));
+ mFloatingTasksOptional.ifPresent(floatingTasks -> params.putBinder(
+ KEY_EXTRA_SHELL_FLOATING_TASKS,
+ floatingTasks.createExternalInterface().asBinder()));
mOneHandedOptional.ifPresent((onehanded) -> params.putBinder(
KEY_EXTRA_SHELL_ONE_HANDED,
onehanded.createExternalInterface().asBinder()));
@@ -483,6 +491,9 @@ public class OverviewProxyService extends CurrentUserTracker implements
mBackAnimation.ifPresent((backAnimation) -> params.putBinder(
KEY_EXTRA_SHELL_BACK_ANIMATION,
backAnimation.createExternalInterface().asBinder()));
+ mDesktopModeOptional.ifPresent((desktopMode -> params.putBinder(
+ KEY_EXTRA_SHELL_DESKTOP_MODE,
+ desktopMode.createExternalInterface().asBinder())));
try {
Log.d(TAG_OPS, "OverviewProxyService connected, initializing overview proxy");
@@ -563,10 +574,12 @@ public class OverviewProxyService extends CurrentUserTracker implements
NotificationShadeWindowController statusBarWinController, SysUiState sysUiState,
Optional<Pip> pipOptional,
Optional<SplitScreen> splitScreenOptional,
+ Optional<FloatingTasks> floatingTasksOptional,
Optional<OneHanded> oneHandedOptional,
Optional<RecentTasks> recentTasks,
Optional<BackAnimation> backAnimation,
Optional<StartingSurface> startingSurface,
+ Optional<DesktopMode> desktopModeOptional,
BroadcastDispatcher broadcastDispatcher,
ShellTransitions shellTransitions,
ScreenLifecycle screenLifecycle,
@@ -601,6 +614,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
mShellTransitions = shellTransitions;
mRecentTasks = recentTasks;
mBackAnimation = backAnimation;
+ mDesktopModeOptional = desktopModeOptional;
mUiEventLogger = uiEventLogger;
dumpManager.registerDumpable(getClass().getSimpleName(), this);
@@ -631,6 +645,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
mCommandQueue = commandQueue;
mSplitScreenOptional = splitScreenOptional;
+ mFloatingTasksOptional = floatingTasksOptional;
// Listen for user setup
startTracking();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
index 246265b2c202..ac5640b14716 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageCaptureImpl.kt
@@ -24,8 +24,9 @@ import android.os.IBinder
import android.util.Log
import android.view.DisplayAddress
import android.view.SurfaceControl
-import android.view.SurfaceControl.DisplayCaptureArgs
-import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import android.window.ScreenCapture
+import android.window.ScreenCapture.DisplayCaptureArgs
+import android.window.ScreenCapture.ScreenshotHardwareBuffer
import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -84,6 +85,6 @@ open class ImageCaptureImpl @Inject constructor(
.setSize(width, height)
.setSourceCrop(crop)
.build()
- return SurfaceControl.captureDisplay(captureArgs)
+ return ScreenCapture.captureDisplay(captureArgs)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 69ee8e8fb8dc..3fee232b3465 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -555,6 +555,8 @@ public class ScreenshotController {
mScreenshotView.announceForAccessibility(
mContext.getResources().getString(R.string.screenshot_saving_title)));
+ mScreenshotView.reset();
+
if (mScreenshotView.isAttachedToWindow()) {
// if we didn't already dismiss for another reason
if (!mScreenshotView.isDismissing()) {
@@ -564,7 +566,6 @@ public class ScreenshotController {
Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
+ "(dismissing=" + mScreenshotView.isDismissing() + ")");
}
- mScreenshotView.reset();
}
mPackageName = topComponent == null ? "" : topComponent.getPackageName();
mScreenshotView.setPackageName(mPackageName);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 5e7fc6faef1f..360fc879731c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -1006,6 +1006,7 @@ public class ScreenshotView extends FrameLayout implements
// Clear any references to the bitmap
mScreenshotPreview.setImageDrawable(null);
mScreenshotPreview.setVisibility(View.INVISIBLE);
+ mScreenshotPreview.setAlpha(1f);
mScreenshotPreviewBorder.setAlpha(0);
mPendingSharedTransition = false;
mActionsContainerBackground.setVisibility(View.GONE);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 3429b9d0e503..d7e86b6e2919 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -33,6 +33,7 @@ import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_Q
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.VibratorHelper.TOUCH_VIBRATION_ATTRIBUTES;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_FOLD_TO_AOD;
import static com.android.systemui.statusbar.phone.panelstate.PanelExpansionStateManagerKt.STATE_CLOSED;
@@ -62,6 +63,7 @@ import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
import android.os.Handler;
import android.os.PowerManager;
+import android.os.Process;
import android.os.Trace;
import android.os.UserManager;
import android.os.VibrationEffect;
@@ -236,6 +238,9 @@ public final class NotificationPanelViewController extends PanelViewController {
private static final boolean SPEW_LOGCAT = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
private static final boolean DEBUG_DRAWABLE = false;
+ private static final VibrationEffect ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT =
+ VibrationEffect.get(VibrationEffect.EFFECT_STRENGTH_MEDIUM, false);
+
/**
* The parallax amount of the quick settings translation when dragging down the panel
*/
@@ -682,14 +687,22 @@ public final class NotificationPanelViewController extends PanelViewController {
private final FalsingTapListener mFalsingTapListener = new FalsingTapListener() {
@Override
- public void onDoubleTapRequired() {
+ public void onAdditionalTapRequired() {
if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
mTapAgainViewController.show();
} else {
mKeyguardIndicationController.showTransientIndication(
R.string.notification_tap_again);
}
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_STRENGTH_MEDIUM);
+
+ if (!mStatusBarStateController.isDozing()) {
+ mVibratorHelper.vibrate(
+ Process.myUid(),
+ mView.getContext().getPackageName(),
+ ADDITIONAL_TAP_REQUIRED_VIBRATION_EFFECT,
+ "falsing-additional-tap-required",
+ TOUCH_VIBRATION_ATTRIBUTES);
+ }
}
};
@@ -2217,7 +2230,8 @@ public final class NotificationPanelViewController extends PanelViewController {
if (cancel) {
collapse(false /* delayed */, 1.0f /* speedUpFactor */);
} else {
- maybeVibrateOnOpening();
+ // Window never will receive touch events that typically trigger haptic on open.
+ maybeVibrateOnOpening(false /* openingWithTouch */);
fling(velocity > 1f ? 1000f * velocity : 0, true /* expand */);
}
onTrackingStopped(false);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index abafecce2965..6be9bbbf4e0d 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -31,10 +31,15 @@ import android.view.ViewGroup;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.LockIconViewController;
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -73,7 +78,7 @@ public class NotificationShadeWindowViewController {
private final AmbientState mAmbientState;
private final PulsingGestureListener mPulsingGestureListener;
- private GestureDetector mGestureDetector;
+ private GestureDetector mPulsingWakeupGestureHandler;
private View mBrightnessMirror;
private boolean mTouchActive;
private boolean mTouchCancelled;
@@ -108,7 +113,10 @@ public class NotificationShadeWindowViewController {
NotificationShadeWindowController controller,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
AmbientState ambientState,
- PulsingGestureListener pulsingGestureListener
+ PulsingGestureListener pulsingGestureListener,
+ FeatureFlags featureFlags,
+ KeyguardBouncerViewModel keyguardBouncerViewModel,
+ KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory
) {
mLockscreenShadeTransitionController = transitionController;
mFalsingCollector = falsingCollector;
@@ -130,6 +138,12 @@ public class NotificationShadeWindowViewController {
// This view is not part of the newly inflated expanded status bar.
mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container);
+ if (featureFlags.isEnabled(Flags.MODERN_BOUNCER)) {
+ KeyguardBouncerViewBinder.bind(
+ mView.findViewById(R.id.keyguard_bouncer_container),
+ keyguardBouncerViewModel,
+ keyguardBouncerComponentFactory);
+ }
}
/**
@@ -149,7 +163,8 @@ public class NotificationShadeWindowViewController {
/** Inflates the {@link R.layout#status_bar_expanded} layout and sets it up. */
public void setupExpandedStatusBar() {
mStackScrollLayout = mView.findViewById(R.id.notification_stack_scroller);
- mGestureDetector = new GestureDetector(mView.getContext(), mPulsingGestureListener);
+ mPulsingWakeupGestureHandler = new GestureDetector(mView.getContext(),
+ mPulsingGestureListener);
mView.setInteractionEventHandler(new NotificationShadeWindowView.InteractionEventHandler() {
@Override
@@ -196,7 +211,7 @@ public class NotificationShadeWindowViewController {
}
mFalsingCollector.onTouchEvent(ev);
- mGestureDetector.onTouchEvent(ev);
+ mPulsingWakeupGestureHandler.onTouchEvent(ev);
mStatusBarKeyguardViewManager.onTouch(ev);
if (mBrightnessMirror != null
&& mBrightnessMirror.getVisibility() == View.VISIBLE) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
index c3f1e571ab87..b4ce95c434fc 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/PanelViewController.java
@@ -96,6 +96,7 @@ public abstract class PanelViewController {
private float mMinExpandHeight;
private boolean mPanelUpdateWhenAnimatorEnds;
private final boolean mVibrateOnOpening;
+ private boolean mHasVibratedOnOpen = false;
protected boolean mIsLaunchAnimationRunning;
private int mFixedDuration = NO_FIXED_DURATION;
protected float mOverExpansion;
@@ -353,8 +354,8 @@ public abstract class PanelViewController {
private void startOpening(MotionEvent event) {
updatePanelExpansionAndVisibility();
- maybeVibrateOnOpening();
-
+ // Reset at start so haptic can be triggered as soon as panel starts to open.
+ mHasVibratedOnOpen = false;
//TODO: keyguard opens QS a different way; log that too?
// Log the position of the swipe that opened the panel
@@ -368,9 +369,18 @@ public abstract class PanelViewController {
.log(LockscreenUiEvent.LOCKSCREEN_UNLOCKED_NOTIFICATION_PANEL_EXPAND);
}
- protected void maybeVibrateOnOpening() {
+ /**
+ * Maybe vibrate as panel is opened.
+ *
+ * @param openingWithTouch Whether the panel is being opened with touch. If the panel is instead
+ * being opened programmatically (such as by the open panel gesture), we always play haptic.
+ */
+ protected void maybeVibrateOnOpening(boolean openingWithTouch) {
if (mVibrateOnOpening) {
- mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ if (!openingWithTouch || !mHasVibratedOnOpen) {
+ mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
+ mHasVibratedOnOpen = true;
+ }
}
}
@@ -1371,6 +1381,9 @@ public abstract class PanelViewController {
break;
case MotionEvent.ACTION_MOVE:
addMovement(event);
+ if (!isFullyCollapsed()) {
+ maybeVibrateOnOpening(true /* openingWithTouch */);
+ }
float h = y - mInitialExpandY;
// If the panel was collapsed when touching, we only need to check for the
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
index 9b3fe92f9225..084b7dc3a646 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
@@ -26,6 +26,7 @@ import com.android.systemui.Dumpable
import com.android.systemui.dock.DockManager
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.CentralSurfaces
import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
@@ -36,12 +37,12 @@ import javax.inject.Inject
/**
* If tap and/or double tap to wake is enabled, this gestureListener will wake the display on
- * tap/double tap when the device is pulsing (AoD 2). Taps are gated by the proximity sensor and
- * falsing manager.
+ * tap/double tap when the device is pulsing (AoD2) or transitioning to AoD. Taps are gated by the
+ * proximity sensor and falsing manager.
*
- * Touches go through the [NotificationShadeWindowViewController] when the device is pulsing.
- * Otherwise, if the device is dozing and NOT pulsing, wake-ups are handled by
- * [com.android.systemui.doze.DozeSensors].
+ * Touches go through the [NotificationShadeWindowViewController] when the device is dozing but the
+ * screen is still ON and not in the true AoD display state. When the device is in the true AoD
+ * display state, wake-ups are handled by [com.android.systemui.doze.DozeSensors].
*/
@CentralSurfacesComponent.CentralSurfacesScope
class PulsingGestureListener @Inject constructor(
@@ -75,12 +76,12 @@ class PulsingGestureListener @Inject constructor(
dumpManager.registerDumpable(this)
}
- override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
- if (statusBarStateController.isPulsing &&
+ override fun onSingleTapUp(e: MotionEvent): Boolean {
+ if (statusBarStateController.isDozing &&
singleTapEnabled &&
!dockManager.isDocked &&
!falsingManager.isProximityNear &&
- !falsingManager.isFalseTap(FalsingManager.MODERATE_PENALTY)
+ !falsingManager.isFalseTap(LOW_PENALTY)
) {
centralSurfaces.wakeUpIfDozing(
SystemClock.uptimeMillis(),
@@ -91,8 +92,15 @@ class PulsingGestureListener @Inject constructor(
return false
}
- override fun onDoubleTap(e: MotionEvent): Boolean {
- if (statusBarStateController.isPulsing &&
+ /**
+ * Receives [MotionEvent.ACTION_DOWN], [MotionEvent.ACTION_MOVE], and [MotionEvent.ACTION_UP]
+ * motion events for a double tap.
+ */
+ override fun onDoubleTapEvent(e: MotionEvent): Boolean {
+ // React to the [MotionEvent.ACTION_UP] event after double tap is detected. Falsing
+ // checks MUST be on the ACTION_UP event.
+ if (e.actionMasked == MotionEvent.ACTION_UP &&
+ statusBarStateController.isDozing &&
(doubleTapEnabled || singleTapEnabled) &&
!falsingManager.isProximityNear &&
!falsingManager.isFalseDoubleTap
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 408c61f8f387..073ab8b16864 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -19,9 +19,6 @@ package com.android.systemui.statusbar;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_MANAGEMENT_DISCLOSURE;
import static android.app.admin.DevicePolicyResources.Strings.SystemUi.KEYGUARD_NAMED_MANAGEMENT_DISCLOSURE;
-import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_FIRST_FRAME_RECEIVED;
-import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_GOOD;
-import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_START;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
import static android.hardware.biometrics.BiometricSourceType.FACE;
import static android.view.View.GONE;
@@ -53,6 +50,7 @@ import android.content.pm.UserInfo;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Color;
+import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
@@ -82,7 +80,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.Utils;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
-import com.android.systemui.biometrics.BiometricMessageDeferral;
+import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -184,6 +182,7 @@ public class KeyguardIndicationController {
private long mChargingTimeRemaining;
private String mBiometricErrorMessageToShowOnScreenOn;
private final Set<Integer> mCoExFaceAcquisitionMsgIdsToShow;
+ private final FaceHelpMessageDeferral mFaceAcquiredMessageDeferral;
private boolean mInited;
private KeyguardUpdateMonitorCallback mUpdateMonitorCallback;
@@ -233,7 +232,8 @@ public class KeyguardIndicationController {
LockPatternUtils lockPatternUtils,
ScreenLifecycle screenLifecycle,
KeyguardBypassController keyguardBypassController,
- AccessibilityManager accessibilityManager) {
+ AccessibilityManager accessibilityManager,
+ FaceHelpMessageDeferral faceHelpMessageDeferral) {
mContext = context;
mBroadcastDispatcher = broadcastDispatcher;
mDevicePolicyManager = devicePolicyManager;
@@ -254,6 +254,7 @@ public class KeyguardIndicationController {
mScreenLifecycle = screenLifecycle;
mScreenLifecycle.addObserver(mScreenObserver);
+ mFaceAcquiredMessageDeferral = faceHelpMessageDeferral;
mCoExFaceAcquisitionMsgIdsToShow = new HashSet<>();
int[] msgIds = context.getResources().getIntArray(
com.android.systemui.R.array.config_face_help_msgs_when_fingerprint_enrolled);
@@ -1041,8 +1042,22 @@ public class KeyguardIndicationController {
}
@Override
+ public void onBiometricAcquired(BiometricSourceType biometricSourceType, int acquireInfo) {
+ if (biometricSourceType == FACE) {
+ mFaceAcquiredMessageDeferral.processFrame(acquireInfo);
+ }
+ }
+
+ @Override
public void onBiometricHelp(int msgId, String helpString,
BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == FACE) {
+ mFaceAcquiredMessageDeferral.updateMessage(msgId, helpString);
+ if (mFaceAcquiredMessageDeferral.shouldDefer(msgId)) {
+ return;
+ }
+ }
+
// TODO(b/141025588): refactor to reduce repetition of code/comments
// Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
// as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
@@ -1053,17 +1068,6 @@ public class KeyguardIndicationController {
return;
}
- if (biometricSourceType == FACE) {
- if (msgId == KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED) {
- mFaceAcquiredMessageDeferral.reset();
- } else {
- mFaceAcquiredMessageDeferral.processMessage(msgId, helpString);
- if (mFaceAcquiredMessageDeferral.shouldDefer(msgId)) {
- return;
- }
- }
- }
-
final boolean faceAuthSoftError = biometricSourceType == FACE
&& msgId != BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
final boolean faceAuthFailed = biometricSourceType == FACE
@@ -1109,11 +1113,23 @@ public class KeyguardIndicationController {
}
@Override
+ public void onBiometricAuthFailed(BiometricSourceType biometricSourceType) {
+ if (biometricSourceType == FACE) {
+ mFaceAcquiredMessageDeferral.reset();
+ }
+ }
+
+ @Override
public void onBiometricError(int msgId, String errString,
BiometricSourceType biometricSourceType) {
CharSequence deferredFaceMessage = null;
if (biometricSourceType == FACE) {
- deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
+ if (msgId == BiometricFaceConstants.FACE_ERROR_TIMEOUT) {
+ deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
+ if (DEBUG) {
+ Log.d(TAG, "showDeferredFaceMessage msgId=" + deferredFaceMessage);
+ }
+ }
mFaceAcquiredMessageDeferral.reset();
}
@@ -1183,7 +1199,8 @@ public class KeyguardIndicationController {
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
&& msgId != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
- || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED);
+ || msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
+ || msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED);
}
private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
@@ -1308,14 +1325,4 @@ public class KeyguardIndicationController {
}
}
};
-
- private final BiometricMessageDeferral mFaceAcquiredMessageDeferral =
- new BiometricMessageDeferral(
- Set.of(
- FACE_ACQUIRED_GOOD,
- FACE_ACQUIRED_START,
- FACE_ACQUIRED_FIRST_FRAME_RECEIVED
- ),
- Set.of(FACE_ACQUIRED_TOO_DARK)
- );
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 41c0367772b5..d67f94f11e65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -168,6 +168,17 @@ public class NotificationShelf extends ActivatableNotificationView implements
return new ShelfState();
}
+ @Override
+ public String toString() {
+ return "NotificationShelf("
+ + "hideBackground=" + mHideBackground + " notGoneIndex=" + mNotGoneIndex
+ + " hasItemsInStableShelf=" + mHasItemsInStableShelf
+ + " statusBarState=" + mStatusBarState + " interactive=" + mInteractive
+ + " animationsEnabled=" + mAnimationsEnabled
+ + " showNotificationShelf=" + mShowNotificationShelf
+ + " indexOfFirstViewInShelf=" + mIndexOfFirstViewInShelf + ')';
+ }
+
/** Update the state of the shelf. */
public void updateState(StackScrollAlgorithm.StackScrollAlgorithmState algorithmState,
AmbientState ambientState) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 039a3625c70c..827d0d0f8444 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -36,7 +36,6 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
-import android.os.Parcelable;
import android.os.Trace;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -551,9 +550,12 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
}
}
+ @Override
public String toString() {
- return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon
- + " notification=" + mNotification + ")";
+ return "StatusBarIconView("
+ + "slot='" + mSlot + " alpha=" + getAlpha() + " icon=" + mIcon
+ + " iconColor=#" + Integer.toHexString(mIconColor)
+ + " notification=" + mNotification + ')';
}
public StatusBarNotification getNotification() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
deleted file mode 100644
index 4551807499ab..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/UserUtil.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.systemui.statusbar;
-
-import android.content.Context;
-import android.content.DialogInterface;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.policy.UserSwitcherController;
-
-public class UserUtil {
- public static void deleteUserWithPrompt(Context context, int userId,
- UserSwitcherController userSwitcherController) {
- new RemoveUserDialog(context, userId, userSwitcherController).show();
- }
-
- private final static class RemoveUserDialog extends SystemUIDialog implements
- DialogInterface.OnClickListener {
-
- private final int mUserId;
- private final UserSwitcherController mUserSwitcherController;
-
- public RemoveUserDialog(Context context, int userId,
- UserSwitcherController userSwitcherController) {
- super(context);
- setTitle(R.string.user_remove_user_title);
- setMessage(context.getString(R.string.user_remove_user_message));
- setButton(DialogInterface.BUTTON_NEUTRAL,
- context.getString(android.R.string.cancel), this);
- setButton(DialogInterface.BUTTON_POSITIVE,
- context.getString(R.string.user_remove_user_remove), this);
- setCanceledOnTouchOutside(false);
- mUserId = userId;
- mUserSwitcherController = userSwitcherController;
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- if (which == BUTTON_NEUTRAL) {
- cancel();
- } else {
- dismiss();
- mUserSwitcherController.removeUserId(mUserId);
- }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 867be1ab305f..c070fccf9808 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -40,7 +40,7 @@ import javax.inject.Inject;
public class VibratorHelper {
private final Vibrator mVibrator;
- private static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
+ public static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
private static final VibrationEffect BIOMETRIC_SUCCESS_VIBRATION_EFFECT =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
index 31b21c9b5321..553826dda919 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationLaunchAnimatorController.kt
@@ -136,7 +136,7 @@ class NotificationLaunchAnimatorController(
headsUpManager.removeNotification(notificationKey, true /* releaseImmediately */, animate)
}
- override fun onLaunchAnimationCancelled() {
+ override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
// TODO(b/184121838): Should we call InteractionJankMonitor.cancel if the animation started
// here?
notificationShadeWindowViewController.setExpandAnimationRunning(false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt
new file mode 100644
index 000000000000..fe03b2ad6a32
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.logging
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.dagger.NotificationRenderLog
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
+import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.stack.NotificationSection
+import javax.inject.Inject
+
+/** Handles logging for the {NotificationRoundnessManager}. */
+class NotificationRoundnessLogger
+@Inject
+constructor(@NotificationRenderLog val buffer: LogBuffer) {
+
+ /** Called when the {NotificationRoundnessManager} updates the corners if the Notifications. */
+ fun onCornersUpdated(
+ view: ExpandableView?,
+ isFirstInSection: Boolean,
+ isLastInSection: Boolean,
+ topChanged: Boolean,
+ bottomChanged: Boolean
+ ) {
+ buffer.log(
+ TAG_ROUNDNESS,
+ INFO,
+ {
+ str1 = (view as? ExpandableNotificationRow)?.entry?.key
+ bool1 = isFirstInSection
+ bool2 = isLastInSection
+ bool3 = topChanged
+ bool4 = bottomChanged
+ },
+ {
+ "onCornersUpdated: " +
+ "entry=$str1 isFirstInSection=$bool1 isLastInSection=$bool2 " +
+ "topChanged=$bool3 bottomChanged=$bool4"
+ }
+ )
+ }
+
+ /** Called when we update the {NotificationRoundnessManager} with new sections. */
+ fun onSectionCornersUpdated(sections: Array<NotificationSection?>, anyChanged: Boolean) {
+ buffer.log(
+ TAG_ROUNDNESS,
+ INFO,
+ {
+ int1 = sections.size
+ bool1 = anyChanged
+ },
+ { "onSectionCornersUpdated: sections size=$int1 anyChanged=$bool1" }
+ )
+ }
+}
+
+private const val TAG_ROUNDNESS = "NotifRoundnessLogger"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
index ba26cfaa30b4..df81c0ed3a61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentView.java
@@ -1369,6 +1369,8 @@ public class NotificationContentView extends FrameLayout implements Notification
}
ImageView bubbleButton = layout.findViewById(com.android.internal.R.id.bubble_button);
View actionContainer = layout.findViewById(com.android.internal.R.id.actions_container);
+ LinearLayout actionListMarginTarget = layout.findViewById(
+ com.android.internal.R.id.notification_action_list_margin_target);
if (bubbleButton == null || actionContainer == null) {
return;
}
@@ -1393,6 +1395,16 @@ public class NotificationContentView extends FrameLayout implements Notification
bubbleButton.setOnClickListener(mContainingNotification.getBubbleClickListener());
bubbleButton.setVisibility(VISIBLE);
actionContainer.setVisibility(VISIBLE);
+ // Set notification_action_list_margin_target's bottom margin to 0 when showing bubble
+ if (actionListMarginTarget != null) {
+ ViewGroup.LayoutParams lp = actionListMarginTarget.getLayoutParams();
+ if (lp instanceof ViewGroup.MarginLayoutParams) {
+ final ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) lp;
+ if (mlp.bottomMargin > 0) {
+ mlp.setMargins(mlp.leftMargin, mlp.topMargin, mlp.rightMargin, 0);
+ }
+ }
+ }
} else {
bubbleButton.setVisibility(GONE);
}
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 b589d9ae1abf..2015c87aac2b 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
@@ -19,12 +19,19 @@ package com.android.systemui.statusbar.notification.stack;
import android.content.res.Resources;
import android.util.MathUtils;
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotificationRoundnessLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
+import java.io.PrintWriter;
import java.util.HashSet;
import javax.inject.Inject;
@@ -33,12 +40,16 @@ import javax.inject.Inject;
* A class that manages the roundness for notification views
*/
@SysUISingleton
-public class NotificationRoundnessManager {
+public class NotificationRoundnessManager implements Dumpable {
+
+ private static final String TAG = "NotificationRoundnessManager";
private final ExpandableView[] mFirstInSectionViews;
private final ExpandableView[] mLastInSectionViews;
private final ExpandableView[] mTmpFirstInSectionViews;
private final ExpandableView[] mTmpLastInSectionViews;
+ private final NotificationRoundnessLogger mNotifLogger;
+ private final DumpManager mDumpManager;
private boolean mExpanded;
private HashSet<ExpandableView> mAnimatedChildren;
private Runnable mRoundingChangedCallback;
@@ -53,12 +64,31 @@ public class NotificationRoundnessManager {
@Inject
NotificationRoundnessManager(
- NotificationSectionsFeatureManager sectionsFeatureManager) {
+ NotificationSectionsFeatureManager sectionsFeatureManager,
+ NotificationRoundnessLogger notifLogger,
+ DumpManager dumpManager) {
int numberOfSections = sectionsFeatureManager.getNumberOfBuckets();
mFirstInSectionViews = new ExpandableView[numberOfSections];
mLastInSectionViews = new ExpandableView[numberOfSections];
mTmpFirstInSectionViews = new ExpandableView[numberOfSections];
mTmpLastInSectionViews = new ExpandableView[numberOfSections];
+ mNotifLogger = notifLogger;
+ mDumpManager = dumpManager;
+
+ mDumpManager.registerDumpable(TAG, this);
+ }
+
+ @Override
+ public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
+ pw.println("mFirstInSectionViews: length=" + mFirstInSectionViews.length);
+ pw.println(dumpViews(mFirstInSectionViews));
+ pw.println("mLastInSectionViews: length=" + mLastInSectionViews.length);
+ pw.println(dumpViews(mFirstInSectionViews));
+ if (mTrackedHeadsUp != null) {
+ pw.println("trackedHeadsUp=" + mTrackedHeadsUp.getEntry());
+ }
+ pw.println("roundForPulsingViews=" + mRoundForPulsingViews);
+ pw.println("isClearAllInProgress=" + mIsClearAllInProgress);
}
public void updateView(ExpandableView view, boolean animate) {
@@ -95,6 +125,9 @@ public class NotificationRoundnessManager {
view.setFirstInSection(isFirstInSection);
view.setLastInSection(isLastInSection);
+ mNotifLogger.onCornersUpdated(view, isFirstInSection,
+ isLastInSection, topChanged, bottomChanged);
+
return (isFirstInSection || isLastInSection) && (topChanged || bottomChanged);
}
@@ -184,6 +217,7 @@ public class NotificationRoundnessManager {
if (isLastInSection(view) && !top) {
return 1.0f;
}
+
if (view == mTrackedHeadsUp) {
// If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be
// rounded.
@@ -220,6 +254,8 @@ public class NotificationRoundnessManager {
if (anyChanged) {
mRoundingChangedCallback.run();
}
+
+ mNotifLogger.onSectionCornersUpdated(sections, anyChanged);
}
private boolean handleRemovedOldViews(NotificationSection[] sections,
@@ -296,4 +332,36 @@ public class NotificationRoundnessManager {
public void setShouldRoundPulsingViews(boolean shouldRoundPulsingViews) {
mRoundForPulsingViews = shouldRoundPulsingViews;
}
+
+ private String dumpViews(ExpandableView[] views) {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < views.length; i++) {
+ if (views[i] == null) continue;
+
+ sb.append("\t")
+ .append("[").append(i).append("] ")
+ .append("isPinned=").append(views[i].isPinned()).append(" ")
+ .append("isFirstInSection=").append(views[i].isFirstInSection()).append(" ")
+ .append("isLastInSection=").append(views[i].isLastInSection()).append(" ");
+
+ if (views[i] instanceof ExpandableNotificationRow) {
+ sb.append("entry=");
+ dumpEntry(((ExpandableNotificationRow) views[i]).getEntry(), sb);
+ }
+
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ private void dumpEntry(NotificationEntry entry, StringBuilder sb) {
+ sb.append("NotificationEntry{key=").append(entry.getKey()).append(" ");
+
+ if (entry.getSection() != null) {
+ sb.append(" section=")
+ .append(entry.getSection().getLabel());
+ }
+
+ sb.append("}");
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 0b2d443598d2..4576a6442838 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -23,7 +23,6 @@ import android.content.res.Resources;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricSourceType;
-import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Handler;
@@ -654,10 +653,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
final boolean fingerprintLockout = biometricSourceType == BiometricSourceType.FINGERPRINT
&& (msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT
|| msgId == FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT);
- final boolean faceLockout = biometricSourceType == BiometricSourceType.FACE
- && (msgId == FaceManager.FACE_ERROR_LOCKOUT
- || msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT);
- if (fingerprintLockout || faceLockout) {
+ if (fingerprintLockout) {
startWakeAndUnlock(MODE_SHOW_BOUNCER);
UI_EVENT_LOGGER.log(BiometricUiEvent.BIOMETRIC_BOUNCER_SHOWN, getSessionId());
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 7463dac6b5d2..f7ce43bfb25c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -99,11 +99,14 @@ import android.view.MotionEvent;
import android.view.ThreadedRenderer;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.view.accessibility.AccessibilityManager;
import android.widget.DateTimeView;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
import androidx.annotation.NonNull;
import androidx.lifecycle.Lifecycle;
@@ -509,10 +512,17 @@ public class CentralSurfacesImpl extends CoreStartable implements
private CentralSurfacesComponent mCentralSurfacesComponent;
// Flags for disabling the status bar
- // Two variables becaseu the first one evidently ran out of room for new flags.
+ // Two variables because the first one evidently ran out of room for new flags.
private int mDisabled1 = 0;
private int mDisabled2 = 0;
+ /**
+ * This keeps track of whether we have (or haven't) registered the predictive back callback.
+ * Since we can have visible -> visible transitions, we need to avoid
+ * double-registering (or double-unregistering) our callback.
+ */
+ private boolean mIsBackCallbackRegistered = false;
+
/** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
private @Appearance int mAppearance;
@@ -636,6 +646,12 @@ public class CentralSurfacesImpl extends CoreStartable implements
private final InteractionJankMonitor mJankMonitor;
+ private final OnBackInvokedCallback mOnBackInvokedCallback = () -> {
+ if (DEBUG) {
+ Log.d(TAG, "mOnBackInvokedCallback() called");
+ }
+ onBackPressed();
+ };
/**
* Public constructor for CentralSurfaces.
@@ -1703,13 +1719,18 @@ public class CentralSurfacesImpl extends CoreStartable implements
}
@Override
- public void onLaunchAnimationCancelled() {
+ public void onLaunchAnimationCancelled(@Nullable Boolean newKeyguardOccludedState) {
+ if (newKeyguardOccludedState != null) {
+ mKeyguardViewMediator.setOccluded(
+ newKeyguardOccludedState, false /* animate */);
+ }
+
// 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.
CentralSurfacesImpl.this.mIsLaunchingActivityOverLockscreen = false;
- getDelegate().onLaunchAnimationCancelled();
+ getDelegate().onLaunchAnimationCancelled(newKeyguardOccludedState);
}
};
} else if (dismissShade) {
@@ -2731,9 +2752,38 @@ public class CentralSurfacesImpl extends CoreStartable implements
if (visibleToUser) {
handleVisibleToUserChangedImpl(visibleToUser);
mNotificationLogger.startNotificationLogging();
+
+ if (!mIsBackCallbackRegistered) {
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.getOnBackInvokedDispatcher()
+ .registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = true;
+ if (DEBUG) Log.d(TAG, "is now VISIBLE to user AND callback registered");
+ }
+ } else {
+ if (DEBUG) Log.d(TAG, "is now VISIBLE to user, BUT callback ALREADY unregistered");
+ }
} else {
mNotificationLogger.stopNotificationLogging();
handleVisibleToUserChangedImpl(visibleToUser);
+
+ if (mIsBackCallbackRegistered) {
+ ViewRootImpl viewRootImpl = getViewRootImpl();
+ if (viewRootImpl != null) {
+ viewRootImpl.getOnBackInvokedDispatcher()
+ .unregisterOnBackInvokedCallback(mOnBackInvokedCallback);
+ mIsBackCallbackRegistered = false;
+ if (DEBUG) Log.d(TAG, "is NOT VISIBLE to user, AND callback unregistered");
+ }
+ } else {
+ if (DEBUG) {
+ Log.d(TAG,
+ "is NOT VISIBLE to user, BUT NO callback (or callback ALREADY "
+ + "unregistered)");
+ }
+ }
}
}
@@ -3298,7 +3348,8 @@ public class CentralSurfacesImpl extends CoreStartable implements
// lock screen where users can use the UDFPS affordance to enter the device
mStatusBarKeyguardViewManager.reset(true);
} else if (mState == StatusBarState.KEYGUARD
- && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()) {
+ && !mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()
+ && isKeyguardSecure()) {
mStatusBarKeyguardViewManager.showGenericBouncer(true /* scrimmed */);
}
}
@@ -3444,6 +3495,12 @@ public class CentralSurfacesImpl extends CoreStartable implements
return mNotificationPanelViewController.getKeyguardBottomAreaView();
}
+ protected ViewRootImpl getViewRootImpl() {
+ NotificationShadeWindowView nswv = getNotificationShadeWindowView();
+ if (nswv != null) return nswv.getViewRootImpl();
+
+ return null;
+ }
/**
* Propagation of the bouncer state, indicating that it's fully visible.
*/
@@ -3552,6 +3609,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
dismissVolumeDialog();
mWakeUpCoordinator.setFullyAwake(false);
mKeyguardBypassController.onStartedGoingToSleep();
+ mStatusBarTouchableRegionManager.updateTouchableRegion();
// The unlocked screen off and fold to aod animations might use our LightRevealScrim -
// we need to be expanded for it to be visible.
@@ -3580,6 +3638,7 @@ public class CentralSurfacesImpl extends CoreStartable implements
// once we fully woke up.
updateRevealEffect(true /* wakingUp */);
updateNotificationPanelTouchState();
+ mStatusBarTouchableRegionManager.updateTouchableRegion();
// If we are waking up during the screen off animation, we should undo making the
// expanded visible (we did that so the LightRevealScrim would be visible).
@@ -3761,6 +3820,12 @@ public class CentralSurfacesImpl extends CoreStartable implements
updateScrimController();
}
+ @VisibleForTesting
+ public void setNotificationShadeWindowViewController(
+ NotificationShadeWindowViewController nswvc) {
+ mNotificationShadeWindowViewController = nswvc;
+ }
+
/**
* Set the amount of progress we are currently in if we're transitioning to the full shade.
* 0.0f means we're not transitioning yet, while 1 means we're all the way in the full
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index e63c383b010b..1dface21f540 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -56,7 +56,9 @@ import javax.inject.Inject;
/**
* A class which manages the bouncer on the lockscreen.
+ * @deprecated Use KeyguardBouncerRepository
*/
+@Deprecated
public class KeyguardBouncer {
private static final String TAG = "KeyguardBouncer";
@@ -220,6 +222,7 @@ public class KeyguardBouncer {
&& !mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
KeyguardUpdateMonitor.getCurrentUser())
&& !needsFullscreenBouncer()
+ && !mKeyguardUpdateMonitor.isFaceLockedOut()
&& !mKeyguardUpdateMonitor.userNeedsStrongAuth()
&& !mKeyguardBypassController.getBypassEnabled()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
index 4d6168989691..00c3e8fac0b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/MultiUserSwitchController.java
@@ -33,6 +33,7 @@ import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.qs.FooterActionsView;
import com.android.systemui.qs.dagger.QSScope;
import com.android.systemui.qs.user.UserSwitchDialogController;
+import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter;
import com.android.systemui.statusbar.policy.UserSwitcherController;
import com.android.systemui.user.UserSwitcherActivity;
import com.android.systemui.util.ViewController;
@@ -49,7 +50,7 @@ public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
private final ActivityStarter mActivityStarter;
private final FeatureFlags mFeatureFlags;
- private UserSwitcherController.BaseUserAdapter mUserListener;
+ private BaseUserSwitcherAdapter mUserListener;
private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
@Override
@@ -135,7 +136,7 @@ public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
final UserSwitcherController controller = mUserSwitcherController;
if (controller != null) {
- mUserListener = new UserSwitcherController.BaseUserAdapter(controller) {
+ mUserListener = new BaseUserSwitcherAdapter(controller) {
@Override
public void notifyDataSetChanged() {
mView.refreshContentDescription(getCurrentUser());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 7b8c5fc998fa..5a70d89908f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -278,6 +278,15 @@ public class NotificationIconContainer extends ViewGroup {
}
}
+ @Override
+ public String toString() {
+ return "NotificationIconContainer("
+ + "dozing=" + mDozing + " onLockScreen=" + mOnLockScreen
+ + " inNotificationIconShelf=" + mInNotificationIconShelf
+ + " speedBumpIndex=" + mSpeedBumpIndex
+ + " themedTextColorPrimary=#" + Integer.toHexString(mThemedTextColorPrimary) + ')';
+ }
+
@VisibleForTesting
public void setIconSize(int size) {
mIconSize = size;
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 985f3cd44808..76f2dd16bbee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -46,6 +46,7 @@ import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.AuthKeyguardMessageArea;
import com.android.keyguard.KeyguardMessageAreaController;
+import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
@@ -53,6 +54,12 @@ import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.data.BouncerView;
+import com.android.systemui.keyguard.data.BouncerViewDelegate;
+import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -123,6 +130,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Nullable
private final FoldAodAnimationController mFoldAodAnimationController;
private KeyguardMessageAreaController<AuthKeyguardMessageArea> mKeyguardMessageAreaController;
+ private final BouncerCallbackInteractor mBouncerCallbackInteractor;
+ private final BouncerInteractor mBouncerInteractor;
+ private final BouncerViewDelegate mBouncerViewDelegate;
private final Lazy<com.android.systemui.shade.ShadeController> mShadeController;
private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() {
@@ -194,10 +204,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
protected CentralSurfaces mCentralSurfaces;
private NotificationPanelViewController mNotificationPanelViewController;
private BiometricUnlockController mBiometricUnlockController;
+ private boolean mCentralSurfacesRegistered;
private View mNotificationContainer;
- protected KeyguardBouncer mBouncer;
+ @Nullable protected KeyguardBouncer mBouncer;
protected boolean mShowing;
protected boolean mOccluded;
protected boolean mRemoteInputActive;
@@ -223,6 +234,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private int mLastBiometricMode;
private boolean mLastScreenOffAnimationPlaying;
private float mQsExpansion;
+ private boolean mIsModernBouncerEnabled;
private OnDismissAction mAfterKeyguardGoneAction;
private Runnable mKeyguardGoneCancelAction;
@@ -237,6 +249,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private final DockManager mDockManager;
private final KeyguardUpdateMonitor mKeyguardUpdateManager;
private final LatencyTracker mLatencyTracker;
+ private final KeyguardSecurityModel mKeyguardSecurityModel;
private KeyguardBypassController mBypassController;
@Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor;
@@ -271,7 +284,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
Optional<SysUIUnfoldComponent> sysUIUnfoldComponent,
Lazy<ShadeController> shadeController,
- LatencyTracker latencyTracker) {
+ LatencyTracker latencyTracker,
+ KeyguardSecurityModel keyguardSecurityModel,
+ FeatureFlags featureFlags,
+ BouncerCallbackInteractor bouncerCallbackInteractor,
+ BouncerInteractor bouncerInteractor,
+ BouncerView bouncerView) {
mContext = context;
mViewMediatorCallback = callback;
mLockPatternUtils = lockPatternUtils;
@@ -288,8 +306,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
mShadeController = shadeController;
mLatencyTracker = latencyTracker;
+ mKeyguardSecurityModel = keyguardSecurityModel;
+ mBouncerCallbackInteractor = bouncerCallbackInteractor;
+ mBouncerInteractor = bouncerInteractor;
+ mBouncerViewDelegate = bouncerView.getDelegate();
mFoldAodAnimationController = sysUIUnfoldComponent
.map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
+ mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER);
}
@Override
@@ -303,7 +326,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBiometricUnlockController = biometricUnlockController;
ViewGroup container = mCentralSurfaces.getBouncerContainer();
- mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
+ if (mIsModernBouncerEnabled) {
+ mBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
+ } else {
+ mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback);
+ }
mNotificationPanelViewController = notificationPanelViewController;
if (panelExpansionStateManager != null) {
panelExpansionStateManager.addExpansionListener(this);
@@ -312,6 +339,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mNotificationContainer = notificationContainer;
mKeyguardMessageAreaController = mKeyguardMessageAreaFactory.create(
centralSurfaces.getKeyguardMessageArea());
+ mCentralSurfacesRegistered = true;
registerListeners();
}
@@ -377,29 +405,45 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mDozing && !mPulsing) {
return;
} else if (mNotificationPanelViewController.isUnlockHintRunning()) {
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ if (mBouncer != null) {
+ mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ }
+ mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
// Don't expand to the bouncer. Instead transition back to the lock screen (see
// CentralSurfaces#showBouncerOrLockScreenIfKeyguard)
return;
} else if (bouncerNeedsScrimming()) {
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ if (mBouncer != null) {
+ mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ }
+ mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else if (mShowing && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
&& !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
&& !mCentralSurfaces.isInLaunchTransition()
&& !isUnlockCollapsing()) {
- mBouncer.setExpansion(fraction);
+ if (mBouncer != null) {
+ mBouncer.setExpansion(fraction);
+ }
+ mBouncerInteractor.setExpansion(fraction);
}
if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking
&& !mKeyguardStateController.canDismissLockScreen()
- && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) {
- mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
+ && !bouncerIsShowing()
+ && !bouncerIsAnimatingAway()) {
+ if (mBouncer != null) {
+ mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */);
+ }
+ mBouncerInteractor.show(/* isScrimmed= */false);
}
- } else if (!mShowing && mBouncer.inTransit()) {
+ } else if (!mShowing && isBouncerInTransit()) {
// Keyguard is not visible anymore, but expansion animation was still running.
// We need to hide the bouncer, otherwise it will be stuck in transit.
- mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ if (mBouncer != null) {
+ mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ }
+ mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
// unlocked.) Let's simply wake-up to dismiss the lock screen.
@@ -440,15 +484,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* {@link KeyguardBouncer#needsFullscreenBouncer()}.
*/
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
- if (mBouncer.needsFullscreenBouncer() && !mDozing) {
+ if (needsFullscreenBouncer() && !mDozing) {
// The keyguard might be showing (already). So we need to hide it.
mCentralSurfaces.hideKeyguard();
- mBouncer.show(true /* resetSecuritySelection */);
+ if (mBouncer != null) {
+ mBouncer.show(true /* resetSecuritySelection */);
+ }
+ mBouncerInteractor.show(true);
} else {
mCentralSurfaces.showKeyguard();
if (hideBouncerWhenShowing) {
hideBouncer(false /* destroyView */);
- mBouncer.prepare();
+ if (mBouncer != null) {
+ mBouncer.prepare();
+ }
}
}
updateStates();
@@ -480,10 +529,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
@VisibleForTesting
void hideBouncer(boolean destroyView) {
- if (mBouncer == null) {
- return;
+ if (mBouncer != null) {
+ mBouncer.hide(destroyView);
}
- mBouncer.hide(destroyView);
+ mBouncerInteractor.hide();
if (mShowing) {
// If we were showing the bouncer and then aborting, we need to also clear out any
// potential actions unless we actually unlocked.
@@ -501,8 +550,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public void showBouncer(boolean scrimmed) {
resetAlternateAuth(false);
- if (mShowing && !mBouncer.isShowing()) {
- mBouncer.show(false /* resetSecuritySelection */, scrimmed);
+ if (mShowing && !isBouncerShowing()) {
+ if (mBouncer != null) {
+ mBouncer.show(false /* resetSecuritySelection */, scrimmed);
+ }
+ mBouncerInteractor.show(scrimmed);
}
updateStates();
}
@@ -535,7 +587,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// instead of the bouncer.
if (shouldShowAltAuth()) {
if (!afterKeyguardGone) {
- mBouncer.setDismissAction(mAfterKeyguardGoneAction,
+ if (mBouncer != null) {
+ mBouncer.setDismissAction(mAfterKeyguardGoneAction,
+ mKeyguardGoneCancelAction);
+ }
+ mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction,
mKeyguardGoneCancelAction);
mAfterKeyguardGoneAction = null;
mKeyguardGoneCancelAction = null;
@@ -549,12 +605,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (afterKeyguardGone) {
// we'll handle the dismiss action after keyguard is gone, so just show the
// bouncer
- mBouncer.show(false /* resetSecuritySelection */);
+ mBouncerInteractor.show(/* isScrimmed= */true);
+ if (mBouncer != null) mBouncer.show(false /* resetSecuritySelection */);
} else {
// after authentication success, run dismiss action with the option to defer
// hiding the keyguard based on the return value of the OnDismissAction
- mBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
- mKeyguardGoneCancelAction);
+ mBouncerInteractor.setDismissAction(
+ mAfterKeyguardGoneAction, mKeyguardGoneCancelAction);
+ mBouncerInteractor.show(/* isScrimmed= */true);
+ if (mBouncer != null) {
+ mBouncer.showWithDismissAction(mAfterKeyguardGoneAction,
+ mKeyguardGoneCancelAction);
+ }
// bouncer will handle the dismiss action, so we no longer need to track it here
mAfterKeyguardGoneAction = null;
mKeyguardGoneCancelAction = null;
@@ -591,7 +653,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// Hide bouncer and quick-quick settings.
if (mOccluded && !mDozing) {
mCentralSurfaces.hideKeyguard();
- if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
+ if (hideBouncerWhenShowing || needsFullscreenBouncer()) {
hideBouncer(false /* destroyView */);
}
} else {
@@ -655,7 +717,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void onFinishedGoingToSleep() {
- mBouncer.onScreenTurnedOff();
+ if (mBouncer != null) {
+ mBouncer.onScreenTurnedOff();
+ }
+ mBouncerInteractor.onScreenTurnedOff();
}
@Override
@@ -667,9 +732,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private void setDozing(boolean dozing) {
if (mDozing != dozing) {
mDozing = dozing;
- if (dozing || mBouncer.needsFullscreenBouncer() || mOccluded) {
- reset(dozing /* hideBouncerWhenShowing */);
- }
+ reset(true /* hideBouncerWhenShowing */);
updateStates();
if (!dozing) {
@@ -748,7 +811,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
// by a FLAG_DISMISS_KEYGUARD_ACTIVITY.
reset(isOccluding /* hideBouncerWhenShowing*/);
}
- if (animate && !mOccluded && mShowing && !mBouncer.isShowing()) {
+ if (animate && !mOccluded && mShowing && !bouncerIsShowing()) {
mCentralSurfaces.animateKeyguardUnoccluding();
}
}
@@ -764,8 +827,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void startPreHideAnimation(Runnable finishRunnable) {
- if (mBouncer.isShowing()) {
- mBouncer.startPreHideAnimation(finishRunnable);
+ if (bouncerIsShowing()) {
+ if (mBouncer != null) {
+ mBouncer.startPreHideAnimation(finishRunnable);
+ }
+ mBouncerInteractor.startDisappearAnimation(finishRunnable);
mCentralSurfaces.onBouncerPreHideAnimation();
// We update the state (which will show the keyguard) only if an animation will run on
@@ -875,8 +941,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
public void onThemeChanged() {
- boolean wasShowing = mBouncer.isShowing();
- boolean wasScrimmed = mBouncer.isScrimmed();
+ if (mIsModernBouncerEnabled) {
+ updateResources();
+ return;
+ }
+ boolean wasShowing = bouncerIsShowing();
+ boolean wasScrimmed = bouncerIsScrimmed();
hideBouncer(true /* destroyView */);
mBouncer.prepare();
@@ -925,7 +995,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* WARNING: This method might cause Binder calls.
*/
public boolean isSecure() {
- return mBouncer.isSecure();
+ if (mBouncer != null) {
+ return mBouncer.isSecure();
+ }
+
+ return mKeyguardSecurityModel.getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None;
}
@Override
@@ -942,10 +1017,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* @return whether the back press has been handled
*/
public boolean onBackPressed(boolean hideImmediately) {
- if (mBouncer.isShowing()) {
+ if (bouncerIsShowing()) {
mCentralSurfaces.endAffordanceLaunch();
// The second condition is for SIM card locked bouncer
- if (mBouncer.isScrimmed() && !mBouncer.needsFullscreenBouncer()) {
+ if (bouncerIsScrimmed()
+ && !needsFullscreenBouncer()) {
hideBouncer(false);
updateStates();
} else {
@@ -958,16 +1034,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public boolean isBouncerShowing() {
- return mBouncer.isShowing() || isShowingAlternateAuth();
+ return bouncerIsShowing() || isShowingAlternateAuth();
}
@Override
public boolean bouncerIsOrWillBeShowing() {
- return isBouncerShowing() || mBouncer.inTransit();
+ return isBouncerShowing() || isBouncerInTransit();
}
public boolean isFullscreenBouncer() {
- return mBouncer.isFullscreenBouncer();
+ if (mBouncerViewDelegate != null) {
+ return mBouncerViewDelegate.isFullScreenBouncer();
+ }
+ return mBouncer != null && mBouncer.isFullscreenBouncer();
}
/**
@@ -988,7 +1067,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private long getNavBarShowDelay() {
if (mKeyguardStateController.isKeyguardFadingAway()) {
return mKeyguardStateController.getKeyguardFadingAwayDelay();
- } else if (mBouncer.isShowing()) {
+ } else if (isBouncerShowing()) {
return NAV_BAR_SHOW_DELAY_BOUNCER;
} else {
// No longer dozing, or remote input is active. No delay.
@@ -1009,20 +1088,29 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
};
protected void updateStates() {
+ if (!mCentralSurfacesRegistered) {
+ return;
+ }
boolean showing = mShowing;
boolean occluded = mOccluded;
- boolean bouncerShowing = mBouncer.isShowing();
+ boolean bouncerShowing = bouncerIsShowing();
boolean bouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing();
- boolean bouncerDismissible = !mBouncer.isFullscreenBouncer();
+ boolean bouncerDismissible = !isFullscreenBouncer();
boolean remoteInputActive = mRemoteInputActive;
if ((bouncerDismissible || !showing || remoteInputActive) !=
(mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive)
|| mFirstUpdate) {
if (bouncerDismissible || !showing || remoteInputActive) {
- mBouncer.setBackButtonEnabled(true);
+ if (mBouncer != null) {
+ mBouncer.setBackButtonEnabled(true);
+ }
+ mBouncerInteractor.setBackButtonEnabled(true);
} else {
- mBouncer.setBackButtonEnabled(false);
+ if (mBouncer != null) {
+ mBouncer.setBackButtonEnabled(false);
+ }
+ mBouncerInteractor.setBackButtonEnabled(false);
}
}
@@ -1099,7 +1187,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|| mPulsing && !mIsDocked)
&& mGesturalNav;
return (!keyguardShowing && !hideWhileDozing && !mScreenOffAnimationPlaying
- || mBouncer.isShowing() || mRemoteInputActive || keyguardWithGestureNav
+ || bouncerIsShowing()
+ || mRemoteInputActive
+ || keyguardWithGestureNav
|| mGlobalActionsVisible);
}
@@ -1118,18 +1208,27 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
public boolean shouldDismissOnMenuPressed() {
- return mBouncer.shouldDismissOnMenuPressed();
+ if (mBouncerViewDelegate != null) {
+ return mBouncerViewDelegate.shouldDismissOnMenuPressed();
+ }
+ return mBouncer != null && mBouncer.shouldDismissOnMenuPressed();
}
public boolean interceptMediaKey(KeyEvent event) {
- return mBouncer.interceptMediaKey(event);
+ if (mBouncerViewDelegate != null) {
+ return mBouncerViewDelegate.interceptMediaKey(event);
+ }
+ return mBouncer != null && mBouncer.interceptMediaKey(event);
}
/**
* @return true if the pre IME back event should be handled
*/
public boolean dispatchBackKeyEventPreIme() {
- return mBouncer.dispatchBackKeyEventPreIme();
+ if (mBouncerViewDelegate != null) {
+ return mBouncerViewDelegate.dispatchBackKeyEventPreIme();
+ }
+ return mBouncer != null && mBouncer.dispatchBackKeyEventPreIme();
}
public void readyForKeyguardDone() {
@@ -1152,7 +1251,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
public boolean isSecure(int userId) {
- return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId);
+ return isSecure() || mLockPatternUtils.isSecure(userId);
}
@Override
@@ -1175,7 +1274,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* fingerprint.
*/
public void notifyKeyguardAuthenticated(boolean strongAuth) {
- mBouncer.notifyKeyguardAuthenticated(strongAuth);
+ if (mBouncer != null) {
+ mBouncer.notifyKeyguardAuthenticated(strongAuth);
+ }
+ mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) {
resetAlternateAuth(false);
@@ -1190,7 +1292,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mKeyguardMessageAreaController.setMessage(message);
}
} else {
- mBouncer.showMessage(message, colorState);
+ if (mBouncer != null) {
+ mBouncer.showMessage(message, colorState);
+ }
+ mBouncerInteractor.showMessage(message, colorState);
}
}
@@ -1223,9 +1328,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
public boolean bouncerNeedsScrimming() {
// When a dream overlay is active, scrimming will cause any expansion to immediately expand.
return (mOccluded && !mDreamOverlayStateController.isOverlayActive())
- || mBouncer.willDismissWithAction()
- || (mBouncer.isShowing() && mBouncer.isScrimmed())
- || mBouncer.isFullscreenBouncer();
+ || bouncerWillDismissWithAction()
+ || (bouncerIsShowing()
+ && bouncerIsScrimmed())
+ || isFullscreenBouncer();
}
/**
@@ -1237,6 +1343,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mBouncer != null) {
mBouncer.updateResources();
}
+ mBouncerInteractor.updateResources();
}
public void dump(PrintWriter pw) {
@@ -1290,6 +1397,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
+ @Nullable
public KeyguardBouncer getBouncer() {
return mBouncer;
}
@@ -1321,6 +1429,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mBouncer != null) {
mBouncer.updateKeyguardPosition(x);
}
+
+ mBouncerInteractor.setKeyguardPosition(x);
}
private static class DismissWithActionRequest {
@@ -1360,9 +1470,65 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
* Returns if bouncer expansion is between 0 and 1 non-inclusive.
*/
public boolean isBouncerInTransit() {
- if (mBouncer == null) return false;
+ if (mBouncer != null) {
+ return mBouncer.inTransit();
+ }
+
+ return mBouncerInteractor.isInTransit();
+ }
+
+ /**
+ * Returns if bouncer is showing
+ */
+ public boolean bouncerIsShowing() {
+ if (mBouncer != null) {
+ return mBouncer.isShowing();
+ }
+
+ return mBouncerInteractor.isFullyShowing();
+ }
- return mBouncer.inTransit();
+ /**
+ * Returns if bouncer is scrimmed
+ */
+ public boolean bouncerIsScrimmed() {
+ if (mBouncer != null) {
+ return mBouncer.isScrimmed();
+ }
+
+ return mBouncerInteractor.isScrimmed();
+ }
+
+ /**
+ * Returns if bouncer is animating away
+ */
+ public boolean bouncerIsAnimatingAway() {
+ if (mBouncer != null) {
+ return mBouncer.isAnimatingAway();
+ }
+
+ return mBouncerInteractor.isAnimatingAway();
+ }
+
+ /**
+ * Returns if bouncer will dismiss with action
+ */
+ public boolean bouncerWillDismissWithAction() {
+ if (mBouncer != null) {
+ return mBouncer.willDismissWithAction();
+ }
+
+ return mBouncerInteractor.willDismissWithAction();
+ }
+
+ /**
+ * Returns if bouncer needs fullscreen bouncer. i.e. sim pin security method
+ */
+ public boolean needsFullscreenBouncer() {
+ KeyguardSecurityModel.SecurityMode mode = mKeyguardSecurityModel.getSecurityMode(
+ KeyguardUpdateMonitor.getCurrentUser());
+ return mode == KeyguardSecurityModel.SecurityMode.SimPin
+ || mode == KeyguardSecurityModel.SecurityMode.SimPuk;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index c0922163903f..ee948c04df38 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -51,7 +51,7 @@ class StatusBarLaunchAnimatorController(
centralSurfaces.notificationPanelViewController.applyLaunchAnimationProgress(linearProgress)
}
- override fun onLaunchAnimationCancelled() {
+ override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
delegate.onLaunchAnimationCancelled()
centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(false)
centralSurfaces.onLaunchAnimationCancelled(isLaunchForActivity)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
index 75dac1a093dd..d9c0293fe13d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarTouchableRegionManager.java
@@ -55,6 +55,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
private final Context mContext;
private final HeadsUpManagerPhone mHeadsUpManager;
private final NotificationShadeWindowController mNotificationShadeWindowController;
+ private final UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
private boolean mIsStatusBarExpanded = false;
private boolean mShouldAdjustInsets = false;
@@ -72,7 +73,8 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
Context context,
NotificationShadeWindowController notificationShadeWindowController,
ConfigurationController configurationController,
- HeadsUpManagerPhone headsUpManager
+ HeadsUpManagerPhone headsUpManager,
+ UnlockedScreenOffAnimationController unlockedScreenOffAnimationController
) {
mContext = context;
initResources();
@@ -115,6 +117,8 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
mNotificationShadeWindowController.setForcePluginOpenListener((forceOpen) -> {
updateTouchableRegion();
});
+
+ mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
}
protected void setup(
@@ -179,7 +183,7 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
/**
* Set the touchable portion of the status bar based on what elements are visible.
*/
- private void updateTouchableRegion() {
+ public void updateTouchableRegion() {
boolean hasCutoutInset = (mNotificationShadeWindowView != null)
&& (mNotificationShadeWindowView.getRootWindowInsets() != null)
&& (mNotificationShadeWindowView.getRootWindowInsets().getDisplayCutout() != null);
@@ -242,12 +246,25 @@ public final class StatusBarTouchableRegionManager implements Dumpable {
touchableRegion.union(bounds);
}
+ /**
+ * Helper to let us know when calculating the region is not needed because we know the entire
+ * screen needs to be touchable.
+ */
+ private boolean shouldMakeEntireScreenTouchable() {
+ // The touchable region is always the full area when expanded, whether we're showing the
+ // shade or the bouncer. It's also fully touchable when the screen off animation is playing
+ // since we don't want stray touches to go through the light reveal scrim to whatever is
+ // underneath.
+ return mIsStatusBarExpanded
+ || mCentralSurfaces.isBouncerShowing()
+ || mUnlockedScreenOffAnimationController.isAnimationPlaying();
+ }
+
private final OnComputeInternalInsetsListener mOnComputeInternalInsetsListener =
new OnComputeInternalInsetsListener() {
@Override
public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
- if (mIsStatusBarExpanded || mCentralSurfaces.isBouncerShowing()) {
- // The touchable region is always the full area when expanded
+ if (shouldMakeEntireScreenTouchable()) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
deleted file mode 100644
index fe846747d0c6..000000000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ConnectivityInfoProcessor.kt
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.pipeline
-
-import android.content.Context
-import com.android.systemui.CoreStartable
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
-import javax.inject.Inject
-import javax.inject.Provider
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
-import kotlinx.coroutines.launch
-
-/**
- * A temporary object that collects on [WifiViewModel] flows for debugging purposes.
- *
- * This will eventually get migrated to a view binder that will use the flow outputs to set state on
- * views. For now, this just collects on flows so that the information gets logged.
- */
-@SysUISingleton
-class ConnectivityInfoProcessor @Inject constructor(
- context: Context,
- // TODO(b/238425913): Don't use the application scope; instead, use the status bar view's
- // scope so we only do work when there's UI that cares about it.
- @Application private val scope: CoroutineScope,
- private val statusBarPipelineFlags: StatusBarPipelineFlags,
- private val wifiViewModelProvider: Provider<WifiViewModel>,
-) : CoreStartable(context) {
- override fun start() {
- if (!statusBarPipelineFlags.isNewPipelineBackendEnabled()) {
- return
- }
- // TODO(b/238425913): The view binder should do this instead. For now, do it here so we can
- // see the logs.
- scope.launch {
- wifiViewModelProvider.get().isActivityInVisible.collect { }
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index 681dc6fc13a2..9a7c3fae780c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -16,25 +16,15 @@
package com.android.systemui.statusbar.pipeline.dagger
-import com.android.systemui.CoreStartable
-import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
import dagger.Binds
import dagger.Module
-import dagger.multibindings.ClassKey
-import dagger.multibindings.IntoMap
@Module
abstract class StatusBarPipelineModule {
- /** Inject into ConnectivityInfoProcessor. */
- @Binds
- @IntoMap
- @ClassKey(ConnectivityInfoProcessor::class)
- abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
-
@Binds
abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt
new file mode 100644
index 000000000000..5b2d69564585
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.graphics.ColorFilter
+import android.graphics.ColorMatrix
+import android.graphics.ColorMatrixColorFilter
+import android.graphics.drawable.Drawable
+import android.os.UserHandle
+import android.widget.BaseAdapter
+import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
+import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper.getUserRecordName
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper.getUserSwitcherActionIconResourceId
+import java.lang.ref.WeakReference
+
+/** Provides views for user switcher experiences. */
+abstract class BaseUserSwitcherAdapter
+protected constructor(
+ protected val controller: UserSwitcherController,
+) : BaseAdapter() {
+
+ protected open val users: ArrayList<UserRecord>
+ get() = controller.users
+
+ init {
+ controller.addAdapter(WeakReference(this))
+ }
+
+ override fun getCount(): Int {
+ return if (controller.isKeyguardShowing) {
+ users.count { !it.isRestricted }
+ } else {
+ users.size
+ }
+ }
+
+ override fun getItem(position: Int): UserRecord {
+ return users[position]
+ }
+
+ override fun getItemId(position: Int): Long {
+ return position.toLong()
+ }
+
+ /**
+ * Notifies that a user item in the UI has been clicked.
+ *
+ * If the user switcher is hosted in a dialog, passing a non-null [dialogShower] will allow
+ * animation to and from the parent dialog.
+ */
+ @JvmOverloads
+ fun onUserListItemClicked(
+ record: UserRecord,
+ dialogShower: DialogShower? = null,
+ ) {
+ controller.onUserListItemClicked(record, dialogShower)
+ }
+
+ open fun getName(context: Context, item: UserRecord): String {
+ return getName(context, item, false)
+ }
+
+ /** Returns the name for the given {@link UserRecord}. */
+ open fun getName(context: Context, item: UserRecord, isTablet: Boolean): String {
+ return getUserRecordName(
+ context = context,
+ record = item,
+ isGuestUserAutoCreated = controller.isGuestUserAutoCreated,
+ isGuestUserResetting = controller.isGuestUserResetting,
+ isTablet = isTablet,
+ )
+ }
+
+ fun refresh() {
+ controller.refreshUsers(UserHandle.USER_NULL)
+ }
+
+ companion object {
+ @JvmStatic
+ protected val disabledUserAvatarColorFilter: ColorFilter by lazy {
+ val matrix = ColorMatrix()
+ matrix.setSaturation(0f) // 0 - grayscale
+ ColorMatrixColorFilter(matrix)
+ }
+
+ @JvmStatic
+ @JvmOverloads
+ protected fun getIconDrawable(
+ context: Context,
+ item: UserRecord,
+ isTablet: Boolean = false,
+ ): Drawable {
+ val iconRes =
+ getUserSwitcherActionIconResourceId(
+ item.isAddUser,
+ item.isGuest,
+ item.isAddSupervisedUser,
+ isTablet,
+ )
+ return checkNotNull(context.getDrawable(iconRes))
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 753e94015751..149ed0a71b91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -19,14 +19,17 @@ package com.android.systemui.statusbar.policy;
import android.annotation.Nullable;
import android.view.View;
-import com.android.systemui.Dumpable;
import com.android.systemui.demomode.DemoMode;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
-public interface BatteryController extends DemoMode, Dumpable,
+/**
+ * Controller for battery related information, including the charge level, power save mode,
+ * and time remaining information
+ */
+public interface BatteryController extends DemoMode,
CallbackController<BatteryStateChangeCallback> {
/**
* Prints the current state of the {@link BatteryController} to the given {@link PrintWriter}.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 33ddf7eed006..c7ad76722929 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -38,11 +38,13 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.fuelgauge.BatterySaverUtils;
import com.android.settingslib.fuelgauge.Estimate;
import com.android.settingslib.utils.PowerUtil;
+import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
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.power.EnhancedEstimates;
import com.android.systemui.util.Assert;
@@ -56,7 +58,8 @@ import java.util.concurrent.atomic.AtomicReference;
* Default implementation of a {@link BatteryController}. This controller monitors for battery
* level change events that are broadcasted by the system.
*/
-public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController {
+public class BatteryControllerImpl extends BroadcastReceiver implements BatteryController,
+ Dumpable {
private static final String TAG = "BatteryController";
private static final String ACTION_LEVEL_TEST = "com.android.systemui.BATTERY_LEVEL_TEST";
@@ -70,6 +73,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
private final ArrayList<EstimateFetchCompletion> mFetchCallbacks = new ArrayList<>();
private final PowerManager mPowerManager;
private final DemoModeController mDemoModeController;
+ private final DumpManager mDumpManager;
private final Handler mMainHandler;
private final Handler mBgHandler;
protected final Context mContext;
@@ -101,6 +105,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
PowerManager powerManager,
BroadcastDispatcher broadcastDispatcher,
DemoModeController demoModeController,
+ DumpManager dumpManager,
@Main Handler mainHandler,
@Background Handler bgHandler) {
mContext = context;
@@ -110,6 +115,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
mEstimates = enhancedEstimates;
mBroadcastDispatcher = broadcastDispatcher;
mDemoModeController = demoModeController;
+ mDumpManager = dumpManager;
}
private void registerReceiver() {
@@ -134,6 +140,7 @@ public class BatteryControllerImpl extends BroadcastReceiver implements BatteryC
}
}
mDemoModeController.addCallback(this);
+ mDumpManager.registerDumpable(TAG, this);
updatePowerSave();
updateEstimateInBackground();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index e7fa6d239012..aae0f93a0e19 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -27,7 +27,6 @@ import android.os.Looper;
import android.os.Message;
import android.os.UserHandle;
import android.os.UserManager;
-import android.util.Log;
import androidx.annotation.NonNull;
@@ -37,6 +36,7 @@ import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.systemui.bluetooth.BluetoothLogger;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
@@ -57,9 +57,9 @@ import javax.inject.Inject;
public class BluetoothControllerImpl implements BluetoothController, BluetoothCallback,
CachedBluetoothDevice.Callback, LocalBluetoothProfileManager.ServiceListener {
private static final String TAG = "BluetoothController";
- private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private final DumpManager mDumpManager;
+ private final BluetoothLogger mLogger;
private final LocalBluetoothManager mLocalBluetoothManager;
private final UserManager mUserManager;
private final int mCurrentUser;
@@ -70,6 +70,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
private final List<CachedBluetoothDevice> mConnectedDevices = new ArrayList<>();
private boolean mEnabled;
+ @ConnectionState
private int mConnectionState = BluetoothAdapter.STATE_DISCONNECTED;
private boolean mAudioProfileOnly;
private boolean mIsActive;
@@ -83,10 +84,12 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
public BluetoothControllerImpl(
Context context,
DumpManager dumpManager,
+ BluetoothLogger logger,
@Background Looper bgLooper,
@Main Looper mainLooper,
@Nullable LocalBluetoothManager localBluetoothManager) {
mDumpManager = dumpManager;
+ mLogger = logger;
mLocalBluetoothManager = localBluetoothManager;
mBgHandler = new Handler(bgLooper);
mHandler = new H(mainLooper);
@@ -116,7 +119,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
return;
}
pw.print(" mEnabled="); pw.println(mEnabled);
- pw.print(" mConnectionState="); pw.println(stateToString(mConnectionState));
+ pw.print(" mConnectionState="); pw.println(connectionStateToString(mConnectionState));
pw.print(" mAudioProfileOnly="); pw.println(mAudioProfileOnly);
pw.print(" mIsActive="); pw.println(mIsActive);
pw.print(" mConnectedDevices="); pw.println(getConnectedDevices());
@@ -127,7 +130,7 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
}
}
- private static String stateToString(int state) {
+ private static String connectionStateToString(@ConnectionState int state) {
switch (state) {
case BluetoothAdapter.STATE_CONNECTED:
return "CONNECTED";
@@ -320,8 +323,8 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
}
@Override
- public void onBluetoothStateChanged(int bluetoothState) {
- if (DEBUG) Log.d(TAG, "BluetoothStateChanged=" + stateToString(bluetoothState));
+ public void onBluetoothStateChanged(@AdapterState int bluetoothState) {
+ mLogger.logStateChange(BluetoothAdapter.nameForState(bluetoothState));
mEnabled = bluetoothState == BluetoothAdapter.STATE_ON
|| bluetoothState == BluetoothAdapter.STATE_TURNING_ON;
mState = bluetoothState;
@@ -330,24 +333,25 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
}
@Override
- public void onDeviceAdded(CachedBluetoothDevice cachedDevice) {
- if (DEBUG) Log.d(TAG, "DeviceAdded=" + cachedDevice.getAddress());
+ public void onDeviceAdded(@NonNull CachedBluetoothDevice cachedDevice) {
+ mLogger.logDeviceAdded(cachedDevice.getAddress());
cachedDevice.registerCallback(this);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
- public void onDeviceDeleted(CachedBluetoothDevice cachedDevice) {
- if (DEBUG) Log.d(TAG, "DeviceDeleted=" + cachedDevice.getAddress());
+ public void onDeviceDeleted(@NonNull CachedBluetoothDevice cachedDevice) {
+ mLogger.logDeviceDeleted(cachedDevice.getAddress());
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
- public void onDeviceBondStateChanged(CachedBluetoothDevice cachedDevice, int bondState) {
- if (DEBUG) Log.d(TAG, "DeviceBondStateChanged=" + cachedDevice.getAddress());
+ public void onDeviceBondStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int bondState) {
+ mLogger.logBondStateChange(cachedDevice.getAddress(), bondState);
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
@@ -355,50 +359,47 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
@Override
public void onDeviceAttributesChanged() {
- if (DEBUG) Log.d(TAG, "DeviceAttributesChanged");
+ mLogger.logDeviceAttributesChanged();
updateConnected();
mHandler.sendEmptyMessage(H.MSG_PAIRED_DEVICES_CHANGED);
}
@Override
- public void onConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
- if (DEBUG) {
- Log.d(TAG, "ConnectionStateChanged=" + cachedDevice.getAddress() + " "
- + stateToString(state));
- }
+ public void onConnectionStateChanged(
+ @Nullable CachedBluetoothDevice cachedDevice,
+ @ConnectionState int state) {
+ mLogger.logDeviceConnectionStateChanged(
+ getAddressOrNull(cachedDevice), connectionStateToString(state));
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
- public void onProfileConnectionStateChanged(CachedBluetoothDevice cachedDevice,
- int state, int bluetoothProfile) {
- if (DEBUG) {
- Log.d(TAG, "ProfileConnectionStateChanged=" + cachedDevice.getAddress() + " "
- + stateToString(state) + " profileId=" + bluetoothProfile);
- }
+ public void onProfileConnectionStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice,
+ @ConnectionState int state,
+ int bluetoothProfile) {
+ mLogger.logProfileConnectionStateChanged(
+ cachedDevice.getAddress(), connectionStateToString(state), bluetoothProfile);
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
- public void onActiveDeviceChanged(CachedBluetoothDevice activeDevice, int bluetoothProfile) {
- if (DEBUG) {
- Log.d(TAG, "ActiveDeviceChanged=" + activeDevice.getAddress()
- + " profileId=" + bluetoothProfile);
- }
+ public void onActiveDeviceChanged(
+ @Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
+ mLogger.logActiveDeviceChanged(getAddressOrNull(activeDevice), bluetoothProfile);
updateActive();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
@Override
- public void onAclConnectionStateChanged(CachedBluetoothDevice cachedDevice, int state) {
- if (DEBUG) {
- Log.d(TAG, "ACLConnectionStateChanged=" + cachedDevice.getAddress() + " "
- + stateToString(state));
- }
+ public void onAclConnectionStateChanged(
+ @NonNull CachedBluetoothDevice cachedDevice, int state) {
+ mLogger.logAclConnectionStateChanged(
+ cachedDevice.getAddress(), connectionStateToString(state));
mCachedState.remove(cachedDevice);
updateConnected();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
@@ -415,6 +416,11 @@ public class BluetoothControllerImpl implements BluetoothController, BluetoothCa
return state;
}
+ @Nullable
+ private String getAddressOrNull(@Nullable CachedBluetoothDevice device) {
+ return device == null ? null : device.getAddress();
+ }
+
@Override
public void onServiceConnected() {
updateConnected();
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 163060814545..dc73d1f007c6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchController.java
@@ -69,7 +69,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
private final Context mContext;
private Resources mResources;
private final UserSwitcherController mUserSwitcherController;
- private UserSwitcherController.BaseUserAdapter mAdapter;
+ private BaseUserSwitcherAdapter mAdapter;
private final KeyguardStateController mKeyguardStateController;
private final FalsingManager mFalsingManager;
protected final SysuiStatusBarStateController mStatusBarStateController;
@@ -171,7 +171,7 @@ public class KeyguardQsUserSwitchController extends ViewController<FrameLayout>
mUserAvatarView = mView.findViewById(R.id.kg_multi_user_avatar);
mUserAvatarViewWithBackground = mView.findViewById(
R.id.kg_multi_user_avatar_with_background);
- mAdapter = new UserSwitcherController.BaseUserAdapter(mUserSwitcherController) {
+ mAdapter = new BaseUserSwitcherAdapter(mUserSwitcherController) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return null;
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 e2f5734cb4f9..0995a00533a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcherController.java
@@ -16,9 +16,6 @@
package com.android.systemui.statusbar.policy;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA;
-import static com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
@@ -232,14 +229,8 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
}
/**
- * See:
- *
- * <ul>
- * <li>{@link com.android.internal.R.bool.config_expandLockScreenUserSwitcher}</li>
- * <li>{@link UserSwitcherController.SIMPLE_USER_SWITCHER_GLOBAL_SETTING}</li>
- * </ul>
+ * Returns {@code true} if the user switcher should be open by default on the lock screen.
*
- * @return true if the user switcher should be open by default on the lock screen.
* @see android.os.UserManager#isUserSwitcherEnabled()
*/
public boolean isSimpleUserSwitcher() {
@@ -436,7 +427,7 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
}
static class KeyguardUserAdapter extends
- UserSwitcherController.BaseUserAdapter implements View.OnClickListener {
+ BaseUserSwitcherAdapter implements View.OnClickListener {
private final Context mContext;
private final Resources mResources;
@@ -514,9 +505,9 @@ public class KeyguardUserSwitcherController extends ViewController<KeyguardUserS
v.bind(name, drawable, item.info.id);
}
v.setActivated(item.isCurrent);
- v.setDisabledByAdmin(mController.isDisabledByAdmin(item));
+ v.setDisabledByAdmin(getController().isDisabledByAdmin(item));
v.setEnabled(item.isSwitchToEnabled);
- v.setAlpha(v.isEnabled() ? USER_SWITCH_ENABLED_ALPHA : USER_SWITCH_DISABLED_ALPHA);
+ UserSwitcherController.setSelectableAlpha(v);
if (item.isCurrent) {
mCurrentUserView = v;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt
new file mode 100644
index 000000000000..843c2329092c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.kt
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package com.android.systemui.statusbar.policy
+
+import android.annotation.UserIdInt
+import android.content.Intent
+import android.view.View
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin
+import com.android.systemui.Dumpable
+import com.android.systemui.qs.user.UserSwitchDialogController.DialogShower
+import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
+import java.lang.ref.WeakReference
+import kotlinx.coroutines.flow.Flow
+
+/** Defines interface for a class that provides user switching functionality and state. */
+interface UserSwitcherController : Dumpable {
+
+ /** The current list of [UserRecord]. */
+ val users: ArrayList<UserRecord>
+
+ /** Whether the user switcher experience should use the simple experience. */
+ val isSimpleUserSwitcher: Boolean
+
+ /** Require a view for jank detection */
+ fun init(view: View)
+
+ /** The [UserRecord] of the current user or `null` when none. */
+ val currentUserRecord: UserRecord?
+
+ /** The name of the current user of the device or `null`, when none is selected. */
+ val currentUserName: String?
+
+ /**
+ * Notifies that a user has been selected.
+ *
+ * This will trigger the right user journeys to create a guest user, switch users, and/or
+ * navigate to the correct destination.
+ *
+ * If a user with the given ID is not found, this method is a no-op.
+ *
+ * @param userId The ID of the user to switch to.
+ * @param dialogShower An optional [DialogShower] in case we need to show dialogs.
+ */
+ fun onUserSelected(userId: Int, dialogShower: DialogShower?)
+
+ /** Whether it is allowed to add users while the device is locked. */
+ val isAddUsersFromLockScreenEnabled: Flow<Boolean>
+
+ /** Whether the guest user is configured to always be present on the device. */
+ val isGuestUserAutoCreated: Boolean
+
+ /** Whether the guest user is currently being reset. */
+ val isGuestUserResetting: Boolean
+
+ /** Creates and switches to the guest user. */
+ fun createAndSwitchToGuestUser(dialogShower: DialogShower?)
+
+ /** Shows the add user dialog. */
+ fun showAddUserDialog(dialogShower: DialogShower?)
+
+ /** Starts an activity to add a supervised user to the device. */
+ fun startSupervisedUserActivity()
+
+ /** Notifies when the display density or font scale has changed. */
+ fun onDensityOrFontScaleChanged()
+
+ /** Registers an adapter to notify when the users change. */
+ fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>)
+
+ /** Notifies the item for a user has been clicked. */
+ fun onUserListItemClicked(record: UserRecord, dialogShower: DialogShower?)
+
+ /**
+ * Removes guest user and switches to target user. The guest must be the current user and its id
+ * must be `guestUserId`.
+ *
+ * If `targetUserId` is `UserHandle.USER_NULL`, then create a new guest user in the foreground,
+ * and immediately switch to it. This is used for wiping the current guest and replacing it with
+ * a new one.
+ *
+ * If `targetUserId` is specified, then remove the guest in the background while switching to
+ * `targetUserId`.
+ *
+ * If device is configured with `config_guestUserAutoCreated`, then after guest user is removed,
+ * a new one is created in the background. This has no effect if `targetUserId` is
+ * `UserHandle.USER_NULL`.
+ *
+ * @param guestUserId id of the guest user to remove
+ * @param targetUserId id of the user to switch to after guest is removed. If
+ * `UserHandle.USER_NULL`, then switch immediately to the newly created guest user.
+ */
+ fun removeGuestUser(@UserIdInt guestUserId: Int, @UserIdInt targetUserId: Int)
+
+ /**
+ * Exits guest user and switches to previous non-guest user. The guest must be the current user.
+ *
+ * @param guestUserId user id of the guest user to exit
+ * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when
+ * target user id is not known
+ * @param forceRemoveGuestOnExit true: remove guest before switching user, false: remove guest
+ * only if its ephemeral, else keep guest
+ */
+ fun exitGuestUser(
+ @UserIdInt guestUserId: Int,
+ @UserIdInt targetUserId: Int,
+ forceRemoveGuestOnExit: Boolean
+ )
+
+ /**
+ * Guarantee guest is present only if the device is provisioned. Otherwise, create a content
+ * observer to wait until the device is provisioned, then schedule the guest creation.
+ */
+ fun schedulePostBootGuestCreation()
+
+ /** Whether keyguard is showing. */
+ val isKeyguardShowing: Boolean
+
+ /** Returns the [EnforcedAdmin] for the given record, or `null` if there isn't one. */
+ fun getEnforcedAdmin(record: UserRecord): EnforcedAdmin?
+
+ /** Returns `true` if the given record is disabled by the admin; `false` otherwise. */
+ fun isDisabledByAdmin(record: UserRecord): Boolean
+
+ /** Starts an activity with the given [Intent]. */
+ fun startActivity(intent: Intent)
+
+ /**
+ * Refreshes users from UserManager.
+ *
+ * The pictures are only loaded if they have not been loaded yet.
+ *
+ * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
+ */
+ fun refreshUsers(forcePictureLoadForId: Int)
+
+ /** Adds a subscriber to when user switches. */
+ fun addUserSwitchCallback(callback: UserSwitchCallback)
+
+ /** Removes a previously-added subscriber. */
+ fun removeUserSwitchCallback(callback: UserSwitchCallback)
+
+ /** Defines interface for classes that can be called back when the user is switched. */
+ fun interface UserSwitchCallback {
+ /** Notifies that the user has switched. */
+ fun onUserSwitched()
+ }
+
+ companion object {
+ /** Alpha value to apply to a user view in the user switcher when it's selectable. */
+ private const val ENABLED_ALPHA =
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA
+
+ /** Alpha value to apply to a user view in the user switcher when it's not selectable. */
+ private const val DISABLED_ALPHA =
+ LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA
+
+ @JvmStatic
+ fun setSelectableAlpha(view: View) {
+ view.alpha =
+ if (view.isEnabled) {
+ ENABLED_ALPHA
+ } else {
+ DISABLED_ALPHA
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt
new file mode 100644
index 000000000000..12834f68c3b7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerImpl.kt
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.Intent
+import android.view.View
+import com.android.settingslib.RestrictedLockUtils
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.user.data.source.UserRecord
+import dagger.Lazy
+import java.io.PrintWriter
+import java.lang.ref.WeakReference
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/** Implementation of [UserSwitcherController]. */
+class UserSwitcherControllerImpl
+@Inject
+constructor(
+ private val flags: FeatureFlags,
+ @Suppress("DEPRECATION") private val oldImpl: Lazy<UserSwitcherControllerOldImpl>,
+) : UserSwitcherController {
+
+ private val isNewImpl: Boolean
+ get() = flags.isEnabled(Flags.REFACTORED_USER_SWITCHER_CONTROLLER)
+ private val _oldImpl: UserSwitcherControllerOldImpl
+ get() = oldImpl.get()
+
+ private fun notYetImplemented(): Nothing {
+ error("Not yet implemented!")
+ }
+
+ override val users: ArrayList<UserRecord>
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.users
+ }
+
+ override val isSimpleUserSwitcher: Boolean
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isSimpleUserSwitcher
+ }
+
+ override fun init(view: View) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.init(view)
+ }
+ }
+
+ override val currentUserRecord: UserRecord?
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.currentUserRecord
+ }
+
+ override val currentUserName: String?
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.currentUserName
+ }
+
+ override fun onUserSelected(
+ userId: Int,
+ dialogShower: UserSwitchDialogController.DialogShower?
+ ) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.onUserSelected(userId, dialogShower)
+ }
+ }
+
+ override val isAddUsersFromLockScreenEnabled: Flow<Boolean>
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isAddUsersFromLockScreenEnabled
+ }
+
+ override val isGuestUserAutoCreated: Boolean
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isGuestUserAutoCreated
+ }
+
+ override val isGuestUserResetting: Boolean
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isGuestUserResetting
+ }
+
+ override fun createAndSwitchToGuestUser(
+ dialogShower: UserSwitchDialogController.DialogShower?,
+ ) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.createAndSwitchToGuestUser(dialogShower)
+ }
+ }
+
+ override fun showAddUserDialog(dialogShower: UserSwitchDialogController.DialogShower?) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.showAddUserDialog(dialogShower)
+ }
+ }
+
+ override fun startSupervisedUserActivity() {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.startSupervisedUserActivity()
+ }
+ }
+
+ override fun onDensityOrFontScaleChanged() {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.onDensityOrFontScaleChanged()
+ }
+ }
+
+ override fun addAdapter(adapter: WeakReference<BaseUserSwitcherAdapter>) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.addAdapter(adapter)
+ }
+ }
+
+ override fun onUserListItemClicked(
+ record: UserRecord,
+ dialogShower: UserSwitchDialogController.DialogShower?,
+ ) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.onUserListItemClicked(record, dialogShower)
+ }
+ }
+
+ override fun removeGuestUser(guestUserId: Int, targetUserId: Int) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.removeGuestUser(guestUserId, targetUserId)
+ }
+ }
+
+ override fun exitGuestUser(
+ guestUserId: Int,
+ targetUserId: Int,
+ forceRemoveGuestOnExit: Boolean
+ ) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.exitGuestUser(guestUserId, targetUserId, forceRemoveGuestOnExit)
+ }
+ }
+
+ override fun schedulePostBootGuestCreation() {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.schedulePostBootGuestCreation()
+ }
+ }
+
+ override val isKeyguardShowing: Boolean
+ get() =
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isKeyguardShowing
+ }
+
+ override fun getEnforcedAdmin(record: UserRecord): RestrictedLockUtils.EnforcedAdmin? {
+ return if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.getEnforcedAdmin(record)
+ }
+ }
+
+ override fun isDisabledByAdmin(record: UserRecord): Boolean {
+ return if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.isDisabledByAdmin(record)
+ }
+ }
+
+ override fun startActivity(intent: Intent) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.startActivity(intent)
+ }
+ }
+
+ override fun refreshUsers(forcePictureLoadForId: Int) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.refreshUsers(forcePictureLoadForId)
+ }
+ }
+
+ override fun addUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.addUserSwitchCallback(callback)
+ }
+ }
+
+ override fun removeUserSwitchCallback(callback: UserSwitcherController.UserSwitchCallback) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.removeUserSwitchCallback(callback)
+ }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ if (isNewImpl) {
+ notYetImplemented()
+ } else {
+ _oldImpl.dump(pw, args)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java
index 3b8ed338db80..d365aa6f952d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImpl.java
@@ -11,9 +11,8 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
-
package com.android.systemui.statusbar.policy;
import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
@@ -34,10 +33,6 @@ import android.content.IntentFilter;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.graphics.Bitmap;
-import android.graphics.ColorFilter;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
-import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -51,7 +46,6 @@ import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.WindowManagerGlobal;
-import android.widget.BaseAdapter;
import android.widget.Toast;
import androidx.annotation.Nullable;
@@ -63,7 +57,6 @@ import com.android.internal.logging.UiEventLogger;
import com.android.internal.util.LatencyTracker;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.users.UserCreatingDialog;
-import com.android.systemui.Dumpable;
import com.android.systemui.GuestResetOrExitSessionReceiver;
import com.android.systemui.GuestResumeSessionReceiver;
import com.android.systemui.R;
@@ -86,7 +79,6 @@ import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.user.CreateUserActivity;
import com.android.systemui.user.data.source.UserRecord;
-import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper;
import com.android.systemui.util.settings.GlobalSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -106,15 +98,14 @@ import kotlinx.coroutines.flow.MutableStateFlow;
import kotlinx.coroutines.flow.StateFlowKt;
/**
- * Keeps a list of all users on the device for user switching.
+ * Old implementation. Keeps a list of all users on the device for user switching.
+ *
+ * @deprecated This is the old implementation. Please depend on {@link UserSwitcherController}
+ * instead.
*/
+@Deprecated
@SysUISingleton
-public class UserSwitcherController implements Dumpable {
-
- public static final float USER_SWITCH_ENABLED_ALPHA =
- LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA;
- public static final float USER_SWITCH_DISABLED_ALPHA =
- LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_NOT_SELECTABLE_ALPHA;
+public class UserSwitcherControllerOldImpl implements UserSwitcherController {
private static final String TAG = "UserSwitcherController";
private static final boolean DEBUG = false;
@@ -123,7 +114,7 @@ public class UserSwitcherController implements Dumpable {
private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000;
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
- private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000l;
+ private static final long MULTI_USER_JOURNEY_TIMEOUT = 20000L;
private static final String INTERACTION_JANK_ADD_NEW_USER_TAG = "add_new_user";
private static final String INTERACTION_JANK_EXIT_GUEST_MODE_TAG = "exit_guest_mode";
@@ -132,7 +123,7 @@ public class UserSwitcherController implements Dumpable {
protected final UserTracker mUserTracker;
protected final UserManager mUserManager;
private final ContentObserver mSettingsObserver;
- private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
+ private final ArrayList<WeakReference<BaseUserSwitcherAdapter>> mAdapters = new ArrayList<>();
@VisibleForTesting
final GuestResumeSessionReceiver mGuestResumeSessionReceiver;
@VisibleForTesting
@@ -158,7 +149,6 @@ public class UserSwitcherController implements Dumpable {
@VisibleForTesting
Dialog mAddUserDialog;
private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
- private boolean mResumeUserOnGuestLogout = true;
private boolean mSimpleUserSwitcher;
// When false, there won't be any visual affordance to add a new user from the keyguard even if
// the user is unlocked
@@ -187,7 +177,8 @@ public class UserSwitcherController implements Dumpable {
Collections.synchronizedList(new ArrayList<>());
@Inject
- public UserSwitcherController(Context context,
+ public UserSwitcherControllerOldImpl(
+ Context context,
IActivityManager activityManager,
UserManager userManager,
UserTracker userTracker,
@@ -222,9 +213,9 @@ public class UserSwitcherController implements Dumpable {
mFalsingManager = falsingManager;
mInteractionJankMonitor = interactionJankMonitor;
mLatencyTracker = latencyTracker;
+ mGlobalSettings = globalSettings;
mGuestResumeSessionReceiver = guestResumeSessionReceiver;
mGuestResetOrExitSessionReceiver = guestResetOrExitSessionReceiver;
- mGlobalSettings = globalSettings;
mBgExecutor = bgExecutor;
mLongRunningExecutor = longRunningExecutor;
mUiExecutor = uiExecutor;
@@ -303,16 +294,10 @@ public class UserSwitcherController implements Dumpable {
refreshUsers(UserHandle.USER_NULL);
}
- /**
- * Refreshes users from UserManager.
- *
- * The pictures are only loaded if they have not been loaded yet.
- *
- * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
- */
+ @Override
@SuppressWarnings("unchecked")
- private void refreshUsers(int forcePictureLoadForId) {
- if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId+")");
+ public void refreshUsers(int forcePictureLoadForId) {
+ if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId + ")");
if (forcePictureLoadForId != UserHandle.USER_NULL) {
mForcePictureLoadForUserId.put(forcePictureLoadForId, true);
}
@@ -323,8 +308,8 @@ public class UserSwitcherController implements Dumpable {
boolean forceAllUsers = mForcePictureLoadForUserId.get(UserHandle.USER_ALL);
SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
- final int N = mUsers.size();
- for (int i = 0; i < N; i++) {
+ final int userCount = mUsers.size();
+ for (int i = 0; i < userCount; i++) {
UserRecord r = mUsers.get(i);
if (r == null || r.picture == null || r.info == null || forceAllUsers
|| mForcePictureLoadForUserId.get(r.info.id)) {
@@ -431,38 +416,41 @@ public class UserSwitcherController implements Dumpable {
});
}
- boolean systemCanCreateUsers() {
+ private boolean systemCanCreateUsers() {
return !mUserManager.hasBaseUserRestriction(
UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
}
- boolean currentUserCanCreateUsers() {
+ private boolean currentUserCanCreateUsers() {
UserInfo currentUser = mUserTracker.getUserInfo();
return currentUser != null
&& (currentUser.isAdmin() || mUserTracker.getUserId() == UserHandle.USER_SYSTEM)
&& systemCanCreateUsers();
}
- boolean anyoneCanCreateUsers() {
+ private boolean anyoneCanCreateUsers() {
return systemCanCreateUsers() && mAddUsersFromLockScreen.getValue();
}
+ @VisibleForTesting
boolean canCreateGuest(boolean hasExistingGuest) {
return mUserSwitcherEnabled
&& (currentUserCanCreateUsers() || anyoneCanCreateUsers())
&& !hasExistingGuest;
}
+ @VisibleForTesting
boolean canCreateUser() {
return mUserSwitcherEnabled
&& (currentUserCanCreateUsers() || anyoneCanCreateUsers())
&& mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY);
}
- boolean createIsRestricted() {
+ private boolean createIsRestricted() {
return !mAddUsersFromLockScreen.getValue();
}
+ @VisibleForTesting
boolean canCreateSupervisedUser() {
return !TextUtils.isEmpty(mCreateSupervisedUserPackage) && canCreateUser();
}
@@ -476,7 +464,7 @@ public class UserSwitcherController implements Dumpable {
private void notifyAdapters() {
for (int i = mAdapters.size() - 1; i >= 0; i--) {
- BaseUserAdapter adapter = mAdapters.get(i).get();
+ BaseUserSwitcherAdapter adapter = mAdapters.get(i).get();
if (adapter != null) {
adapter.notifyDataSetChanged();
} else {
@@ -485,37 +473,20 @@ public class UserSwitcherController implements Dumpable {
}
}
+ @Override
public boolean isSimpleUserSwitcher() {
return mSimpleUserSwitcher;
}
- public void setResumeUserOnGuestLogout(boolean resume) {
- mResumeUserOnGuestLogout = resume;
- }
-
/**
* Returns whether the current user is a system user.
*/
- public boolean isSystemUser() {
+ @VisibleForTesting
+ boolean isSystemUser() {
return mUserTracker.getUserId() == UserHandle.USER_SYSTEM;
}
- public void removeUserId(int userId) {
- if (userId == UserHandle.USER_SYSTEM) {
- Log.w(TAG, "User " + userId + " could not removed.");
- return;
- }
- if (mUserTracker.getUserId() == userId) {
- switchToUserId(UserHandle.USER_SYSTEM);
- }
- if (mUserManager.removeUser(userId)) {
- refreshUsers(UserHandle.USER_NULL);
- }
- }
-
- /**
- * @return UserRecord for the current user
- */
+ @Override
public @Nullable UserRecord getCurrentUserRecord() {
for (int i = 0; i < mUsers.size(); ++i) {
UserRecord userRecord = mUsers.get(i);
@@ -526,17 +497,7 @@ public class UserSwitcherController implements Dumpable {
return null;
}
- /**
- * Notifies that a user has been selected.
- *
- * <p>This will trigger the right user journeys to create a guest user, switch users, and/or
- * navigate to the correct destination.
- *
- * <p>If a user with the given ID is not found, this method is a no-op.
- *
- * @param userId The ID of the user to switch to.
- * @param dialogShower An optional {@link DialogShower} in case we need to show dialogs.
- */
+ @Override
public void onUserSelected(int userId, @Nullable DialogShower dialogShower) {
UserRecord userRecord = mUsers.stream()
.filter(x -> x.resolveId() == userId)
@@ -549,23 +510,23 @@ public class UserSwitcherController implements Dumpable {
onUserListItemClicked(userRecord, dialogShower);
}
- /** Whether it is allowed to add users while the device is locked. */
- public Flow<Boolean> getAddUsersFromLockScreen() {
+ @Override
+ public Flow<Boolean> isAddUsersFromLockScreenEnabled() {
return mAddUsersFromLockScreen;
}
- /** Returns {@code true} if the guest user is configured to always be present on the device. */
+ @Override
public boolean isGuestUserAutoCreated() {
return mGuestUserAutoCreated;
}
- /** Returns {@code true} if the guest user is currently being reset. */
+ @Override
public boolean isGuestUserResetting() {
return mGuestIsResetting.get();
}
- @VisibleForTesting
- void onUserListItemClicked(UserRecord record, DialogShower dialogShower) {
+ @Override
+ public void onUserListItemClicked(UserRecord record, DialogShower dialogShower) {
if (record.isGuest && record.info == null) {
createAndSwitchToGuestUser(dialogShower);
} else if (record.isAddUser) {
@@ -604,7 +565,7 @@ public class UserSwitcherController implements Dumpable {
switchToUserId(id);
}
- protected void switchToUserId(int id) {
+ private void switchToUserId(int id) {
try {
if (mView != null) {
mInteractionJankMonitor.begin(InteractionJankMonitor.Configuration.Builder
@@ -621,7 +582,7 @@ public class UserSwitcherController implements Dumpable {
private void showExitGuestDialog(int id, boolean isGuestEphemeral, DialogShower dialogShower) {
int newId = UserHandle.USER_SYSTEM;
- if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
+ if (mLastNonGuestUser != UserHandle.USER_SYSTEM) {
UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
newId = info.id;
@@ -645,9 +606,7 @@ public class UserSwitcherController implements Dumpable {
}
}
- /**
- * Creates and switches to the guest user.
- */
+ @Override
public void createAndSwitchToGuestUser(@Nullable DialogShower dialogShower) {
createGuestAsync(guestId -> {
// guestId may be USER_NULL if we haven't reloaded the user list yet.
@@ -658,9 +617,7 @@ public class UserSwitcherController implements Dumpable {
});
}
- /**
- * Shows the add user dialog.
- */
+ @Override
public void showAddUserDialog(@Nullable DialogShower dialogShower) {
if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
mAddUserDialog.cancel();
@@ -677,9 +634,7 @@ public class UserSwitcherController implements Dumpable {
}
}
- /**
- * Starts an activity to add a supervised user to the device.
- */
+ @Override
public void startSupervisedUserActivity() {
final Intent intent = new Intent()
.setAction(UserManager.ACTION_CREATE_SUPERVISED_USER)
@@ -711,7 +666,7 @@ public class UserSwitcherController implements Dumpable {
public void onReceive(Context context, Intent intent) {
if (DEBUG) {
Log.v(TAG, "Broadcast: a=" + intent.getAction()
- + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
+ + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
}
boolean unpauseRefreshUsers = false;
@@ -725,8 +680,8 @@ public class UserSwitcherController implements Dumpable {
final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
final UserInfo userInfo = mUserManager.getUserInfo(currentId);
- final int N = mUsers.size();
- for (int i = 0; i < N; i++) {
+ final int userCount = mUsers.size();
+ for (int i = 0; i < userCount; i++) {
UserRecord record = mUsers.get(i);
if (record.info == null) continue;
boolean shouldBeCurrent = record.info.id == currentId;
@@ -805,7 +760,7 @@ public class UserSwitcherController implements Dumpable {
pw.println("mGuestUserAutoCreated=" + mGuestUserAutoCreated);
}
- /** Returns the name of the current user of the phone. */
+ @Override
public String getCurrentUserName() {
if (mUsers.isEmpty()) return null;
UserRecord item = mUsers.stream().filter(x -> x.isCurrent).findFirst().orElse(null);
@@ -814,40 +769,22 @@ public class UserSwitcherController implements Dumpable {
return item.info.name;
}
+ @Override
public void onDensityOrFontScaleChanged() {
refreshUsers(UserHandle.USER_ALL);
}
- @VisibleForTesting
- public void addAdapter(WeakReference<BaseUserAdapter> adapter) {
+ @Override
+ public void addAdapter(WeakReference<BaseUserSwitcherAdapter> adapter) {
mAdapters.add(adapter);
}
- @VisibleForTesting
+ @Override
public ArrayList<UserRecord> getUsers() {
return mUsers;
}
- /**
- * Removes guest user and switches to target user. The guest must be the current user and its id
- * must be {@code guestUserId}.
- *
- * <p>If {@code targetUserId} is {@link UserHandle#USER_NULL}, then create a new guest user in
- * the foreground, and immediately switch to it. This is used for wiping the current guest and
- * replacing it with a new one.
- *
- * <p>If {@code targetUserId} is specified, then remove the guest in the background while
- * switching to {@code targetUserId}.
- *
- * <p>If device is configured with {@link
- * com.android.internal.R.bool.config_guestUserAutoCreated}, then after guest user is removed, a
- * new one is created in the background. This has no effect if {@code targetUserId} is {@link
- * UserHandle#USER_NULL}.
- *
- * @param guestUserId id of the guest user to remove
- * @param targetUserId id of the user to switch to after guest is removed. If {@link
- * UserHandle#USER_NULL}, then switch immediately to the newly created guest user.
- */
+ @Override
public void removeGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId) {
UserInfo currentUser = mUserTracker.getUserInfo();
if (currentUser.id != guestUserId) {
@@ -894,18 +831,9 @@ public class UserSwitcherController implements Dumpable {
}
}
- /**
- * Exits guest user and switches to previous non-guest user. The guest must be the current
- * user.
- *
- * @param guestUserId user id of the guest user to exit
- * @param targetUserId user id of the guest user to exit, set to UserHandle#USER_NULL when
- * target user id is not known
- * @param forceRemoveGuestOnExit true: remove guest before switching user,
- * false: remove guest only if its ephemeral, else keep guest
- */
+ @Override
public void exitGuestUser(@UserIdInt int guestUserId, @UserIdInt int targetUserId,
- boolean forceRemoveGuestOnExit) {
+ boolean forceRemoveGuestOnExit) {
UserInfo currentUser = mUserTracker.getUserInfo();
if (currentUser.id != guestUserId) {
Log.w(TAG, "User requesting to start a new session (" + guestUserId + ")"
@@ -921,7 +849,7 @@ public class UserSwitcherController implements Dumpable {
int newUserId = UserHandle.USER_SYSTEM;
if (targetUserId == UserHandle.USER_NULL) {
// when target user is not specified switch to last non guest user
- if (mResumeUserOnGuestLogout && mLastNonGuestUser != UserHandle.USER_SYSTEM) {
+ if (mLastNonGuestUser != UserHandle.USER_SYSTEM) {
UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
newUserId = info.id;
@@ -959,10 +887,7 @@ public class UserSwitcherController implements Dumpable {
}
- /**
- * Guarantee guest is present only if the device is provisioned. Otherwise, create a content
- * observer to wait until the device is provisioned, then schedule the guest creation.
- */
+ @Override
public void schedulePostBootGuestCreation() {
if (isDeviceAllowedToAddGuest()) {
guaranteeGuestPresent();
@@ -1014,7 +939,7 @@ public class UserSwitcherController implements Dumpable {
* @return The multi-user user ID of the newly created guest user, or
* {@link UserHandle#USER_NULL} if the guest couldn't be created.
*/
- public @UserIdInt int createGuest() {
+ private @UserIdInt int createGuest() {
UserInfo guest;
try {
guest = mUserManager.createGuest(mContext);
@@ -1029,123 +954,27 @@ public class UserSwitcherController implements Dumpable {
return guest.id;
}
- /**
- * Require a view for jank detection
- */
+ @Override
public void init(View view) {
mView = view;
}
- @VisibleForTesting
- public KeyguardStateController getKeyguardStateController() {
- return mKeyguardStateController;
+ @Override
+ public boolean isKeyguardShowing() {
+ return mKeyguardStateController.isShowing();
}
- /**
- * Returns the {@link EnforcedAdmin} for the given record, or {@code null} if there isn't one.
- */
+ @Override
@Nullable
public EnforcedAdmin getEnforcedAdmin(UserRecord record) {
return mEnforcedAdminByUserRecord.get(record);
}
- /**
- * Returns {@code true} if the given record is disabled by the admin; {@code false} otherwise.
- */
+ @Override
public boolean isDisabledByAdmin(UserRecord record) {
return mDisabledByAdmin.contains(record);
}
- public static abstract class BaseUserAdapter extends BaseAdapter {
-
- final UserSwitcherController mController;
- private final KeyguardStateController mKeyguardStateController;
-
- protected BaseUserAdapter(UserSwitcherController controller) {
- mController = controller;
- mKeyguardStateController = controller.getKeyguardStateController();
- controller.addAdapter(new WeakReference<>(this));
- }
-
- protected ArrayList<UserRecord> getUsers() {
- return mController.getUsers();
- }
-
- public int getUserCount() {
- return countUsers(false);
- }
-
- @Override
- public int getCount() {
- return countUsers(true);
- }
-
- private int countUsers(boolean includeGuest) {
- boolean keyguardShowing = mKeyguardStateController.isShowing();
- final int userSize = getUsers().size();
- int count = 0;
- for (int i = 0; i < userSize; i++) {
- if (getUsers().get(i).isGuest && !includeGuest) {
- continue;
- }
- if (getUsers().get(i).isRestricted && keyguardShowing) {
- break;
- }
- count++;
- }
- return count;
- }
-
- @Override
- public UserRecord getItem(int position) {
- return getUsers().get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- /**
- * It handles click events on user list items.
- *
- * If the user switcher is hosted in a dialog, passing a non-null {@link DialogShower}
- * will allow animation to and from the parent dialog.
- *
- */
- public void onUserListItemClicked(UserRecord record, @Nullable DialogShower dialogShower) {
- mController.onUserListItemClicked(record, dialogShower);
- }
-
- public void onUserListItemClicked(UserRecord record) {
- onUserListItemClicked(record, null);
- }
-
- public String getName(Context context, UserRecord item) {
- return LegacyUserUiHelper.getUserRecordName(
- context,
- item,
- mController.isGuestUserAutoCreated(),
- mController.isGuestUserResetting());
- }
-
- protected static ColorFilter getDisabledUserAvatarColorFilter() {
- ColorMatrix matrix = new ColorMatrix();
- matrix.setSaturation(0f); // 0 - grayscale
- return new ColorMatrixColorFilter(matrix);
- }
-
- protected static Drawable getIconDrawable(Context context, UserRecord item) {
- int iconRes = LegacyUserUiHelper.getUserSwitcherActionIconResourceId(
- item.isAddUser, item.isGuest, item.isAddSupervisedUser);
- return context.getDrawable(iconRes);
- }
-
- public void refresh() {
- mController.refreshUsers(UserHandle.USER_NULL);
- }
- }
-
private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) {
EnforcedAdmin admin = RestrictedLockUtilsInternal.checkIfRestrictionEnforced(mContext,
UserManager.DISALLOW_ADD_USER, mUserTracker.getUserId());
@@ -1166,20 +995,17 @@ public class UserSwitcherController implements Dumpable {
defaultSimpleUserSwitcher, UserHandle.USER_SYSTEM) != 0;
}
+ @Override
public void startActivity(Intent intent) {
mActivityStarter.startActivity(intent, true);
}
- /**
- * Add a subscriber to when user switches.
- */
+ @Override
public void addUserSwitchCallback(UserSwitchCallback callback) {
mUserSwitchCallbacks.add(callback);
}
- /**
- * Remove a subscriber to when user switches.
- */
+ @Override
public void removeUserSwitchCallback(UserSwitchCallback callback) {
mUserSwitchCallbacks.remove(callback);
}
@@ -1206,7 +1032,7 @@ public class UserSwitcherController implements Dumpable {
// which
// helps making the transition faster.
if (!mKeyguardStateController.isShowing()) {
- mHandler.post(UserSwitcherController.this::notifyAdapters);
+ mHandler.post(UserSwitcherControllerOldImpl.this::notifyAdapters);
} else {
notifyAdapters();
}
@@ -1355,13 +1181,4 @@ public class UserSwitcherController implements Dumpable {
}
}
- /**
- * Callback to for when this controller receives the intent to switch users.
- */
- public interface UserSwitchCallback {
- /**
- * Called when user has switched.
- */
- void onUserSwitched();
- }
}
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 1b7353923ada..b1b45b51d8e4 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
@@ -58,6 +58,8 @@ import com.android.systemui.statusbar.policy.SecurityController;
import com.android.systemui.statusbar.policy.SecurityControllerImpl;
import com.android.systemui.statusbar.policy.UserInfoController;
import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.UserSwitcherControllerImpl;
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.statusbar.policy.WalletControllerImpl;
import com.android.systemui.statusbar.policy.ZenModeController;
@@ -196,4 +198,8 @@ public interface StatusBarPolicyModule {
static DataSaverController provideDataSaverController(NetworkController networkController) {
return networkController.getDataSaverController();
}
+
+ /** Binds {@link UserSwitcherController} to its implementation. */
+ @Binds
+ UserSwitcherController bindUserSwitcherController(UserSwitcherControllerImpl impl);
}
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
index 734eeecec215..a52e2aff52c1 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayController.kt
@@ -19,12 +19,10 @@ package com.android.systemui.temporarydisplay
import android.annotation.LayoutRes
import android.annotation.SuppressLint
import android.content.Context
-import android.content.pm.PackageManager
import android.graphics.PixelFormat
import android.graphics.drawable.Drawable
import android.os.PowerManager
import android.os.SystemClock
-import android.util.Log
import android.view.LayoutInflater
import android.view.ViewGroup
import android.view.WindowManager
@@ -33,11 +31,7 @@ import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
import androidx.annotation.CallSuper
-import com.android.internal.widget.CachingIconView
-import com.android.settingslib.Utils
-import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -50,17 +44,22 @@ import com.android.systemui.util.concurrency.DelayableExecutor
* The generic type T is expected to contain all the information necessary for the subclasses to
* display the view in a certain state, since they receive <T> in [updateView].
*
- * TODO(b/245610654): Remove all the media-specific logic from this class.
+ * @property windowTitle the title to use for the window that displays the temporary view. Should be
+ * normally cased, like "Window Title".
+ * @property wakeReason a string used for logging if we needed to wake the screen in order to
+ * display the temporary view. Should be screaming snake cased, like WAKE_REASON.
*/
-abstract class TemporaryViewDisplayController<T : TemporaryViewInfo>(
+abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : TemporaryViewLogger>(
internal val context: Context,
- internal val logger: MediaTttLogger,
+ internal val logger: U,
internal val windowManager: WindowManager,
@Main private val mainExecutor: DelayableExecutor,
private val accessibilityManager: AccessibilityManager,
private val configurationController: ConfigurationController,
private val powerManager: PowerManager,
@LayoutRes private val viewLayoutRes: Int,
+ private val windowTitle: String,
+ private val wakeReason: String,
) {
/**
* Window layout params that will be used as a starting point for the [windowLayoutParams] of
@@ -72,7 +71,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo>(
height = WindowManager.LayoutParams.WRAP_CONTENT
type = WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY
flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- title = WINDOW_TITLE
+ title = windowTitle
format = PixelFormat.TRANSLUCENT
setTrustedOverlay()
}
@@ -115,10 +114,10 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo>(
powerManager.wakeUp(
SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_APPLICATION,
- "com.android.systemui:media_tap_to_transfer_activated"
+ "com.android.systemui:$wakeReason",
)
}
-
+ logger.logChipAddition()
inflateAndUpdateView(newInfo)
}
@@ -192,80 +191,8 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo>(
* appears.
*/
open fun animateViewIn(view: ViewGroup) {}
-
- /**
- * Returns the size that the icon should be, or null if no size override is needed.
- */
- open fun getIconSize(isAppIcon: Boolean): Int? = null
-
- /**
- * An internal method to set the icon on the view.
- *
- * This is in the common superclass since both the sender and the receiver show an icon.
- *
- * @param appPackageName the package name of the app playing the media. Will be used to fetch
- * the app icon and app name if overrides aren't provided.
- *
- * @return the content description of the icon.
- */
- internal fun setIcon(
- currentView: ViewGroup,
- appPackageName: String?,
- appIconDrawableOverride: Drawable? = null,
- appNameOverride: CharSequence? = null,
- ): CharSequence {
- val appIconView = currentView.requireViewById<CachingIconView>(R.id.app_icon)
- val iconInfo = getIconInfo(appPackageName)
-
- getIconSize(iconInfo.isAppIcon)?.let { size ->
- val lp = appIconView.layoutParams
- lp.width = size
- lp.height = size
- appIconView.layoutParams = lp
- }
-
- appIconView.contentDescription = appNameOverride ?: iconInfo.iconName
- appIconView.setImageDrawable(appIconDrawableOverride ?: iconInfo.icon)
- return appIconView.contentDescription
- }
-
- /**
- * Returns the information needed to display the icon.
- *
- * The information will either contain app name and icon of the app playing media, or a default
- * name and icon if we can't find the app name/icon.
- */
- private fun getIconInfo(appPackageName: String?): IconInfo {
- if (appPackageName != null) {
- try {
- return IconInfo(
- iconName = context.packageManager.getApplicationInfo(
- appPackageName, PackageManager.ApplicationInfoFlags.of(0)
- ).loadLabel(context.packageManager).toString(),
- icon = context.packageManager.getApplicationIcon(appPackageName),
- isAppIcon = true
- )
- } catch (e: PackageManager.NameNotFoundException) {
- Log.w(TAG, "Cannot find package $appPackageName", e)
- }
- }
- return IconInfo(
- iconName = context.getString(R.string.media_output_dialog_unknown_launch_app_name),
- icon = context.resources.getDrawable(R.drawable.ic_cast).apply {
- this.setTint(
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
- )
- },
- isAppIcon = false
- )
- }
}
-// Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
-// UpdateMediaTapToTransferReceiverDisplayTest
-private const val WINDOW_TITLE = "Media Transfer Chip View"
-private val TAG = TemporaryViewDisplayController::class.simpleName!!
-
object TemporaryDisplayRemovalReason {
const val REASON_TIMEOUT = "TIMEOUT"
const val REASON_SCREEN_TAP = "SCREEN_TAP"
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
new file mode 100644
index 000000000000..606a11a84686
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.temporarydisplay
+
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogLevel
+
+/** A logger for temporary view changes -- see [TemporaryViewDisplayController]. */
+open class TemporaryViewLogger(
+ internal val buffer: LogBuffer,
+ internal val tag: String,
+) {
+ /** Logs that we added the chip to a new window. */
+ fun logChipAddition() {
+ buffer.log(tag, LogLevel.DEBUG, {}, { "Chip added" })
+ }
+
+ /** Logs that we removed the chip for the given [reason]. */
+ fun logChipRemoval(reason: String) {
+ buffer.log(tag, LogLevel.DEBUG, { str1 = reason }, { "Chip removed due to $str1" })
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
index 27746c024dad..00ed3d635fa1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java
@@ -36,6 +36,7 @@ import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dock.DockManagerImpl;
import com.android.systemui.doze.DozeHost;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.navigationbar.gestural.GestureModule;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -116,9 +117,12 @@ public abstract class TvSystemUIModule {
static BatteryController provideBatteryController(Context context,
EnhancedEstimates enhancedEstimates, PowerManager powerManager,
BroadcastDispatcher broadcastDispatcher, DemoModeController demoModeController,
+ DumpManager dumpManager,
@Main Handler mainHandler, @Background Handler bgHandler) {
BatteryController bC = new BatteryControllerImpl(context, enhancedEstimates, powerManager,
- broadcastDispatcher, demoModeController, mainHandler, bgHandler);
+ broadcastDispatcher, demoModeController,
+ dumpManager,
+ mainHandler, bgHandler);
bC.init();
return bC;
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
index d43f739f9e71..108ab43977e9 100644
--- a/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/UserSwitcherActivity.kt
@@ -53,10 +53,8 @@ import com.android.systemui.flags.Flags
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.plugins.FalsingManager.LOW_PENALTY
import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter
import com.android.systemui.statusbar.policy.UserSwitcherController
-import com.android.systemui.statusbar.policy.UserSwitcherController.BaseUserAdapter
-import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_DISABLED_ALPHA
-import com.android.systemui.statusbar.policy.UserSwitcherController.USER_SWITCH_ENABLED_ALPHA
import com.android.systemui.user.data.source.UserRecord
import com.android.systemui.user.ui.binder.UserSwitcherViewBinder
import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel
@@ -66,10 +64,10 @@ import kotlin.math.ceil
private const val USER_VIEW = "user_view"
-/**
- * Support a fullscreen user switcher
- */
-open class UserSwitcherActivity @Inject constructor(
+/** Support a fullscreen user switcher */
+open class UserSwitcherActivity
+@Inject
+constructor(
private val userSwitcherController: UserSwitcherController,
private val broadcastDispatcher: BroadcastDispatcher,
private val falsingCollector: FalsingCollector,
@@ -86,11 +84,12 @@ open class UserSwitcherActivity @Inject constructor(
private lateinit var addButton: View
private var addUserRecords = mutableListOf<UserRecord>()
private val onBackCallback = OnBackInvokedCallback { finish() }
- private val userSwitchedCallback: UserTracker.Callback = object : UserTracker.Callback {
- override fun onUserChanged(newUser: Int, userContext: Context) {
- finish()
+ private val userSwitchedCallback: UserTracker.Callback =
+ object : UserTracker.Callback {
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ finish()
+ }
}
- }
// When the add users options become available, insert another option to manage users
private val manageUserRecord =
UserRecord(
@@ -114,13 +113,14 @@ open class UserSwitcherActivity @Inject constructor(
@VisibleForTesting
fun createActivity() {
setContentView(R.layout.user_switcher_fullscreen)
- window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
- or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
+ window.decorView.systemUiVisibility =
+ (View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION)
if (isUsingModernArchitecture()) {
Log.d(TAG, "Using modern architecture.")
- val viewModel = ViewModelProvider(
- this, viewModelFactory.get())[UserSwitcherViewModel::class.java]
+ val viewModel =
+ ViewModelProvider(this, viewModelFactory.get())[UserSwitcherViewModel::class.java]
UserSwitcherViewBinder.bind(
view = requireViewById(R.id.user_switcher_root),
viewModel = viewModel,
@@ -136,27 +136,23 @@ open class UserSwitcherActivity @Inject constructor(
parent = requireViewById<UserSwitcherRootView>(R.id.user_switcher_root)
- parent.touchHandler = object : Gefingerpoken {
- override fun onTouchEvent(ev: MotionEvent?): Boolean {
- falsingCollector.onTouchEvent(ev)
- return false
+ parent.touchHandler =
+ object : Gefingerpoken {
+ override fun onTouchEvent(ev: MotionEvent?): Boolean {
+ falsingCollector.onTouchEvent(ev)
+ return false
+ }
}
- }
- requireViewById<View>(R.id.cancel).apply {
- setOnClickListener {
- _ -> finish()
- }
- }
+ requireViewById<View>(R.id.cancel).apply { setOnClickListener { _ -> finish() } }
- addButton = requireViewById<View>(R.id.add).apply {
- setOnClickListener {
- _ -> showPopupMenu()
- }
- }
+ addButton =
+ requireViewById<View>(R.id.add).apply { setOnClickListener { _ -> showPopupMenu() } }
onBackInvokedDispatcher.registerOnBackInvokedCallback(
- OnBackInvokedDispatcher.PRIORITY_DEFAULT, onBackCallback)
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT,
+ onBackCallback
+ )
userSwitcherController.init(parent)
initBroadcastReceiver()
@@ -169,25 +165,30 @@ open class UserSwitcherActivity @Inject constructor(
val items = mutableListOf<UserRecord>()
addUserRecords.forEach { items.add(it) }
- var popupMenuAdapter = ItemAdapter(
- this,
- R.layout.user_switcher_fullscreen_popup_item,
- layoutInflater,
- { item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item) },
- { item: UserRecord -> adapter.findUserIcon(item).mutate().apply {
- setTint(resources.getColor(
- R.color.user_switcher_fullscreen_popup_item_tint,
- getTheme()
- ))
- } }
- )
+ var popupMenuAdapter =
+ ItemAdapter(
+ this,
+ R.layout.user_switcher_fullscreen_popup_item,
+ layoutInflater,
+ { item: UserRecord -> adapter.getName(this@UserSwitcherActivity, item, true) },
+ { item: UserRecord ->
+ adapter.findUserIcon(item, true).mutate().apply {
+ setTint(
+ resources.getColor(
+ R.color.user_switcher_fullscreen_popup_item_tint,
+ getTheme()
+ )
+ )
+ }
+ }
+ )
popupMenuAdapter.addAll(items)
- popupMenu = UserSwitcherPopupMenu(this).apply {
- setAnchorView(addButton)
- setAdapter(popupMenuAdapter)
- setOnItemClickListener {
- parent: AdapterView<*>, view: View, pos: Int, id: Long ->
+ popupMenu =
+ UserSwitcherPopupMenu(this).apply {
+ setAnchorView(addButton)
+ setAdapter(popupMenuAdapter)
+ setOnItemClickListener { parent: AdapterView<*>, view: View, pos: Int, id: Long ->
if (falsingManager.isFalseTap(LOW_PENALTY) || !view.isEnabled()) {
return@setOnItemClickListener
}
@@ -206,10 +207,10 @@ open class UserSwitcherActivity @Inject constructor(
if (!item.isAddUser) {
this@UserSwitcherActivity.finish()
}
- }
+ }
- show()
- }
+ show()
+ }
}
private fun buildUserViews() {
@@ -227,8 +228,8 @@ open class UserSwitcherActivity @Inject constructor(
val totalWidth = parent.width
val userViewCount = adapter.getTotalUserViews()
val maxColumns = getMaxColumns(userViewCount)
- val horizontalGap = resources
- .getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap)
+ val horizontalGap =
+ resources.getDimensionPixelSize(R.dimen.user_switcher_fullscreen_horizontal_gap)
val totalWidthOfHorizontalGap = (maxColumns - 1) * horizontalGap
val maxWidgetDiameter = (totalWidth - totalWidthOfHorizontalGap) / maxColumns
@@ -299,14 +300,15 @@ open class UserSwitcherActivity @Inject constructor(
}
private fun initBroadcastReceiver() {
- broadcastReceiver = object : BroadcastReceiver() {
- override fun onReceive(context: Context, intent: Intent) {
- val action = intent.getAction()
- if (Intent.ACTION_SCREEN_OFF.equals(action)) {
- finish()
+ broadcastReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val action = intent.getAction()
+ if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+ finish()
+ }
}
}
- }
val filter = IntentFilter()
filter.addAction(Intent.ACTION_SCREEN_OFF)
@@ -322,6 +324,7 @@ open class UserSwitcherActivity @Inject constructor(
return flags.isEnabled(Flags.MODERN_USER_SWITCHER_ACTIVITY)
}
+ /** Provides views to populate the option menu. */
private class ItemAdapter(
val parentContext: Context,
val resource: Int,
@@ -334,61 +337,45 @@ open class UserSwitcherActivity @Inject constructor(
val item = getItem(position)
val view = convertView ?: layoutInflater.inflate(resource, parent, false)
- view.requireViewById<ImageView>(R.id.icon).apply {
- setImageDrawable(iconGetter(item))
- }
- view.requireViewById<TextView>(R.id.text).apply {
- setText(textGetter(item))
- }
+ view.requireViewById<ImageView>(R.id.icon).apply { setImageDrawable(iconGetter(item)) }
+ view.requireViewById<TextView>(R.id.text).apply { setText(textGetter(item)) }
return view
}
}
- private inner class UserAdapter : BaseUserAdapter(userSwitcherController) {
+ private inner class UserAdapter : BaseUserSwitcherAdapter(userSwitcherController) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val item = getItem(position)
var view = convertView as ViewGroup?
if (view == null) {
- view = layoutInflater.inflate(
- R.layout.user_switcher_fullscreen_item,
- parent,
- false
- ) as ViewGroup
- }
- (view.getChildAt(0) as ImageView).apply {
- setImageDrawable(getDrawable(item))
- }
- (view.getChildAt(1) as TextView).apply {
- setText(getName(getContext(), item))
+ view =
+ layoutInflater.inflate(R.layout.user_switcher_fullscreen_item, parent, false)
+ as ViewGroup
}
+ (view.getChildAt(0) as ImageView).apply { setImageDrawable(getDrawable(item)) }
+ (view.getChildAt(1) as TextView).apply { setText(getName(getContext(), item)) }
view.setEnabled(item.isSwitchToEnabled)
- view.setAlpha(
- if (view.isEnabled()) {
- USER_SWITCH_ENABLED_ALPHA
- } else {
- USER_SWITCH_DISABLED_ALPHA
- }
- )
+ UserSwitcherController.setSelectableAlpha(view)
view.setTag(USER_VIEW)
return view
}
- override fun getName(context: Context, item: UserRecord): String {
+ override fun getName(context: Context, item: UserRecord, isTablet: Boolean): String {
return if (item == manageUserRecord) {
getString(R.string.manage_users)
} else {
- super.getName(context, item)
+ super.getName(context, item, isTablet)
}
}
- fun findUserIcon(item: UserRecord): Drawable {
+ fun findUserIcon(item: UserRecord, isTablet: Boolean = false): Drawable {
if (item == manageUserRecord) {
return getDrawable(R.drawable.ic_manage_users)
}
if (item.info == null) {
- return getIconDrawable(this@UserSwitcherActivity, item)
+ return getIconDrawable(this@UserSwitcherActivity, item, isTablet)
}
val userIcon = userManager.getUserIcon(item.info.id)
if (userIcon != null) {
@@ -398,23 +385,20 @@ open class UserSwitcherActivity @Inject constructor(
}
fun getTotalUserViews(): Int {
- return users.count { item ->
- !doNotRenderUserView(item)
- }
+ return users.count { item -> !doNotRenderUserView(item) }
}
fun doNotRenderUserView(item: UserRecord): Boolean {
- return item.isAddUser ||
- item.isAddSupervisedUser ||
- item.isGuest && item.info == null
+ return item.isAddUser || item.isAddSupervisedUser || item.isGuest && item.info == null
}
private fun getDrawable(item: UserRecord): Drawable {
- var drawable = if (item.isGuest) {
- getDrawable(R.drawable.ic_account_circle)
- } else {
- findUserIcon(item)
- }
+ var drawable =
+ if (item.isGuest) {
+ getDrawable(R.drawable.ic_account_circle)
+ } else {
+ findUserIcon(item)
+ }
drawable.mutate()
if (!item.isCurrent && !item.isSwitchToEnabled) {
@@ -426,16 +410,16 @@ open class UserSwitcherActivity @Inject constructor(
)
}
- val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate()
- as LayerDrawable
- if (item == userSwitcherController.getCurrentUserRecord()) {
+ val ld = getDrawable(R.drawable.user_switcher_icon_large).mutate() as LayerDrawable
+ if (item == userSwitcherController.currentUserRecord) {
(ld.findDrawableByLayerId(R.id.ring) as GradientDrawable).apply {
- val stroke = resources
- .getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width)
- val color = Utils.getColorAttrDefaultColor(
- this@UserSwitcherActivity,
- com.android.internal.R.attr.colorAccentPrimary
- )
+ val stroke =
+ resources.getDimensionPixelSize(R.dimen.user_switcher_icon_selected_width)
+ val color =
+ Utils.getColorAttrDefaultColor(
+ this@UserSwitcherActivity,
+ com.android.internal.R.attr.colorAccentPrimary
+ )
setStroke(stroke, color)
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 305b5ee920a1..035638800f9c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -99,7 +99,7 @@ constructor(
override val actions: Flow<List<UserActionModel>> =
userRecords.map { records -> records.filter { it.isNotUser() }.map { it.toActionModel() } }
- override val isActionableWhenLocked: Flow<Boolean> = controller.addUsersFromLockScreen
+ override val isActionableWhenLocked: Flow<Boolean> = controller.isAddUsersFromLockScreenEnabled
override val isGuestUserAutoCreated: Boolean = controller.isGuestUserAutoCreated
diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
index 18369d9a71d2..15fdc352d864 100644
--- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt
@@ -49,8 +49,11 @@ object LegacyUserUiHelper {
isAddUser: Boolean,
isGuest: Boolean,
isAddSupervisedUser: Boolean,
+ isTablet: Boolean = false,
): Int {
- return if (isAddUser) {
+ return if (isAddUser && isTablet) {
+ R.drawable.ic_account_circle_filled
+ } else if (isAddUser) {
R.drawable.ic_add
} else if (isGuest) {
R.drawable.ic_account_circle
@@ -67,6 +70,7 @@ object LegacyUserUiHelper {
record: UserRecord,
isGuestUserAutoCreated: Boolean,
isGuestUserResetting: Boolean,
+ isTablet: Boolean = false,
): String {
val resourceId: Int? = getGuestUserRecordNameResourceId(record)
return when {
@@ -80,6 +84,7 @@ object LegacyUserUiHelper {
isGuestUserResetting = isGuestUserResetting,
isAddUser = record.isAddUser,
isAddSupervisedUser = record.isAddSupervisedUser,
+ isTablet = isTablet,
)
)
}
@@ -108,12 +113,14 @@ object LegacyUserUiHelper {
isGuestUserResetting: Boolean,
isAddUser: Boolean,
isAddSupervisedUser: Boolean,
+ isTablet: Boolean = false,
): Int {
check(isGuest || isAddUser || isAddSupervisedUser)
return when {
isGuest && isGuestUserAutoCreated && isGuestUserResetting ->
com.android.settingslib.R.string.guest_resetting
+ isGuest && isTablet -> com.android.settingslib.R.string.guest_new_guest
isGuest && isGuestUserAutoCreated -> com.android.internal.R.string.guest_name
isGuest -> com.android.internal.R.string.guest_name
isAddUser -> com.android.settingslib.R.string.user_add_user
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
index 83a3d0d0457a..938417f9dbe3 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/binder/UserSwitcherViewBinder.kt
@@ -129,7 +129,10 @@ object UserSwitcherViewBinder {
viewModel.users.collect { users ->
val viewPool =
view.children.filter { it.tag == USER_VIEW_TAG }.toMutableList()
- viewPool.forEach { view.removeView(it) }
+ viewPool.forEach {
+ view.removeView(it)
+ flowWidget.removeView(it)
+ }
users.forEach { userViewModel ->
val userView =
if (viewPool.isNotEmpty()) {
@@ -171,6 +174,7 @@ object UserSwitcherViewBinder {
setOnItemClickListener { _, _, position, _ ->
val itemPositionExcludingHeader = position - 1
adapter.getItem(itemPositionExcludingHeader).onClicked()
+ dismiss()
}
show()
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index 66ce01b7a86e..398341d256d2 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -20,6 +20,7 @@ package com.android.systemui.user.ui.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.android.systemui.R
+import com.android.systemui.common.ui.drawable.CircularDrawable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.user.domain.interactor.UserInteractor
import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper
@@ -130,7 +131,7 @@ private constructor(
return UserViewModel(
viewKey = model.id,
name = model.name,
- image = model.image,
+ image = CircularDrawable(model.image),
isSelectionMarkerVisible = model.isSelected,
alpha =
if (model.isSelectable) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt b/packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt
new file mode 100644
index 000000000000..ac931e510139
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/recycler/HorizontalSpacerItemDecoration.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.recycler
+
+import android.graphics.Rect
+import android.view.View
+import androidx.annotation.Dimension
+import androidx.recyclerview.widget.RecyclerView
+
+/**
+ * RecyclerView ItemDecorator that adds a horizontal space of the given size between items
+ * and double that space on the ends.
+ */
+class HorizontalSpacerItemDecoration(@Dimension private val offset: Int) :
+ RecyclerView.ItemDecoration() {
+
+ override fun getItemOffsets(
+ outRect: Rect,
+ view: View,
+ parent: RecyclerView,
+ state: RecyclerView.State
+ ) {
+ val position: Int = parent.getChildAdapterPosition(view)
+ val itemCount = parent.adapter?.itemCount ?: 0
+
+ val left = if (position == 0) offset * 2 else offset
+ val right = if (position == itemCount - 1) offset * 2 else offset
+
+ outRect.set(left, 0, right, 0)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index c7ba5182eef8..903aba1a74a4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -48,6 +48,7 @@ import android.app.KeyguardManager;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
@@ -74,6 +75,7 @@ import android.os.VibrationEffect;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.InputFilter;
+import android.util.FeatureFlagUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -1049,7 +1051,13 @@ public class VolumeDialogImpl implements VolumeDialog,
Events.writeEvent(Events.EVENT_SETTINGS_CLICK);
dismissH(DISMISS_REASON_SETTINGS_CLICKED);
mMediaOutputDialogFactory.dismiss();
- mVolumePanelFactory.create(true /* aboveStatusBar */, null);
+ if (FeatureFlagUtils.isEnabled(mContext,
+ FeatureFlagUtils.SETTINGS_VOLUME_PANEL_IN_SYSTEMUI)) {
+ mVolumePanelFactory.create(true /* aboveStatusBar */, null);
+ } else {
+ mActivityStarter.startActivity(new Intent(Settings.Panel.ACTION_VOLUME),
+ true /* dismissShade */);
+ }
});
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
index 2878ad90835b..0f7e14374e60 100644
--- a/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/ImageWallpaper.java
@@ -16,12 +16,21 @@
package com.android.systemui.wallpapers;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.systemui.flags.Flags.USE_CANVAS_RENDERER;
+
import android.app.WallpaperColors;
+import android.app.WallpaperManager;
+import android.content.ComponentCallbacks2;
+import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
+import android.os.AsyncTask;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
@@ -31,16 +40,22 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.MathUtils;
import android.util.Size;
+import android.view.Display;
+import android.view.DisplayInfo;
+import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.wallpapers.canvas.ImageCanvasWallpaperRenderer;
import com.android.systemui.wallpapers.gl.EglHelper;
import com.android.systemui.wallpapers.gl.ImageWallpaperRenderer;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -59,16 +74,19 @@ public class ImageWallpaper extends WallpaperService {
private static final @android.annotation.NonNull RectF LOCAL_COLOR_BOUNDS =
new RectF(0, 0, 1, 1);
private static final boolean DEBUG = false;
+
private final ArrayList<RectF> mLocalColorsToAdd = new ArrayList<>();
private final ArraySet<RectF> mColorAreas = new ArraySet<>();
private volatile int mPages = 1;
private HandlerThread mWorker;
// scaled down version
private Bitmap mMiniBitmap;
+ private final FeatureFlags mFeatureFlags;
@Inject
- public ImageWallpaper() {
+ public ImageWallpaper(FeatureFlags featureFlags) {
super();
+ mFeatureFlags = featureFlags;
}
@Override
@@ -80,7 +98,7 @@ public class ImageWallpaper extends WallpaperService {
@Override
public Engine onCreateEngine() {
- return new GLEngine();
+ return mFeatureFlags.isEnabled(USE_CANVAS_RENDERER) ? new CanvasEngine() : new GLEngine();
}
@Override
@@ -489,4 +507,270 @@ public class ImageWallpaper extends WallpaperService {
mRenderer.dump(prefix, fd, out, args);
}
}
+
+
+ class CanvasEngine extends WallpaperService.Engine implements DisplayListener {
+
+ // time [ms] before unloading the wallpaper after it is loaded
+ private static final int DELAY_FORGET_WALLPAPER = 5000;
+
+ private final Runnable mUnloadWallpaperCallback = this::unloadWallpaper;
+
+ private WallpaperManager mWallpaperManager;
+ private ImageCanvasWallpaperRenderer mImageCanvasWallpaperRenderer;
+ private Bitmap mBitmap;
+
+ private Display mDisplay;
+ private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
+
+ private AsyncTask<Void, Void, Bitmap> mLoader;
+ private boolean mNeedsDrawAfterLoadingWallpaper = false;
+
+ CanvasEngine() {
+ super();
+ setFixedSizeAllowed(true);
+ setShowForAllUsers(true);
+ }
+
+ void trimMemory(int level) {
+ if (level >= ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW
+ && level <= ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL
+ && isBitmapLoaded()) {
+ if (DEBUG) {
+ Log.d(TAG, "trimMemory");
+ }
+ unloadWallpaper();
+ }
+ }
+
+ @Override
+ public void onCreate(SurfaceHolder surfaceHolder) {
+ if (DEBUG) {
+ Log.d(TAG, "onCreate");
+ }
+
+ mWallpaperManager = getSystemService(WallpaperManager.class);
+ super.onCreate(surfaceHolder);
+
+ final Context displayContext = getDisplayContext();
+ final int displayId = displayContext == null ? DEFAULT_DISPLAY :
+ displayContext.getDisplayId();
+ DisplayManager dm = getSystemService(DisplayManager.class);
+ if (dm != null) {
+ mDisplay = dm.getDisplay(displayId);
+ if (mDisplay == null) {
+ Log.e(TAG, "Cannot find display! Fallback to default.");
+ mDisplay = dm.getDisplay(DEFAULT_DISPLAY);
+ }
+ }
+ setOffsetNotificationsEnabled(false);
+
+ mImageCanvasWallpaperRenderer = new ImageCanvasWallpaperRenderer(surfaceHolder);
+ loadWallpaper(false);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unloadWallpaper();
+ }
+
+ @Override
+ public boolean shouldZoomOutWallpaper() {
+ return true;
+ }
+
+ @Override
+ public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ if (DEBUG) {
+ Log.d(TAG, "onSurfaceChanged: width=" + width + ", height=" + height);
+ }
+ super.onSurfaceChanged(holder, format, width, height);
+ mImageCanvasWallpaperRenderer.setSurfaceHolder(holder);
+ drawFrame(false);
+ }
+
+ @Override
+ public void onSurfaceDestroyed(SurfaceHolder holder) {
+ super.onSurfaceDestroyed(holder);
+ if (DEBUG) {
+ Log.i(TAG, "onSurfaceDestroyed");
+ }
+ mImageCanvasWallpaperRenderer.setSurfaceHolder(null);
+ }
+
+ @Override
+ public void onSurfaceCreated(SurfaceHolder holder) {
+ super.onSurfaceCreated(holder);
+ if (DEBUG) {
+ Log.i(TAG, "onSurfaceCreated");
+ }
+ mImageCanvasWallpaperRenderer.setSurfaceHolder(holder);
+ }
+
+ @Override
+ public void onSurfaceRedrawNeeded(SurfaceHolder holder) {
+ if (DEBUG) {
+ Log.d(TAG, "onSurfaceRedrawNeeded");
+ }
+ super.onSurfaceRedrawNeeded(holder);
+ // At the end of this method we should have drawn into the surface.
+ // This means that the bitmap should be loaded synchronously if
+ // it was already unloaded.
+ if (!isBitmapLoaded()) {
+ setBitmap(mWallpaperManager.getBitmap(true /* hardware */));
+ }
+ drawFrame(true);
+ }
+
+ private DisplayInfo getDisplayInfo() {
+ mDisplay.getDisplayInfo(mTmpDisplayInfo);
+ return mTmpDisplayInfo;
+ }
+
+ private void drawFrame(boolean forceRedraw) {
+ if (!mImageCanvasWallpaperRenderer.isSurfaceHolderLoaded()) {
+ Log.e(TAG, "attempt to draw a frame without a valid surface");
+ return;
+ }
+
+ if (!isBitmapLoaded()) {
+ // ensure that we load the wallpaper.
+ // if the wallpaper is currently loading, this call will have no effect.
+ loadWallpaper(true);
+ return;
+ }
+ mImageCanvasWallpaperRenderer.drawFrame(mBitmap, forceRedraw);
+ }
+
+ private void setBitmap(Bitmap bitmap) {
+ if (bitmap == null) {
+ Log.e(TAG, "Attempt to set a null bitmap");
+ } else if (mBitmap == bitmap) {
+ Log.e(TAG, "The value of bitmap is the same");
+ } else if (bitmap.getWidth() < 1 || bitmap.getHeight() < 1) {
+ Log.e(TAG, "Attempt to set an invalid wallpaper of length "
+ + bitmap.getWidth() + "x" + bitmap.getHeight());
+ } else {
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ }
+ mBitmap = bitmap;
+ }
+ }
+
+ private boolean isBitmapLoaded() {
+ return mBitmap != null && !mBitmap.isRecycled();
+ }
+
+ /**
+ * Loads the wallpaper on background thread and schedules updating the surface frame,
+ * and if {@code needsDraw} is set also draws a frame.
+ *
+ * If loading is already in-flight, subsequent loads are ignored (but needDraw is or-ed to
+ * the active request).
+ *
+ */
+ private void loadWallpaper(boolean needsDraw) {
+ mNeedsDrawAfterLoadingWallpaper |= needsDraw;
+ if (mLoader != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Skipping loadWallpaper, already in flight ");
+ }
+ return;
+ }
+ mLoader = new AsyncTask<Void, Void, Bitmap>() {
+ @Override
+ protected Bitmap doInBackground(Void... params) {
+ Throwable exception;
+ try {
+ Bitmap wallpaper = mWallpaperManager.getBitmap(true /* hardware */);
+ if (wallpaper != null
+ && wallpaper.getByteCount() > RecordingCanvas.MAX_BITMAP_SIZE) {
+ throw new RuntimeException("Wallpaper is too large to draw!");
+ }
+ return wallpaper;
+ } catch (RuntimeException | OutOfMemoryError e) {
+ exception = e;
+ }
+
+ if (isCancelled()) {
+ return null;
+ }
+
+ // Note that if we do fail at this, and the default wallpaper can't
+ // be loaded, we will go into a cycle. Don't do a build where the
+ // default wallpaper can't be loaded.
+ Log.w(TAG, "Unable to load wallpaper!", exception);
+ try {
+ mWallpaperManager.clear();
+ } catch (IOException ex) {
+ // now we're really screwed.
+ Log.w(TAG, "Unable reset to default wallpaper!", ex);
+ }
+
+ if (isCancelled()) {
+ return null;
+ }
+
+ try {
+ return mWallpaperManager.getBitmap(true /* hardware */);
+ } catch (RuntimeException | OutOfMemoryError e) {
+ Log.w(TAG, "Unable to load default wallpaper!", e);
+ }
+ return null;
+ }
+
+ @Override
+ protected void onPostExecute(Bitmap bitmap) {
+ setBitmap(bitmap);
+
+ if (mNeedsDrawAfterLoadingWallpaper) {
+ drawFrame(true);
+ }
+
+ mLoader = null;
+ mNeedsDrawAfterLoadingWallpaper = false;
+ scheduleUnloadWallpaper();
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ private void unloadWallpaper() {
+ if (mLoader != null) {
+ mLoader.cancel(false);
+ mLoader = null;
+ }
+
+ if (mBitmap != null) {
+ mBitmap.recycle();
+ }
+ mBitmap = null;
+
+ final Surface surface = getSurfaceHolder().getSurface();
+ surface.hwuiDestroy();
+ mWallpaperManager.forgetLoadedWallpaper();
+ }
+
+ private void scheduleUnloadWallpaper() {
+ Handler handler = getMainThreadHandler();
+ handler.removeCallbacks(mUnloadWallpaperCallback);
+ handler.postDelayed(mUnloadWallpaperCallback, DELAY_FORGET_WALLPAPER);
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRenderer.java b/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRenderer.java
new file mode 100644
index 000000000000..fdba16ed2059
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRenderer.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.wallpapers.canvas;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Helper to draw a wallpaper on a surface.
+ * It handles the geometry regarding the dimensions of the display and the wallpaper,
+ * and rescales the surface and the wallpaper accordingly.
+ */
+public class ImageCanvasWallpaperRenderer {
+
+ private static final String TAG = ImageCanvasWallpaperRenderer.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private SurfaceHolder mSurfaceHolder;
+ //private Bitmap mBitmap = null;
+
+ @VisibleForTesting
+ static final int MIN_SURFACE_WIDTH = 128;
+ @VisibleForTesting
+ static final int MIN_SURFACE_HEIGHT = 128;
+
+ private boolean mSurfaceRedrawNeeded;
+
+ private int mLastSurfaceWidth = -1;
+ private int mLastSurfaceHeight = -1;
+
+ public ImageCanvasWallpaperRenderer(SurfaceHolder surfaceHolder) {
+ mSurfaceHolder = surfaceHolder;
+ }
+
+ /**
+ * Set the surface holder on which to draw.
+ * Should be called when the surface holder is created or changed
+ * @param surfaceHolder the surface on which to draw the wallpaper
+ */
+ public void setSurfaceHolder(SurfaceHolder surfaceHolder) {
+ mSurfaceHolder = surfaceHolder;
+ }
+
+ /**
+ * Check if a surface holder is loaded
+ * @return true if a valid surfaceHolder has been set.
+ */
+ public boolean isSurfaceHolderLoaded() {
+ return mSurfaceHolder != null;
+ }
+
+ /**
+ * Computes and set the surface dimensions, by using the play and the bitmap dimensions.
+ * The Bitmap must be loaded before any call to this function
+ */
+ private boolean updateSurfaceSize(Bitmap bitmap) {
+ int surfaceWidth = Math.max(MIN_SURFACE_WIDTH, bitmap.getWidth());
+ int surfaceHeight = Math.max(MIN_SURFACE_HEIGHT, bitmap.getHeight());
+ boolean surfaceChanged =
+ surfaceWidth != mLastSurfaceWidth || surfaceHeight != mLastSurfaceHeight;
+ if (surfaceChanged) {
+ /*
+ Used a fixed size surface, because we are special. We can do
+ this because we know the current design of window animations doesn't
+ cause this to break.
+ */
+ mSurfaceHolder.setFixedSize(surfaceWidth, surfaceHeight);
+ mLastSurfaceWidth = surfaceWidth;
+ mLastSurfaceHeight = surfaceHeight;
+ }
+ return surfaceChanged;
+ }
+
+ /**
+ * Draw a the wallpaper on the surface.
+ * The bitmap and the surface must be loaded before calling
+ * this function.
+ * @param forceRedraw redraw the wallpaper even if no changes are detected
+ */
+ public void drawFrame(Bitmap bitmap, boolean forceRedraw) {
+
+ if (bitmap == null || bitmap.isRecycled()) {
+ Log.e(TAG, "Attempt to draw frame before background is loaded:");
+ return;
+ }
+
+ if (bitmap.getWidth() < 1 || bitmap.getHeight() < 1) {
+ Log.e(TAG, "Attempt to set an invalid wallpaper of length "
+ + bitmap.getWidth() + "x" + bitmap.getHeight());
+ return;
+ }
+
+ mSurfaceRedrawNeeded |= forceRedraw;
+ boolean surfaceChanged = updateSurfaceSize(bitmap);
+
+ boolean redrawNeeded = surfaceChanged || mSurfaceRedrawNeeded;
+ mSurfaceRedrawNeeded = false;
+
+ if (!redrawNeeded) {
+ if (DEBUG) {
+ Log.d(TAG, "Suppressed drawFrame since redraw is not needed ");
+ }
+ return;
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, "Redrawing wallpaper");
+ }
+ drawWallpaperWithCanvas(bitmap);
+ }
+
+ @VisibleForTesting
+ void drawWallpaperWithCanvas(Bitmap bitmap) {
+ Canvas c = mSurfaceHolder.lockHardwareCanvas();
+ if (c != null) {
+ Rect dest = mSurfaceHolder.getSurfaceFrame();
+ Log.i(TAG, "Redrawing in rect: " + dest + " with surface size: "
+ + mLastSurfaceWidth + "x" + mLastSurfaceHeight);
+ try {
+ c.drawBitmap(bitmap, null, dest, null);
+ } finally {
+ mSurfaceHolder.unlockCanvasAndPost(c);
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 3961a8bcc980..3472cb1c2a7d 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -55,6 +55,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.systemui.tracing.nano.SystemUiTraceProto;
+import com.android.wm.shell.floating.FloatingTasks;
import com.android.wm.shell.nano.WmShellTraceProto;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedEventCallback;
@@ -106,6 +107,7 @@ public final class WMShell extends CoreStartable
private final Optional<Pip> mPipOptional;
private final Optional<SplitScreen> mSplitScreenOptional;
private final Optional<OneHanded> mOneHandedOptional;
+ private final Optional<FloatingTasks> mFloatingTasksOptional;
private final CommandQueue mCommandQueue;
private final ConfigurationController mConfigurationController;
@@ -166,6 +168,7 @@ public final class WMShell extends CoreStartable
Optional<Pip> pipOptional,
Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
+ Optional<FloatingTasks> floatingTasksOptional,
CommandQueue commandQueue,
ConfigurationController configurationController,
KeyguardStateController keyguardStateController,
@@ -190,6 +193,7 @@ public final class WMShell extends CoreStartable
mWakefulnessLifecycle = wakefulnessLifecycle;
mProtoTracer = protoTracer;
mUserTracker = userTracker;
+ mFloatingTasksOptional = floatingTasksOptional;
mSysUiMainExecutor = sysUiMainExecutor;
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 914d94522e83..25e7dbb5f26d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -119,6 +119,20 @@ class ClockEventControllerTest : SysuiTestCase() {
}
@Test
+ fun fontChanged_verifyFontSizeUpdated() {
+ clockEventController.clock = clock
+ verify(events).onColorPaletteChanged(any(), any(), any())
+
+ clockEventController.registerListeners()
+
+ val captor = argumentCaptor<ConfigurationController.ConfigurationListener>()
+ verify(configurationController).addCallback(capture(captor))
+ captor.value.onDensityOrFontScaleChanged()
+
+ verify(events).onFontSettingChanged()
+ }
+
+ @Test
fun batteryCallback_keyguardShowingCharging_verifyChargeAnimation() {
clockEventController.clock = clock
clockEventController.registerListeners()
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index 0bf038d45af0..aca60c033bac 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -86,12 +86,12 @@ private fun faceModel(user: Int) = KeyguardFaceListenModel(
becauseCannotSkipBouncer = false,
biometricSettingEnabledForUser = false,
bouncerFullyShown = false,
- bouncerIsOrWillShow = false,
- onlyFaceEnrolled = false,
faceAuthenticated = false,
faceDisabled = false,
+ faceLockedOut = false,
+ fpLockedOut = false,
goingToSleep = false,
- keyguardAwakeExcludingBouncerShowing = false,
+ keyguardAwake = false,
keyguardGoingAway = false,
listeningForFaceAssistant = false,
occludingAppRequestingFaceAuth = false,
@@ -99,5 +99,5 @@ private fun faceModel(user: Int) = KeyguardFaceListenModel(
scanningAllowedByStrongAuth = false,
secureCameraLaunched = false,
switchingUser = false,
- udfpsBouncerShowing = false
+ udfpsBouncerShowing = false,
)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
new file mode 100644
index 000000000000..9e5bfe53ea05
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.internal.util.LatencyTracker
+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.classifier.FalsingCollectorFake
+import com.android.systemui.statusbar.policy.DevicePostureController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.any
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class KeyguardPinViewControllerTest : SysuiTestCase() {
+ @Mock private lateinit var keyguardPinView: KeyguardPINView
+
+ @Mock private lateinit var keyguardMessageArea: BouncerKeyguardMessageArea
+
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+
+ @Mock private lateinit var securityMode: SecurityMode
+
+ @Mock private lateinit var lockPatternUtils: LockPatternUtils
+
+ @Mock private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback
+
+ @Mock
+ private lateinit var keyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory
+
+ @Mock
+ private lateinit var keyguardMessageAreaController:
+ KeyguardMessageAreaController<BouncerKeyguardMessageArea>
+
+ @Mock private lateinit var mLatencyTracker: LatencyTracker
+
+ @Mock private lateinit var liftToActivateListener: LiftToActivateListener
+
+ @Mock private val mEmergencyButtonController: EmergencyButtonController? = null
+ private val falsingCollector: FalsingCollector = FalsingCollectorFake()
+ @Mock lateinit var postureController: DevicePostureController
+
+ lateinit var pinViewController: KeyguardPinViewController
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ Mockito.`when`(keyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
+ .thenReturn(keyguardMessageArea)
+ Mockito.`when`(
+ keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
+ )
+ .thenReturn(keyguardMessageAreaController)
+ pinViewController =
+ KeyguardPinViewController(
+ keyguardPinView,
+ keyguardUpdateMonitor,
+ securityMode,
+ lockPatternUtils,
+ mKeyguardSecurityCallback,
+ keyguardMessageAreaControllerFactory,
+ mLatencyTracker,
+ liftToActivateListener,
+ mEmergencyButtonController,
+ falsingCollector,
+ postureController
+ )
+ }
+
+ @Test
+ fun startAppearAnimation() {
+ pinViewController.startAppearAnimation()
+ verify(keyguardMessageAreaController).setMessageIfEmpty(R.string.keyguard_enter_your_pin)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
index 28e99da49496..43f6f1aac097 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java
@@ -116,9 +116,7 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase {
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
when(mUserSwitcherController.getCurrentUserName()).thenReturn("Test User");
- when(mUserSwitcherController.getKeyguardStateController())
- .thenReturn(mKeyguardStateController);
- when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mUserSwitcherController.isKeyguardShowing()).thenReturn(true);
mScreenWidth = getUiDevice().getDisplayWidth();
mFakeMeasureSpec = View
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 093e59255f65..12d3d42cb29f 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -20,6 +20,7 @@ import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
+import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
@@ -212,8 +213,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mBiometricEnabledCallbackArgCaptor;
@Captor
private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor;
- @Captor
- private ArgumentCaptor<CancellationSignal> mCancellationSignalCaptor;
// Direct executor
private final Executor mBackgroundExecutor = Runnable::run;
@@ -328,8 +327,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@After
public void tearDown() {
mMockitoSession.finishMocking();
- mKeyguardUpdateMonitor.removeCallback(mTestCallback);
- mKeyguardUpdateMonitor.destroy();
+ cleanupKeyguardUpdateMonitor();
}
@Test
@@ -351,6 +349,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testSimStateInitialized() {
+ cleanupKeyguardUpdateMonitor();
final int subId = 3;
final int state = TelephonyManager.SIM_STATE_ABSENT;
@@ -596,13 +595,11 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testTriesToAuthenticate_whenBouncer() {
- fingerprintIsNotEnrolled();
- faceAuthEnabled();
setKeyguardBouncerVisibility(true);
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
- verify(mFaceManager, atLeastOnce()).isHardwareDetected();
- verify(mFaceManager, atLeastOnce()).hasEnrolledTemplates(anyInt());
+ verify(mFaceManager).isHardwareDetected();
+ verify(mFaceManager).hasEnrolledTemplates(anyInt());
}
@Test
@@ -751,8 +748,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- mKeyguardUpdateMonitor.mFaceAuthenticationCallback
- .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+ faceAuthLockedOut();
verify(mLockPatternUtils, never()).requireStrongAuth(anyInt(), anyInt());
}
@@ -764,7 +760,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
- .onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
+ .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
}
@@ -775,10 +771,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(true);
- mKeyguardUpdateMonitor.mFaceAuthenticationCallback
- .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
+ faceAuthLockedOut();
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
- .onAuthenticationError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
+ .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
}
@@ -1207,7 +1202,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() {
- mFaceManager = null;
+ cleanupKeyguardUpdateMonitor();
+ mSpiedContext.addMockSystemService(FaceManager.class, null);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(false);
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
@@ -1217,7 +1214,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException {
// Face auth should run when the following is true.
keyguardNotGoingAway();
- bouncerFullyVisibleAndNotGoingToSleep();
+ occludingAppRequestsFaceAuth();
currentUserIsPrimary();
strongAuthNotRequired();
biometricsEnabledForCurrentUser();
@@ -1225,6 +1222,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
biometricsNotDisabledThroughDevicePolicyManager();
userNotCurrentlySwitching();
mTestableLooper.processAllMessages();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
// Fingerprint is locked out.
fingerprintErrorLockedOut();
@@ -1236,9 +1234,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse()
throws RemoteException {
// Face auth should run when the following is true.
- faceAuthEnabled();
bouncerFullyVisibleAndNotGoingToSleep();
- fingerprintIsNotEnrolled();
keyguardNotGoingAway();
currentUserIsPrimary();
strongAuthNotRequired();
@@ -1259,12 +1255,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
+ cleanupKeyguardUpdateMonitor();
// This disables face auth
when(mUserManager.isPrimaryUser()).thenReturn(false);
mKeyguardUpdateMonitor =
new TestableKeyguardUpdateMonitor(mSpiedContext);
- // Preconditions for face auth to run
+ // Face auth should run when the following is true.
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
strongAuthNotRequired();
@@ -1281,7 +1278,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenStrongAuthDoesNotAllowScanning_returnsFalse()
throws RemoteException {
- // Preconditions for face auth to run
+ // Face auth should run when the following is true.
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
currentUserIsPrimary();
@@ -1302,11 +1299,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse()
throws RemoteException {
- // Preconditions for face auth to run
- faceAuthEnabled();
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- fingerprintIsNotEnrolled();
currentUserIsPrimary();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
@@ -1326,11 +1320,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse()
throws RemoteException {
- // Preconditions for face auth to run
- faceAuthEnabled();
+ // Face auth should run when the following is true.
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- fingerprintIsNotEnrolled();
currentUserIsPrimary();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
@@ -1349,11 +1341,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse()
throws RemoteException {
- // Preconditions for face auth to run
- faceAuthEnabled();
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
- fingerprintIsNotEnrolled();
currentUserIsPrimary();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
@@ -1372,7 +1361,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenOccludingAppRequestsFaceAuth_returnsTrue()
throws RemoteException {
- // Preconditions for face auth to run
+ // Face auth should run when the following is true.
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
currentUserIsPrimary();
@@ -1395,8 +1384,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue()
throws RemoteException {
- // Preconditions for face auth to run
- faceAuthEnabled();
+ // Face auth should run when the following is true.
keyguardNotGoingAway();
currentUserIsPrimary();
currentUserDoesNotHaveTrust();
@@ -1408,7 +1396,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
bouncerFullyVisibleAndNotGoingToSleep();
- fingerprintIsNotEnrolled();
mTestableLooper.processAllMessages();
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
@@ -1417,7 +1404,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testShouldListenForFace_whenAuthInterruptIsActive_returnsTrue()
throws RemoteException {
- // Preconditions for face auth to run
+ // Face auth should run when the following is true.
keyguardNotGoingAway();
currentUserIsPrimary();
currentUserDoesNotHaveTrust();
@@ -1443,7 +1430,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
userNotCurrentlySwitching();
- bouncerFullyVisible();
statusBarShadeIsLocked();
mTestableLooper.processAllMessages();
@@ -1457,9 +1443,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
keyguardIsVisible();
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
statusBarShadeIsNotLocked();
- assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
- bouncerNotFullyVisible();
-
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
}
@@ -1500,9 +1483,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
- public void testBouncerVisibility_whenBothFingerprintAndFaceIsEnrolled_stopsFaceAuth()
+ public void testShouldListenForFace_whenFaceIsLockedOut_returnsFalse()
throws RemoteException {
- // Both fingerprint and face are enrolled by default
// Preconditions for face auth to run
keyguardNotGoingAway();
currentUserIsPrimary();
@@ -1510,31 +1492,15 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
userNotCurrentlySwitching();
- deviceNotGoingToSleep();
- deviceIsInteractive();
- statusBarShadeIsNotLocked();
- keyguardIsVisible();
-
+ mKeyguardUpdateMonitor.setUdfpsBouncerShowing(true);
mTestableLooper.processAllMessages();
- clearInvocations(mUiEventLogger);
-
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
- mKeyguardUpdateMonitor.requestFaceAuth(true,
- FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
-
- verify(mFaceManager).authenticate(any(),
- mCancellationSignalCaptor.capture(),
- mAuthenticationCallbackCaptor.capture(),
- any(),
- anyInt(),
- anyBoolean());
- CancellationSignal cancelSignal = mCancellationSignalCaptor.getValue();
-
- bouncerWillBeVisibleSoon();
+ // Face is locked out.
+ faceAuthLockedOut();
mTestableLooper.processAllMessages();
- assertThat(cancelSignal.isCanceled()).isTrue();
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
}
@Test
@@ -1567,9 +1533,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testFingerAcquired_wakesUpPowerManager() {
- mContext.getOrCreateTestableResources().addOverride(
+ cleanupKeyguardUpdateMonitor();
+ mSpiedContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.kg_wake_on_acquire_start, true);
- mSpiedContext = spy(mContext);
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
fingerprintAcquireStart();
@@ -1578,28 +1544,26 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testFingerAcquired_doesNotWakeUpPowerManager() {
- mContext.getOrCreateTestableResources().addOverride(
+ cleanupKeyguardUpdateMonitor();
+ mSpiedContext.getOrCreateTestableResources().addOverride(
com.android.internal.R.bool.kg_wake_on_acquire_start, false);
- mSpiedContext = spy(mContext);
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);
fingerprintAcquireStart();
verify(mPowerManager, never()).wakeUp(anyLong(), anyInt(), anyString());
}
- private void faceAuthEnabled() {
- // this ensures KeyguardUpdateMonitor updates the cached mIsFaceEnrolled flag using the
- // face manager mock wire-up in setup()
- mKeyguardUpdateMonitor.isFaceAuthEnabledForUser(mCurrentUserId);
+ private void cleanupKeyguardUpdateMonitor() {
+ if (mKeyguardUpdateMonitor != null) {
+ mKeyguardUpdateMonitor.removeCallback(mTestCallback);
+ mKeyguardUpdateMonitor.destroy();
+ mKeyguardUpdateMonitor = null;
+ }
}
- private void fingerprintIsNotEnrolled() {
- when(mFingerprintManager.hasEnrolledTemplates(mCurrentUserId)).thenReturn(false);
- // This updates the cached fingerprint state.
- // There is no straightforward API to update the fingerprint state.
- // It currently works updates after enrollment changes because something else invokes
- // startListeningForFingerprint(), which internally calls this method.
- mKeyguardUpdateMonitor.isUnlockWithFingerprintPossible(mCurrentUserId);
+ private void faceAuthLockedOut() {
+ mKeyguardUpdateMonitor.mFaceAuthenticationCallback
+ .onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
}
private void statusBarShadeIsNotLocked() {
@@ -1708,19 +1672,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.dispatchStartedWakingUp();
}
- private void bouncerNotFullyVisible() {
- setKeyguardBouncerVisibility(false);
- }
-
private void bouncerFullyVisible() {
setKeyguardBouncerVisibility(true);
}
- private void bouncerWillBeVisibleSoon() {
- mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true, false);
- mTestableLooper.processAllMessages();
- }
-
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible);
mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index df10dfe9f160..5a26d05d7b37 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -1005,18 +1005,13 @@ public class ScreenDecorationsTest extends SysuiTestCase {
assertEquals(new Size(3, 3), resDelegate.getTopRoundedSize());
assertEquals(new Size(4, 4), resDelegate.getBottomRoundedSize());
- setupResources(20 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
- getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px)
- /* roundedTopDrawable */,
- getTestsDrawable(com.android.systemui.tests.R.drawable.rounded5px)
- /* roundedBottomDrawable */,
- 0 /* roundedPadding */, true /* privacyDot */, false /* faceScanning*/);
+ doReturn(2f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
mDisplayInfo.rotation = Surface.ROTATION_270;
mScreenDecorations.onConfigurationChanged(null);
- assertEquals(new Size(4, 4), resDelegate.getTopRoundedSize());
- assertEquals(new Size(5, 5), resDelegate.getBottomRoundedSize());
+ assertEquals(new Size(6, 6), resDelegate.getTopRoundedSize());
+ assertEquals(new Size(8, 8), resDelegate.getBottomRoundedSize());
}
@Test
@@ -1293,51 +1288,6 @@ public class ScreenDecorationsTest extends SysuiTestCase {
}
@Test
- public void testOnDisplayChanged_hwcLayer() {
- setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
- null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
- final DisplayDecorationSupport decorationSupport = new DisplayDecorationSupport();
- decorationSupport.format = PixelFormat.R_8;
- doReturn(decorationSupport).when(mDisplay).getDisplayDecorationSupport();
-
- // top cutout
- mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
-
- mScreenDecorations.start();
-
- final ScreenDecorHwcLayer hwcLayer = mScreenDecorations.mScreenDecorHwcLayer;
- spyOn(hwcLayer);
- doReturn(mDisplay).when(hwcLayer).getDisplay();
-
- mScreenDecorations.mDisplayListener.onDisplayChanged(1);
-
- verify(hwcLayer, times(1)).onDisplayChanged(any());
- }
-
- @Test
- public void testOnDisplayChanged_nonHwcLayer() {
- setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
- null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
- 0 /* roundedPadding */, false /* privacyDot */, false /* faceScanning */);
-
- // top cutout
- mMockCutoutList.add(new CutoutDecorProviderImpl(BOUNDS_POSITION_TOP));
-
- mScreenDecorations.start();
-
- final ScreenDecorations.DisplayCutoutView cutoutView = (ScreenDecorations.DisplayCutoutView)
- mScreenDecorations.getOverlayView(R.id.display_cutout);
- assertNotNull(cutoutView);
- spyOn(cutoutView);
- doReturn(mDisplay).when(cutoutView).getDisplay();
-
- mScreenDecorations.mDisplayListener.onDisplayChanged(1);
-
- verify(cutoutView, times(1)).onDisplayChanged(any());
- }
-
- @Test
public void testHasSameProvidersWithNullOverlays() {
setupResources(0 /* radius */, 0 /* radiusTop */, 0 /* radiusBottom */,
null /* roundedTopDrawable */, null /* roundedBottomDrawable */,
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 e2790e47fe06..a61cd23b60fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityLaunchAnimatorTest.kt
@@ -161,7 +161,18 @@ class ActivityLaunchAnimatorTest : SysuiTestCase() {
runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
waitForIdleSync()
- verify(controller).onLaunchAnimationCancelled()
+ verify(controller).onLaunchAnimationCancelled(false /* newKeyguardOccludedState */)
+ verify(controller, never()).onLaunchAnimationStart(anyBoolean())
+ }
+
+ @Test
+ fun passesOccludedStateToLaunchAnimationCancelled_ifTrue() {
+ val runner = activityLaunchAnimator.createRunner(controller)
+ runner.onAnimationCancelled(true /* isKeyguardOccluded */)
+ runner.onAnimationStart(0, emptyArray(), emptyArray(), emptyArray(), iCallback)
+
+ waitForIdleSync()
+ verify(controller).onLaunchAnimationCancelled(true /* newKeyguardOccludedState */)
verify(controller, never()).onLaunchAnimationStart(anyBoolean())
}
@@ -253,7 +264,7 @@ private class TestLaunchAnimatorController(override var launchContainer: ViewGro
assertOnMainThread()
}
- override fun onLaunchAnimationCancelled() {
+ override fun onLaunchAnimationCancelled(newKeyguardOccludedState: Boolean?) {
assertOnMainThread()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index bf3788e4c76a..4a5b23c02e40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -29,6 +29,7 @@ import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.testing.ViewUtils
+import android.view.KeyEvent
import android.view.View
import android.view.WindowInsets
import android.view.WindowManager
@@ -92,6 +93,21 @@ class AuthContainerViewTest : SysuiTestCase() {
}
@Test
+ fun testDismissesOnBack() {
+ val container = initializeFingerprintContainer(addToView = true)
+ assertThat(container.parent).isNotNull()
+ val root = container.rootView
+
+ // Simulate back invocation
+ container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK))
+ container.dispatchKeyEvent(KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK))
+ waitForIdleSync()
+
+ assertThat(container.parent).isNull()
+ assertThat(root.isAttachedToWindow).isFalse()
+ }
+
+ @Test
fun testIgnoresAnimatedInWhenDismissed() {
val container = initializeFingerprintContainer(addToView = false)
container.dismissFromSystemServer()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index 44ef922d2c39..37bb0c296735 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -18,6 +18,7 @@ package com.android.systemui.biometrics
import android.graphics.Point
import android.hardware.biometrics.BiometricSourceType
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
@@ -76,6 +77,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var statusBarStateController: StatusBarStateController
@Mock private lateinit var lightRevealScrim: LightRevealScrim
+ @Mock private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
@Before
fun setUp() {
@@ -86,6 +88,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
.startMocking()
`when`(RotationUtils.getRotation(context)).thenReturn(RotationUtils.ROTATION_NONE)
+ `when`(authController.udfpsProps).thenReturn(listOf(fpSensorProp))
`when`(udfpsControllerProvider.get()).thenReturn(udfpsController)
controller = AuthRippleController(
@@ -132,7 +135,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN update sensor location and show ripple
- verify(rippleView).setFingerprintSensorLocation(fpsLocation, -1f)
+ verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
verify(rippleView).startUnlockedRipple(any())
}
@@ -155,7 +158,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
false /* isStrongBiometric */)
// THEN update sensor location and show ripple
- verify(rippleView).setFingerprintSensorLocation(fpsLocation, -1f)
+ verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
verify(rippleView).startUnlockedRipple(any())
}
@@ -342,4 +345,23 @@ class AuthRippleControllerTest : SysuiTestCase() {
captor.value.onUiModeChanged()
verify(rippleView).setLockScreenColor(ArgumentMatchers.anyInt())
}
+
+ @Test
+ fun testUdfps_onFingerDown_showDwellRipple() {
+ // GIVEN view is already attached
+ controller.onViewAttached()
+ val captor = ArgumentCaptor.forClass(UdfpsController.Callback::class.java)
+ verify(udfpsController).addCallback(captor.capture())
+
+ // GIVEN fp is updated to Point(5, 5)
+ val fpsLocation = Point(5, 5)
+ `when`(authController.fingerprintSensorLocation).thenReturn(fpsLocation)
+
+ // WHEN finger is down
+ captor.value.onFingerDown()
+
+ // THEN update sensor location and show ripple
+ verify(rippleView).setFingerprintSensorLocation(fpsLocation, 0f)
+ verify(rippleView).startDwellRipple(false)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt
deleted file mode 100644
index 419fedf99c15..000000000000
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/BiometricMessageDeferralTest.kt
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics
-
-import android.testing.AndroidTestingRunner
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import org.junit.Assert.assertEquals
-import org.junit.Assert.assertFalse
-import org.junit.Assert.assertNull
-import org.junit.Assert.assertTrue
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidTestingRunner::class)
-class BiometricMessageDeferralTest : SysuiTestCase() {
-
- @Test
- fun testProcessNoMessages_noDeferredMessage() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf())
-
- assertNull(biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testProcessNonDeferredMessages_noDeferredMessage() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
-
- // WHEN there are no deferred messages processed
- for (i in 0..3) {
- biometricMessageDeferral.processMessage(4, "test")
- }
-
- // THEN getDeferredMessage is null
- assertNull(biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testAllProcessedMessagesWereDeferred() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1))
-
- // WHEN all the processed messages are a deferred message
- for (i in 0..3) {
- biometricMessageDeferral.processMessage(1, "test")
- }
-
- // THEN deferredMessage will return the string associated with the deferred msgId
- assertEquals("test", biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testReturnsMostFrequentDeferredMessage() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
-
- // WHEN there's two msgId=1 processed and one msgId=2 processed
- biometricMessageDeferral.processMessage(1, "msgId-1")
- biometricMessageDeferral.processMessage(1, "msgId-1")
- biometricMessageDeferral.processMessage(1, "msgId-1")
- biometricMessageDeferral.processMessage(2, "msgId-2")
-
- // THEN the most frequent deferred message is that meets the threshold is returned
- assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testDeferredMessage_mustMeetThreshold() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1))
-
- // WHEN more nonDeferredMessages are shown than the deferred message
- val totalMessages = 10
- val nonDeferredMessagesCount =
- (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1
- for (i in 0 until nonDeferredMessagesCount) {
- biometricMessageDeferral.processMessage(4, "non-deferred-msg")
- }
- for (i in nonDeferredMessagesCount until totalMessages) {
- biometricMessageDeferral.processMessage(1, "msgId-1")
- }
-
- // THEN there's no deferred message because it didn't meet the threshold
- assertNull(biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testDeferredMessage_manyExcludedMessages_getDeferredMessage() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1))
-
- // WHEN more excludedMessages are shown than the deferred message
- val totalMessages = 10
- val excludedMessagesCount = (totalMessages * BiometricMessageDeferral.THRESHOLD).toInt() + 1
- for (i in 0 until excludedMessagesCount) {
- biometricMessageDeferral.processMessage(3, "excluded-msg")
- }
- for (i in excludedMessagesCount until totalMessages) {
- biometricMessageDeferral.processMessage(1, "msgId-1")
- }
-
- // THEN there IS a deferred message because the deferred msg meets the threshold amongst the
- // non-excluded messages
- assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testResetClearsOutCounts() {
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(), setOf(1, 2))
-
- // GIVEN two msgId=1 events processed
- biometricMessageDeferral.processMessage(1, "msgId-1")
- biometricMessageDeferral.processMessage(1, "msgId-1")
-
- // WHEN counts are reset and then a single deferred message is processed (msgId=2)
- biometricMessageDeferral.reset()
- biometricMessageDeferral.processMessage(2, "msgId-2")
-
- // THEN msgId-2 is the deferred message since the two msgId=1 events were reset
- assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage())
- }
-
- @Test
- fun testShouldDefer() {
- // GIVEN should defer msgIds 1 and 2
- val biometricMessageDeferral = BiometricMessageDeferral(setOf(3), setOf(1, 2))
-
- // THEN shouldDefer returns true for ids 1 & 2
- assertTrue(biometricMessageDeferral.shouldDefer(1))
- assertTrue(biometricMessageDeferral.shouldDefer(2))
-
- // THEN should defer returns false for ids 3 & 4
- assertFalse(biometricMessageDeferral.shouldDefer(3))
- assertFalse(biometricMessageDeferral.shouldDefer(4))
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
new file mode 100644
index 000000000000..c9ccdb36da89
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/FaceHelpMessageDeferralTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.keyguard.logging.BiometricMessageDeferralLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertNull
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class FaceHelpMessageDeferralTest : SysuiTestCase() {
+ val threshold = .75f
+ @Mock lateinit var logger: BiometricMessageDeferralLogger
+ @Mock lateinit var dumpManager: DumpManager
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ @Test
+ fun testProcessFrame_logs() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1))
+ biometricMessageDeferral.processFrame(1)
+ verify(logger).logFrameProcessed(1, 1, "1")
+ }
+
+ @Test
+ fun testUpdateMessage_logs() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1))
+ biometricMessageDeferral.updateMessage(1, "hi")
+ verify(logger).logUpdateMessage(1, "hi")
+ }
+
+ @Test
+ fun testReset_logs() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1))
+ biometricMessageDeferral.reset()
+ verify(logger).reset()
+ }
+
+ @Test
+ fun testProcessNoMessages_noDeferredMessage() {
+ val biometricMessageDeferral = createMsgDeferral(emptySet())
+
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testProcessNonDeferredMessages_noDeferredMessage() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+ // WHEN there are no deferred messages processed
+ for (i in 0..3) {
+ biometricMessageDeferral.processFrame(4)
+ biometricMessageDeferral.updateMessage(4, "test")
+ }
+
+ // THEN getDeferredMessage is null
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testProcessMessagesWithDeferredMessage_deferredMessageWasNeverGivenAString() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+ biometricMessageDeferral.processFrame(1)
+
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testAllProcessedMessagesWereDeferred() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1))
+
+ // WHEN all the processed messages are a deferred message
+ for (i in 0..3) {
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.updateMessage(1, "test")
+ }
+
+ // THEN deferredMessage will return the string associated with the deferred msgId
+ assertEquals("test", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testReturnsMostFrequentDeferredMessage() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+ // WHEN there's 80%of the messages are msgId=1 and 20% is msgId=2
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.updateMessage(1, "msgId-1")
+
+ biometricMessageDeferral.processFrame(2)
+ biometricMessageDeferral.updateMessage(2, "msgId-2")
+
+ // THEN the most frequent deferred message is that meets the threshold is returned
+ assertEquals("msgId-1", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testDeferredMessage_mustMeetThreshold() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1))
+
+ // WHEN more nonDeferredMessages are shown than the deferred message
+ val totalMessages = 10
+ val nonDeferredMessagesCount = (totalMessages * threshold).toInt() + 1
+ for (i in 0 until nonDeferredMessagesCount) {
+ biometricMessageDeferral.processFrame(4)
+ biometricMessageDeferral.updateMessage(4, "non-deferred-msg")
+ }
+ for (i in nonDeferredMessagesCount until totalMessages) {
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.updateMessage(1, "msgId-1")
+ }
+
+ // THEN there's no deferred message because it didn't meet the threshold
+ assertNull(biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testResetClearsOutCounts() {
+ val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+ // GIVEN two msgId=1 events processed
+ biometricMessageDeferral.processFrame(
+ 1,
+ )
+ biometricMessageDeferral.updateMessage(1, "msgId-1")
+ biometricMessageDeferral.processFrame(1)
+ biometricMessageDeferral.updateMessage(1, "msgId-1")
+
+ // WHEN counts are reset and then a single deferred message is processed (msgId=2)
+ biometricMessageDeferral.reset()
+ biometricMessageDeferral.processFrame(2)
+ biometricMessageDeferral.updateMessage(2, "msgId-2")
+
+ // THEN msgId-2 is the deferred message since the two msgId=1 events were reset
+ assertEquals("msgId-2", biometricMessageDeferral.getDeferredMessage())
+ }
+
+ @Test
+ fun testShouldDefer() {
+ // GIVEN should defer msgIds 1 and 2
+ val biometricMessageDeferral = createMsgDeferral(setOf(1, 2))
+
+ // THEN shouldDefer returns true for ids 1 & 2
+ assertTrue(biometricMessageDeferral.shouldDefer(1))
+ assertTrue(biometricMessageDeferral.shouldDefer(2))
+
+ // THEN should defer returns false for ids 3 & 4
+ assertFalse(biometricMessageDeferral.shouldDefer(3))
+ assertFalse(biometricMessageDeferral.shouldDefer(4))
+ }
+
+ private fun createMsgDeferral(messagesToDefer: Set<Int>): BiometricMessageDeferral {
+ return BiometricMessageDeferral(messagesToDefer, threshold, logger, dumpManager)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index cb8358dd22cc..5c564e65ea86 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -17,23 +17,13 @@
package com.android.systemui.biometrics
import android.graphics.Rect
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTINGS
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
-import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
-import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
+import android.hardware.biometrics.BiometricOverlayConstants.*
import android.hardware.fingerprint.FingerprintManager
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
-import android.view.LayoutInflater
-import android.view.MotionEvent
-import android.view.View
-import android.view.Surface
+import android.view.*
import android.view.Surface.Rotation
-import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
@@ -65,7 +55,6 @@ import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
-private const val HAL_CONTROLS_ILLUMINATION = true
private const val REQUEST_ID = 2L
// Dimensions for the current display resolution.
@@ -95,8 +84,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var configurationController: ConfigurationController
@Mock private lateinit var systemClock: SystemClock
@Mock private lateinit var keyguardStateController: KeyguardStateController
- @Mock private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
- @Mock private lateinit var hbmProvider: UdfpsHbmProvider
+ @Mock private lateinit var unlockedScreenOffAnimationController:
+ UnlockedScreenOffAnimationController
+ @Mock private lateinit var udfpsDisplayMode: UdfpsDisplayModeProvider
@Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var udfpsView: UdfpsView
@@ -130,8 +120,9 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager,
keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
configurationController, systemClock, keyguardStateController,
- unlockedScreenOffAnimationController, HAL_CONTROLS_ILLUMINATION, hbmProvider,
- REQUEST_ID, reason, controllerCallback, onTouch, activityLaunchAnimator)
+ unlockedScreenOffAnimationController, udfpsDisplayMode, REQUEST_ID, reason,
+ controllerCallback, onTouch, activityLaunchAnimator
+ )
block()
}
@@ -246,7 +237,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
val didShow = controllerOverlay.show(udfpsController, overlayParams)
verify(windowManager).addView(eq(controllerOverlay.overlayView), any())
- verify(udfpsView).setHbmProvider(eq(hbmProvider))
+ verify(udfpsView).setUdfpsDisplayModeProvider(eq(udfpsDisplayMode))
verify(udfpsView).animationViewController = any()
verify(udfpsView).addView(any())
@@ -351,12 +342,12 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
}
@Test
- fun stopIlluminatingOnHide() = withReason(REASON_AUTH_BP) {
- whenever(udfpsView.isIlluminationRequested).thenReturn(true)
+ fun unconfigureDisplayOnHide() = withReason(REASON_AUTH_BP) {
+ whenever(udfpsView.isDisplayConfigured).thenReturn(true)
controllerOverlay.show(udfpsController, overlayParams)
controllerOverlay.hide()
- verify(udfpsView).stopIllumination()
+ verify(udfpsView).unconfigureDisplay()
}
@Test
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 d7a0a0ae974d..53e30fdb0a4a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -18,6 +18,7 @@ package com.android.systemui.biometrics;
import static android.view.MotionEvent.ACTION_DOWN;
import static android.view.MotionEvent.ACTION_MOVE;
+import static android.view.MotionEvent.ACTION_UP;
import static junit.framework.Assert.assertEquals;
@@ -125,7 +126,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Mock
private WindowManager mWindowManager;
@Mock
- private UdfpsHbmProvider mHbmProvider;
+ private UdfpsDisplayModeProvider mDisplayModeProvider;
@Mock
private StatusBarStateController mStatusBarStateController;
@Mock
@@ -193,7 +194,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
private IUdfpsOverlayController mOverlayController;
@Captor private ArgumentCaptor<UdfpsView.OnTouchListener> mTouchListenerCaptor;
@Captor private ArgumentCaptor<View.OnHoverListener> mHoverListenerCaptor;
- @Captor private ArgumentCaptor<Runnable> mOnIlluminatedRunnableCaptor;
+ @Captor private ArgumentCaptor<Runnable> mOnDisplayConfiguredCaptor;
@Captor private ArgumentCaptor<ScreenLifecycle.Observer> mScreenObserverCaptor;
private ScreenLifecycle.Observer mScreenObserver;
@@ -256,7 +257,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mVibrator,
mUdfpsHapticsSimulator,
mUdfpsShell,
- Optional.of(mHbmProvider),
+ Optional.of(mDisplayModeProvider),
mKeyguardStateController,
mDisplayManager,
mHandler,
@@ -512,7 +513,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
final float expectedMajor = touchMajor / scaleFactor;
// Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// Show the overlay.
@@ -590,7 +591,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void fingerDown() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
@@ -617,12 +618,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
anyFloat(), anyFloat());
verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
- // AND illumination begins
- verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
+ // AND display configuration begins
+ verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
verify(mLatencyTracker, never()).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
verify(mKeyguardUpdateMonitor).onUdfpsPointerDown(eq((int) TEST_REQUEST_ID));
- // AND onIlluminatedRunnable notifies FingerprintManager about onUiReady
- mOnIlluminatedRunnableCaptor.getValue().run();
+ // AND onDisplayConfigured notifies FingerprintManager about onUiReady
+ mOnDisplayConfiguredCaptor.getValue().run();
mBiometricsExecutor.runAllReady();
InOrder inOrder = inOrder(mAlternateTouchProvider, mLatencyTracker);
inOrder.verify(mAlternateTouchProvider).onUiReady();
@@ -640,10 +641,10 @@ public class UdfpsControllerTest extends SysuiTestCase {
// WHEN fingerprint is requested because of AOD interrupt
mUdfpsController.onAodInterrupt(0, 0, 2f, 3f);
mFgExecutor.runAllReady();
- // THEN illumination begins
- // AND onIlluminatedRunnable that notifies FingerprintManager is set
- verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
- mOnIlluminatedRunnableCaptor.getValue().run();
+ // THEN display configuration begins
+ // AND onDisplayConfigured notifies FingerprintManager about onUiReady
+ verify(mUdfpsView).configureDisplay(mOnDisplayConfiguredCaptor.capture());
+ mOnDisplayConfiguredCaptor.getValue().run();
mBiometricsExecutor.runAllReady();
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */);
@@ -661,11 +662,11 @@ public class UdfpsControllerTest extends SysuiTestCase {
mFgExecutor.runAllReady();
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- when(mUdfpsView.isIlluminationRequested()).thenReturn(true);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
// WHEN it is cancelled
mUdfpsController.onCancelUdfps();
- // THEN the illumination is hidden
- verify(mUdfpsView).stopIllumination();
+ // THEN the display is unconfigured
+ verify(mUdfpsView).unconfigureDisplay();
}
@Test
@@ -678,12 +679,64 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
mFgExecutor.runAllReady();
- when(mUdfpsView.isIlluminationRequested()).thenReturn(true);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
// WHEN it times out
mFgExecutor.advanceClockToNext();
mFgExecutor.runAllReady();
- // THEN the illumination is hidden
- verify(mUdfpsView).stopIllumination();
+ // THEN the display is unconfigured
+ verify(mUdfpsView).unconfigureDisplay();
+ }
+
+ @Test
+ public void aodInterruptCancelTimeoutActionWhenFingerUp() throws RemoteException {
+ when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
+
+ // GIVEN AOD interrupt
+ mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
+ BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
+ mScreenObserver.onScreenTurnedOn();
+ mFgExecutor.runAllReady();
+ mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
+ mFgExecutor.runAllReady();
+
+ // Configure UdfpsView to accept the ACTION_UP event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+
+ // WHEN ACTION_UP is received
+ verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
+ MotionEvent upEvent = MotionEvent.obtain(0, 0, ACTION_UP, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
+ mBiometricsExecutor.runAllReady();
+ upEvent.recycle();
+
+ // Configure UdfpsView to accept the ACTION_DOWN event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
+
+ // WHEN ACTION_DOWN is received
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricsExecutor.runAllReady();
+ downEvent.recycle();
+
+ // WHEN ACTION_MOVE is received
+ MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
+ mBiometricsExecutor.runAllReady();
+ moveEvent.recycle();
+ mFgExecutor.runAllReady();
+
+ // Configure UdfpsView to accept the finger up event
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(true);
+
+ // WHEN it times out
+ mFgExecutor.advanceClockToNext();
+ mFgExecutor.runAllReady();
+
+ // THEN the display should be unconfigured once. If the timeout action is not
+ // cancelled, the display would be unconfigured twice which would cause two
+ // FP attempts.
+ verify(mUdfpsView, times(1)).unconfigureDisplay();
}
@Test
@@ -698,8 +751,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(true);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- // THEN no illumination because screen is off
- verify(mUdfpsView, never()).startIllumination(any());
+ // THEN display doesn't get configured because it's off
+ verify(mUdfpsView, never()).configureDisplay(any());
}
@Test
@@ -715,14 +768,14 @@ public class UdfpsControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.isFingerprintDetectionRunning()).thenReturn(false);
mUdfpsController.onAodInterrupt(0, 0, 0f, 0f);
- // THEN no illumination because screen is off
- verify(mUdfpsView, never()).startIllumination(any());
+ // THEN display doesn't get configured because it's off
+ verify(mUdfpsView, never()).configureDisplay(any());
}
@Test
public void playHapticOnTouchUdfpsArea_a11yTouchExplorationEnabled() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing and a11y touch exploration enabled
@@ -757,7 +810,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
@Test
public void noHapticOnTouchUdfpsArea_a11yTouchExplorationDisabled() throws RemoteException {
// Configure UdfpsView to accept the ACTION_DOWN event
- when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
+ when(mUdfpsView.isDisplayConfigured()).thenReturn(false);
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
// GIVEN that the overlay is showing and a11y touch exploration NOT enabled
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
index 0327cfcf3450..b78c06391057 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsViewTest.kt
@@ -36,13 +36,12 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.nullable
import org.mockito.Mockito.verify
-import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
+import org.mockito.junit.MockitoJUnit
private const val SENSOR_X = 50
private const val SENSOR_Y = 250
@@ -57,7 +56,7 @@ class UdfpsViewTest : SysuiTestCase() {
var rule = MockitoJUnit.rule()
@Mock
- lateinit var hbmProvider: UdfpsHbmProvider
+ lateinit var hbmProvider: UdfpsDisplayModeProvider
@Mock
lateinit var animationViewController: UdfpsAnimationViewController<UdfpsAnimationView>
@@ -66,13 +65,11 @@ class UdfpsViewTest : SysuiTestCase() {
@Before
fun setup() {
context.setTheme(R.style.Theme_AppCompat)
- context.orCreateTestableResources.addOverride(
- com.android.internal.R.integer.config_udfps_illumination_transition_ms, 0)
view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
view.animationViewController = animationViewController
val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
view.overlayParams = UdfpsOverlayParams(sensorBounds, 1920, 1080, 1f, Surface.ROTATION_0)
- view.setHbmProvider(hbmProvider)
+ view.setUdfpsDisplayModeProvider(hbmProvider)
ViewUtils.attachView(view)
}
@@ -143,27 +140,27 @@ class UdfpsViewTest : SysuiTestCase() {
@Test
fun startAndStopIllumination() {
val onDone: Runnable = mock()
- view.startIllumination(onDone)
+ view.configureDisplay(onDone)
val illuminator = withArgCaptor<Runnable> {
- verify(hbmProvider).enableHbm(anyBoolean(), capture())
+ verify(hbmProvider).enable(capture())
}
- assertThat(view.isIlluminationRequested).isTrue()
- verify(animationViewController).onIlluminationStarting()
- verify(animationViewController, never()).onIlluminationStopped()
+ assertThat(view.isDisplayConfigured).isTrue()
+ verify(animationViewController).onDisplayConfiguring()
+ verify(animationViewController, never()).onDisplayUnconfigured()
verify(onDone, never()).run()
// fake illumination event
illuminator.run()
waitForLooper()
verify(onDone).run()
- verify(hbmProvider, never()).disableHbm(any())
+ verify(hbmProvider, never()).disable(any())
- view.stopIllumination()
- assertThat(view.isIlluminationRequested).isFalse()
- verify(animationViewController).onIlluminationStopped()
- verify(hbmProvider).disableHbm(nullable(Runnable::class.java))
+ view.unconfigureDisplay()
+ assertThat(view.isDisplayConfigured).isFalse()
+ verify(animationViewController).onDisplayUnconfigured()
+ verify(hbmProvider).disable(nullable(Runnable::class.java))
}
private fun waitForLooper() = TestableLooper.get(this).processAllMessages()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
index 8e00d10a7d39..faa5db4327a6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/DistanceClassifierTest.java
@@ -17,7 +17,7 @@
package com.android.systemui.classifier;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
-import static com.android.systemui.classifier.Classifier.QS_SWIPE;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.google.common.truth.Truth.assertThat;
@@ -106,9 +106,9 @@ public class DistanceClassifierTest extends ClassifierTest {
}
@Test
- public void testPass_QsSwipeAlwaysPasses() {
+ public void testPass_QsSwipeSideAlwaysPasses() {
mClassifier.onTouchEvent(appendDownEvent(1, 1));
- assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 1).isFalse())
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 1).isFalse())
.isFalse();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index acb5622c9790..3e9cf1e51b63 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -229,7 +229,10 @@ public class FalsingCollectorImplTest extends SysuiTestCase {
}
@Test
- public void testAvoidDozingNotPulsing() {
+ public void testGestureWhenDozing() {
+ // We check the FalsingManager for taps during the transition to AoD (dozing=true,
+ // pulsing=false), so the FalsingCollector needs to continue to analyze events that occur
+ // while the device is dozing.
MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
@@ -239,13 +242,13 @@ public class FalsingCollectorImplTest extends SysuiTestCase {
mFalsingCollector.onTouchEvent(down);
verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
- // Up event would normally flush the up event, but doesn't.
+ // Up event flushes
mFalsingCollector.onTouchEvent(up);
- verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+ verify(mFalsingDataProvider, times(2)).onMotionEvent(any(MotionEvent.class));
}
@Test
- public void testAvoidDozingButPulsing() {
+ public void testGestureWhenPulsing() {
MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
index 1d61e29cad35..d70d6fc6baa6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/TypeClassifierTest.java
@@ -22,7 +22,8 @@ import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DISMISS;
import static com.android.systemui.classifier.Classifier.NOTIFICATION_DRAG_DOWN;
import static com.android.systemui.classifier.Classifier.PULSE_EXPAND;
-import static com.android.systemui.classifier.Classifier.QS_SWIPE;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE_NESTED;
+import static com.android.systemui.classifier.Classifier.QS_SWIPE_SIDE;
import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
import static com.android.systemui.classifier.Classifier.UNLOCK;
@@ -323,44 +324,86 @@ public class TypeClassifierTest extends ClassifierTest {
}
@Test
- public void testPass_QsSwipe() {
+ public void testPass_QsSwipeSide() {
when(mDataProvider.isVertical()).thenReturn(false);
when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
when(mDataProvider.isRight()).thenReturn(false);
- assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(false);
- assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse();
when(mDataProvider.isUp()).thenReturn(false);
when(mDataProvider.isRight()).thenReturn(true);
- assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(true);
- assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isFalse();
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isFalse();
}
@Test
- public void testFalse_QsSwipe() {
+ public void testFalse_QsSwipeSide() {
when(mDataProvider.isVertical()).thenReturn(true);
when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
when(mDataProvider.isRight()).thenReturn(false);
- assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(false);
- assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue();
when(mDataProvider.isUp()).thenReturn(false);
when(mDataProvider.isRight()).thenReturn(true);
- assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue();
when(mDataProvider.isUp()).thenReturn(true);
when(mDataProvider.isRight()).thenReturn(true);
- assertThat(mClassifier.classifyGesture(QS_SWIPE, 0.5, 0).isFalse()).isTrue();
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_SIDE, 0.5, 0).isFalse()).isTrue();
+ }
+
+ @Test
+ public void testPass_QsNestedSwipe() {
+ when(mDataProvider.isVertical()).thenReturn(true);
+
+ when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isFalse();
+ }
+
+ @Test
+ public void testFalse_QsNestedSwipe() {
+ when(mDataProvider.isVertical()).thenReturn(false);
+
+ when(mDataProvider.isUp()).thenReturn(false); // up and right should cause no effect.
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(false);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(false);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue();
+
+ when(mDataProvider.isUp()).thenReturn(true);
+ when(mDataProvider.isRight()).thenReturn(true);
+ assertThat(mClassifier.classifyGesture(QS_SWIPE_NESTED, 0.5, 0).isFalse()).isTrue();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
new file mode 100644
index 000000000000..08fe7c486529
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/IntentCreatorTest.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.clipboardoverlay;
+
+import android.content.ClipData;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class IntentCreatorTest extends SysuiTestCase {
+ private static final int EXTERNAL_INTENT_FLAGS = Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION;
+
+ @Test
+ public void test_getTextEditorIntent() {
+ Intent intent = IntentCreator.getTextEditorIntent(getContext());
+ assertEquals(new ComponentName(getContext(), EditTextActivity.class),
+ intent.getComponent());
+ assertFlags(intent, Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ }
+
+ @Test
+ public void test_getRemoteCopyIntent() {
+ getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage,
+ "");
+
+ ClipData clipData = ClipData.newPlainText("Test", "Test Item");
+ Intent intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
+
+ assertEquals(null, intent.getComponent());
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+ assertEquals(clipData, intent.getClipData());
+
+ // Try again with a remote copy component
+ ComponentName fakeComponent = new ComponentName("com.android.remotecopy",
+ "com.android.remotecopy.RemoteCopyActivity");
+ getContext().getOrCreateTestableResources().addOverride(R.string.config_remoteCopyPackage,
+ fakeComponent.flattenToString());
+
+ intent = IntentCreator.getRemoteCopyIntent(clipData, getContext());
+ assertEquals(fakeComponent, intent.getComponent());
+ }
+
+ @Test
+ public void test_getImageEditIntent() {
+ getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
+ "");
+ Uri fakeUri = Uri.parse("content://foo");
+ Intent intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
+
+ assertEquals(Intent.ACTION_EDIT, intent.getAction());
+ assertEquals("image/*", intent.getType());
+ assertEquals(null, intent.getComponent());
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+
+ // try again with an editor component
+ ComponentName fakeComponent = new ComponentName("com.android.remotecopy",
+ "com.android.remotecopy.RemoteCopyActivity");
+ getContext().getOrCreateTestableResources().addOverride(R.string.config_screenshotEditor,
+ fakeComponent.flattenToString());
+ intent = IntentCreator.getImageEditIntent(fakeUri, getContext());
+ assertEquals(fakeComponent, intent.getComponent());
+ }
+
+ @Test
+ public void test_getShareIntent_plaintext() {
+ ClipData clipData = ClipData.newPlainText("Test", "Test Item");
+ Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+ Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+ assertEquals("Test Item", target.getStringExtra(Intent.EXTRA_TEXT));
+ assertEquals("text/plain", target.getType());
+ }
+
+ @Test
+ public void test_getShareIntent_html() {
+ ClipData clipData = ClipData.newHtmlText("Test", "Some HTML",
+ "<b>Some HTML</b>");
+ Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+ Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+ assertEquals("Some HTML", target.getStringExtra(Intent.EXTRA_TEXT));
+ assertEquals("text/plain", target.getType());
+ }
+
+ @Test
+ public void test_getShareIntent_image() {
+ Uri uri = Uri.parse("content://something");
+ ClipData clipData = new ClipData("Test", new String[]{"image/png"},
+ new ClipData.Item(uri));
+ Intent intent = IntentCreator.getShareIntent(clipData, getContext());
+
+ assertEquals(Intent.ACTION_CHOOSER, intent.getAction());
+ assertFlags(intent, EXTERNAL_INTENT_FLAGS);
+ Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
+ assertEquals(uri, target.getData());
+ assertEquals("image/png", target.getType());
+ }
+
+ // Assert that the given flags are set
+ private void assertFlags(Intent intent, int flags) {
+ assertTrue((intent.getFlags() & flags) == flags);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
index f93336134900..93a1868b72f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/decor/RoundedCornerResDelegateTest.kt
@@ -24,12 +24,11 @@ import androidx.annotation.DrawableRes
import androidx.test.filters.SmallTest
import com.android.internal.R as InternalR
import com.android.systemui.R as SystemUIR
-import com.android.systemui.tests.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.tests.R
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
-
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
@@ -102,14 +101,11 @@ class RoundedCornerResDelegateTest : SysuiTestCase() {
assertEquals(Size(3, 3), roundedCornerResDelegate.topRoundedSize)
assertEquals(Size(4, 4), roundedCornerResDelegate.bottomRoundedSize)
- setupResources(radius = 100,
- roundedTopDrawable = getTestsDrawable(R.drawable.rounded4px),
- roundedBottomDrawable = getTestsDrawable(R.drawable.rounded5px))
-
+ roundedCornerResDelegate.physicalPixelDisplaySizeRatio = 2f
roundedCornerResDelegate.updateDisplayUniqueId(null, 1)
- assertEquals(Size(4, 4), roundedCornerResDelegate.topRoundedSize)
- assertEquals(Size(5, 5), roundedCornerResDelegate.bottomRoundedSize)
+ assertEquals(Size(6, 6), roundedCornerResDelegate.topRoundedSize)
+ assertEquals(Size(8, 8), roundedCornerResDelegate.bottomRoundedSize)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
index d70467ddeebe..c5a7de410eb0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java
@@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest;
import com.android.keyguard.BouncerPanelExpansionCalculator;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
+import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
import com.android.systemui.statusbar.BlurUtils;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback;
@@ -88,6 +89,9 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
@Mock
ViewRootImpl mViewRoot;
+ @Mock
+ BouncerCallbackInteractor mBouncerCallbackInteractor;
+
DreamOverlayContainerViewController mController;
@Before
@@ -110,7 +114,8 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase {
mResources,
MAX_BURN_IN_OFFSET,
BURN_IN_PROTECTION_UPDATE_INTERVAL,
- MILLIS_UNTIL_FULL_JITTER);
+ MILLIS_UNTIL_FULL_JITTER,
+ mBouncerCallbackInteractor);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index 9d4275e65302..eec33ca2ff78 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -23,8 +23,10 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
+import android.os.RemoteException;
import android.service.dreams.DreamService;
import android.service.dreams.IDreamOverlay;
import android.service.dreams.IDreamOverlayCallback;
@@ -57,6 +59,8 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class DreamOverlayServiceTest extends SysuiTestCase {
+ private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package",
+ "lowlight");
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -129,7 +133,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
mDreamOverlayComponentFactory,
mStateController,
mKeyguardUpdateMonitor,
- mUiEventLogger);
+ mUiEventLogger,
+ LOW_LIGHT_COMPONENT);
}
@Test
@@ -204,6 +209,22 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
}
@Test
+ public void testLowLightSetByIntentExtra() throws RemoteException {
+ final Intent intent = new Intent();
+ intent.putExtra(DreamService.EXTRA_DREAM_COMPONENT, LOW_LIGHT_COMPONENT);
+
+ final IBinder proxy = mService.onBind(intent);
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+ assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ verify(mStateController).setLowLightActive(true);
+ }
+
+ @Test
public void testDestroy() {
mService.onDestroy();
mMainExecutor.runAllReady();
@@ -211,6 +232,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
verify(mKeyguardUpdateMonitor).removeCallback(any());
verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
verify(mStateController).setOverlayActive(false);
+ verify(mStateController).setLowLightActive(false);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
index 2adf2857a385..d1d32a13589a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStateControllerTest.java
@@ -218,4 +218,20 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
assertThat(stateController.getComplications(true).contains(complication))
.isTrue();
}
+
+ @Test
+ public void testNotifyLowLightChanged() {
+ final DreamOverlayStateController stateController =
+ new DreamOverlayStateController(mExecutor);
+
+ stateController.addCallback(mCallback);
+ mExecutor.runAllReady();
+ assertThat(stateController.isLowLightActive()).isFalse();
+
+ stateController.setLowLightActive(true);
+
+ mExecutor.runAllReady();
+ verify(mCallback, times(1)).onStateChanged();
+ assertThat(stateController.isLowLightActive()).isTrue();
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
index 7f6b79b48939..aa021781296a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayStatusBarViewControllerTest.java
@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -101,6 +102,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
DreamOverlayStatusBarItemsProvider.StatusBarItem mStatusBarItem;
@Mock
View mStatusBarItemView;
+ @Mock
+ DreamOverlayStateController mDreamOverlayStateController;
private final Executor mMainExecutor = Runnable::run;
@@ -126,7 +129,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
Optional.of(mDreamOverlayNotificationCountProvider),
mZenModeController,
mStatusBarWindowStateController,
- mDreamOverlayStatusBarItemsProvider);
+ mDreamOverlayStatusBarItemsProvider,
+ mDreamOverlayStateController);
}
@Test
@@ -137,6 +141,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
verify(mZenModeController).addCallback(any());
verify(mDreamOverlayNotificationCountProvider).addCallback(any());
verify(mDreamOverlayStatusBarItemsProvider).addCallback(any());
+ verify(mDreamOverlayStateController).addCallback(any());
}
@Test
@@ -266,7 +271,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
Optional.empty(),
mZenModeController,
mStatusBarWindowStateController,
- mDreamOverlayStatusBarItemsProvider);
+ mDreamOverlayStatusBarItemsProvider,
+ mDreamOverlayStateController);
controller.onViewAttached();
verify(mView, never()).showIcon(
eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
@@ -305,12 +311,13 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
verify(mZenModeController).removeCallback(any());
verify(mDreamOverlayNotificationCountProvider).removeCallback(any());
verify(mDreamOverlayStatusBarItemsProvider).removeCallback(any());
+ verify(mDreamOverlayStateController).removeCallback(any());
}
@Test
public void testOnViewDetachedRemovesViews() {
mController.onViewDetached();
- verify(mView).removeAllStatusBarItemViews();
+ verify(mView).removeAllExtraStatusBarItemViews();
}
@Test
@@ -458,6 +465,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
@Test
public void testStatusBarShownWhenSystemStatusBarHidden() {
mController.onViewAttached();
+ reset(mView);
final ArgumentCaptor<StatusBarWindowStateListener>
callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class);
@@ -471,6 +479,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
public void testUnattachedStatusBarVisibilityUnchangedWhenSystemStatusBarHidden() {
mController.onViewAttached();
mController.onViewDetached();
+ reset(mView);
final ArgumentCaptor<StatusBarWindowStateListener>
callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class);
@@ -493,4 +502,21 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
verify(mView).setExtraStatusBarItemViews(List.of(mStatusBarItemView));
}
+
+ @Test
+ public void testLowLightHidesStatusBar() {
+ when(mDreamOverlayStateController.isLowLightActive()).thenReturn(true);
+ mController.onViewAttached();
+
+ verify(mView).setVisibility(View.INVISIBLE);
+ reset(mView);
+
+ when(mDreamOverlayStateController.isLowLightActive()).thenReturn(false);
+ final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture =
+ ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
+ verify(mDreamOverlayStateController).addCallback(callbackCapture.capture());
+ callbackCapture.getValue().onStateChanged();
+
+ verify(mView).setVisibility(View.VISIBLE);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java
index bc944404efe6..522b5b5a8530 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamMediaEntryComplicationTest.java
@@ -16,17 +16,28 @@
package com.android.systemui.dreams.complication;
+import static com.android.systemui.flags.Flags.DREAM_MEDIA_TAP_TO_OPEN;
+
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import android.app.PendingIntent;
+import android.content.Intent;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import androidx.test.filters.SmallTest;
+import com.android.systemui.ActivityIntentHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.dream.MediaDreamComplication;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import org.junit.Before;
import org.junit.Test;
@@ -48,21 +59,52 @@ public class DreamMediaEntryComplicationTest extends SysuiTestCase {
@Mock
private MediaDreamComplication mMediaComplication;
+ @Mock
+ private MediaCarouselController mMediaCarouselController;
+
+ @Mock
+ private ActivityStarter mActivityStarter;
+
+ @Mock
+ private ActivityIntentHelper mActivityIntentHelper;
+
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+
+ @Mock
+ private NotificationLockscreenUserManager mLockscreenUserManager;
+
+ @Mock
+ private FeatureFlags mFeatureFlags;
+
+ @Mock
+ private PendingIntent mPendingIntent;
+
+ private final Intent mIntent = new Intent("android.test.TEST_ACTION");
+ private final Integer mCurrentUserId = 99;
+
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ when(mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN)).thenReturn(false);
}
/**
* Ensures clicking media entry chip adds/removes media complication.
*/
@Test
- public void testClick() {
+ public void testClickToOpenUMO() {
final DreamMediaEntryComplication.DreamMediaEntryViewController viewController =
new DreamMediaEntryComplication.DreamMediaEntryViewController(
mView,
mDreamOverlayStateController,
- mMediaComplication);
+ mMediaComplication,
+ mMediaCarouselController,
+ mActivityStarter,
+ mActivityIntentHelper,
+ mKeyguardStateController,
+ mLockscreenUserManager,
+ mFeatureFlags);
final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
ArgumentCaptor.forClass(View.OnClickListener.class);
@@ -85,10 +127,90 @@ public class DreamMediaEntryComplicationTest extends SysuiTestCase {
new DreamMediaEntryComplication.DreamMediaEntryViewController(
mView,
mDreamOverlayStateController,
- mMediaComplication);
+ mMediaComplication,
+ mMediaCarouselController,
+ mActivityStarter,
+ mActivityIntentHelper,
+ mKeyguardStateController,
+ mLockscreenUserManager,
+ mFeatureFlags);
viewController.onViewDetached();
verify(mView).setSelected(false);
verify(mDreamOverlayStateController).removeComplication(mMediaComplication);
}
+
+ /**
+ * Ensures clicking media entry chip opens media when flag is set.
+ */
+ @Test
+ public void testClickToOpenMediaOverLockscreen() {
+ when(mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN)).thenReturn(true);
+
+ when(mMediaCarouselController.getCurrentVisibleMediaContentIntent()).thenReturn(
+ mPendingIntent);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ when(mLockscreenUserManager.getCurrentUserId()).thenReturn(mCurrentUserId);
+
+ final DreamMediaEntryComplication.DreamMediaEntryViewController viewController =
+ new DreamMediaEntryComplication.DreamMediaEntryViewController(
+ mView,
+ mDreamOverlayStateController,
+ mMediaComplication,
+ mMediaCarouselController,
+ mActivityStarter,
+ mActivityIntentHelper,
+ mKeyguardStateController,
+ mLockscreenUserManager,
+ mFeatureFlags);
+ viewController.onViewAttached();
+
+ final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
+ ArgumentCaptor.forClass(View.OnClickListener.class);
+ verify(mView).setOnClickListener(clickListenerCaptor.capture());
+
+ when(mActivityIntentHelper.wouldShowOverLockscreen(mIntent, mCurrentUserId)).thenReturn(
+ true);
+
+ clickListenerCaptor.getValue().onClick(mView);
+ verify(mActivityStarter).startActivity(mIntent, true, null, true);
+ }
+
+ /**
+ * Ensures clicking media entry chip opens media when flag is set.
+ */
+ @Test
+ public void testClickToOpenMediaDismissingLockscreen() {
+ when(mFeatureFlags.isEnabled(DREAM_MEDIA_TAP_TO_OPEN)).thenReturn(true);
+
+ when(mMediaCarouselController.getCurrentVisibleMediaContentIntent()).thenReturn(
+ mPendingIntent);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mPendingIntent.getIntent()).thenReturn(mIntent);
+ when(mLockscreenUserManager.getCurrentUserId()).thenReturn(mCurrentUserId);
+
+ final DreamMediaEntryComplication.DreamMediaEntryViewController viewController =
+ new DreamMediaEntryComplication.DreamMediaEntryViewController(
+ mView,
+ mDreamOverlayStateController,
+ mMediaComplication,
+ mMediaCarouselController,
+ mActivityStarter,
+ mActivityIntentHelper,
+ mKeyguardStateController,
+ mLockscreenUserManager,
+ mFeatureFlags);
+ viewController.onViewAttached();
+
+ final ArgumentCaptor<View.OnClickListener> clickListenerCaptor =
+ ArgumentCaptor.forClass(View.OnClickListener.class);
+ verify(mView).setOnClickListener(clickListenerCaptor.capture());
+
+ when(mActivityIntentHelper.wouldShowOverLockscreen(mIntent, mCurrentUserId)).thenReturn(
+ false);
+
+ clickListenerCaptor.getValue().onClick(mView);
+ verify(mActivityStarter).postStartActivityDismissingKeyguard(mPendingIntent, null);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
index eecbee56d203..a78c902a1f30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardUnlockAnimationControllerTest.kt
@@ -27,11 +27,11 @@ import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor.forClass
import org.mockito.Mock
+import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@@ -60,7 +60,18 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
@Mock
private lateinit var launcherUnlockAnimationController: ILauncherUnlockAnimationController.Stub
- private lateinit var remoteAnimationTarget: RemoteAnimationTarget
+ private var surfaceControl1 = mock(SurfaceControl::class.java)
+ private var remoteTarget1 = RemoteAnimationTarget(
+ 0 /* taskId */, 0, surfaceControl1, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
+ mock(WindowConfiguration::class.java), false, surfaceControl1, Rect(),
+ mock(ActivityManager.RunningTaskInfo::class.java), false)
+
+ private var surfaceControl2 = mock(SurfaceControl::class.java)
+ private var remoteTarget2 = RemoteAnimationTarget(
+ 1 /* taskId */, 0, surfaceControl2, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
+ mock(WindowConfiguration::class.java), false, surfaceControl2, Rect(),
+ mock(ActivityManager.RunningTaskInfo::class.java), false)
+ private lateinit var remoteAnimationTargets: Array<RemoteAnimationTarget>
@Before
fun setUp() {
@@ -77,10 +88,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
// All of these fields are final, so we can't mock them, but are needed so that the surface
// appear amount setter doesn't short circuit.
- remoteAnimationTarget = RemoteAnimationTarget(
- 0, 0, null, false, Rect(), Rect(), 0, Point(), Rect(), Rect(),
- mock(WindowConfiguration::class.java), false, mock(SurfaceControl::class.java), Rect(),
- mock(ActivityManager.RunningTaskInfo::class.java), false)
+ remoteAnimationTargets = arrayOf(remoteTarget1)
// Set the surface applier to our mock so that we can verify the arguments passed to it.
// This applier does not have any side effects within the unlock animation controller, so
@@ -99,7 +107,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(true)
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
@@ -130,7 +138,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
`when`(biometricUnlockController.isWakeAndUnlock).thenReturn(false)
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
@@ -154,7 +162,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
`when`(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(false)
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
true /* requestedShowSurfaceBehindKeyguard */
)
@@ -176,7 +184,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
`when`(keyguardStateController.isFlingingToDismissKeyguard).thenReturn(true)
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
true /* requestedShowSurfaceBehindKeyguard */
)
@@ -196,7 +204,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
@Test
fun playCannedUnlockAnimation_ifDidNotRequestShowSurface() {
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
@@ -210,7 +218,7 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
`when`(notificationShadeWindowController.isLaunchingActivity).thenReturn(true)
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
true /* requestedShowSurfaceBehindKeyguard */
)
@@ -225,11 +233,46 @@ class KeyguardUnlockAnimationControllerTest : SysuiTestCase() {
keyguardUnlockAnimationController.willUnlockWithInWindowLauncherAnimations = true
keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
- remoteAnimationTarget,
+ remoteAnimationTargets,
0 /* startTime */,
false /* requestedShowSurfaceBehindKeyguard */
)
assertTrue(keyguardUnlockAnimationController.isPlayingCannedUnlockAnimation())
}
+
+ /**
+ * If we are not wake and unlocking, we expect the unlock animation to play normally.
+ */
+ @Test
+ fun surfaceAnimation_multipleTargets() {
+ keyguardUnlockAnimationController.notifyStartSurfaceBehindRemoteAnimation(
+ arrayOf(remoteTarget1, remoteTarget2),
+ 0 /* startTime */,
+ false /* requestedShowSurfaceBehindKeyguard */
+ )
+
+ // Set appear to 50%, we'll just verify that we're not applying the identity matrix which
+ // means an animation is in progress.
+ keyguardUnlockAnimationController.setSurfaceBehindAppearAmount(0.5f)
+
+ val captor = forClass(SyncRtSurfaceTransactionApplier.SurfaceParams::class.java)
+ verify(surfaceTransactionApplier, times(2)).scheduleApply(captor.capture())
+
+ val allParams = captor.allValues
+
+ val remainingTargets = mutableListOf(surfaceControl1, surfaceControl2)
+ allParams.forEach { params ->
+ assertTrue(!params.matrix.isIdentity)
+ remainingTargets.remove(params.surface)
+ }
+
+ // Make sure we called applyParams with each of the surface controls once. The order does
+ // not matter, so don't explicitly check for that.
+ assertTrue(remainingTargets.isEmpty())
+
+ // Since the animation is running, we should not have finished the remote animation.
+ verify(keyguardViewMediator, times(0)).onKeyguardExitRemoteAnimationFinished(
+ false /* cancelled */)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt
new file mode 100644
index 000000000000..3a61c57d086f
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import android.view.View
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.KeyguardBouncer
+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.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BouncerCallbackInteractorTest : SysuiTestCase() {
+ private val bouncerCallbackInteractor = BouncerCallbackInteractor()
+ @Mock private lateinit var bouncerExpansionCallback: KeyguardBouncer.BouncerExpansionCallback
+ @Mock private lateinit var keyguardResetCallback: KeyguardBouncer.KeyguardResetCallback
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ bouncerCallbackInteractor.addBouncerExpansionCallback(bouncerExpansionCallback)
+ bouncerCallbackInteractor.addKeyguardResetCallback(keyguardResetCallback)
+ }
+
+ @Test
+ fun testOnFullyShown() {
+ bouncerCallbackInteractor.dispatchFullyShown()
+ verify(bouncerExpansionCallback).onFullyShown()
+ }
+
+ @Test
+ fun testOnFullyHidden() {
+ bouncerCallbackInteractor.dispatchFullyHidden()
+ verify(bouncerExpansionCallback).onFullyHidden()
+ }
+
+ @Test
+ fun testOnExpansionChanged() {
+ bouncerCallbackInteractor.dispatchExpansionChanged(5f)
+ verify(bouncerExpansionCallback).onExpansionChanged(5f)
+ }
+
+ @Test
+ fun testOnVisibilityChanged() {
+ bouncerCallbackInteractor.dispatchVisibilityChanged(View.INVISIBLE)
+ verify(bouncerExpansionCallback).onVisibilityChanged(false)
+ }
+
+ @Test
+ fun testOnStartingToHide() {
+ bouncerCallbackInteractor.dispatchStartingToHide()
+ verify(bouncerExpansionCallback).onStartingToHide()
+ }
+
+ @Test
+ fun testOnStartingToShow() {
+ bouncerCallbackInteractor.dispatchStartingToShow()
+ verify(bouncerExpansionCallback).onStartingToShow()
+ }
+
+ @Test
+ fun testOnKeyguardReset() {
+ bouncerCallbackInteractor.dispatchReset()
+ verify(keyguardResetCallback).onKeyguardReset()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt
new file mode 100644
index 000000000000..e6c8dd87d982
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.domain.interactor
+
+import android.os.Looper
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper.RunWithLooper
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.DejankUtils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
+import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
+import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN
+import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.mockito.any
+import com.android.systemui.utils.os.FakeHandler
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWithLooper(setAsMainLooper = true)
+@RunWith(AndroidTestingRunner::class)
+class BouncerInteractorTest : SysuiTestCase() {
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private lateinit var repository: KeyguardBouncerRepository
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var bouncerView: BouncerView
+ @Mock private lateinit var keyguardStateController: KeyguardStateController
+ @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
+ @Mock private lateinit var bouncerCallbackInteractor: BouncerCallbackInteractor
+ @Mock private lateinit var falsingCollector: FalsingCollector
+ @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry
+ @Mock private lateinit var keyguardBypassController: KeyguardBypassController
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ private val mainHandler = FakeHandler(Looper.getMainLooper())
+ private lateinit var bouncerInteractor: BouncerInteractor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ DejankUtils.setImmediate(true)
+ bouncerInteractor =
+ BouncerInteractor(
+ repository,
+ bouncerView,
+ mainHandler,
+ keyguardStateController,
+ keyguardSecurityModel,
+ bouncerCallbackInteractor,
+ falsingCollector,
+ dismissCallbackRegistry,
+ keyguardBypassController,
+ keyguardUpdateMonitor,
+ )
+ `when`(repository.startingDisappearAnimation.value).thenReturn(null)
+ `when`(repository.show.value).thenReturn(null)
+ }
+
+ @Test
+ fun testShow_isScrimmed() {
+ bouncerInteractor.show(true)
+ verify(repository).setShowMessage(null)
+ verify(repository).setOnScreenTurnedOff(false)
+ verify(repository).setKeyguardAuthenticated(null)
+ verify(repository).setHide(false)
+ verify(repository).setStartingToHide(false)
+ verify(repository).setScrimmed(true)
+ verify(repository).setExpansion(EXPANSION_VISIBLE)
+ verify(repository).setShowingSoon(true)
+ verify(keyguardStateController).notifyBouncerShowing(true)
+ verify(bouncerCallbackInteractor).dispatchStartingToShow()
+ verify(repository).setVisible(true)
+ verify(repository).setShow(any(KeyguardBouncerModel::class.java))
+ verify(repository).setShowingSoon(false)
+ }
+
+ @Test
+ fun testShow_isNotScrimmed() {
+ verify(repository, never()).setExpansion(EXPANSION_VISIBLE)
+ }
+
+ @Test
+ fun testShow_keyguardIsDone() {
+ `when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true)
+ verify(keyguardStateController, never()).notifyBouncerShowing(true)
+ verify(bouncerCallbackInteractor, never()).dispatchStartingToShow()
+ }
+
+ @Test
+ fun testHide() {
+ bouncerInteractor.hide()
+ verify(falsingCollector).onBouncerHidden()
+ verify(keyguardStateController).notifyBouncerShowing(false)
+ verify(repository).setShowingSoon(false)
+ verify(repository).setOnDismissAction(null)
+ verify(repository).setVisible(false)
+ verify(repository).setHide(true)
+ verify(repository).setShow(null)
+ }
+
+ @Test
+ fun testExpansion() {
+ `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ bouncerInteractor.setExpansion(0.6f)
+ verify(repository).setExpansion(0.6f)
+ verify(bouncerCallbackInteractor).dispatchExpansionChanged(0.6f)
+ }
+
+ @Test
+ fun testExpansion_fullyShown() {
+ `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ `when`(repository.startingDisappearAnimation.value).thenReturn(null)
+ bouncerInteractor.setExpansion(EXPANSION_VISIBLE)
+ verify(falsingCollector).onBouncerShown()
+ verify(bouncerCallbackInteractor).dispatchFullyShown()
+ }
+
+ @Test
+ fun testExpansion_fullyHidden() {
+ `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ `when`(repository.startingDisappearAnimation.value).thenReturn(null)
+ bouncerInteractor.setExpansion(EXPANSION_HIDDEN)
+ verify(repository).setVisible(false)
+ verify(repository).setShow(null)
+ verify(falsingCollector).onBouncerHidden()
+ verify(bouncerCallbackInteractor).dispatchReset()
+ verify(bouncerCallbackInteractor).dispatchFullyHidden()
+ }
+
+ @Test
+ fun testExpansion_startingToHide() {
+ `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE)
+ bouncerInteractor.setExpansion(0.1f)
+ verify(repository).setStartingToHide(true)
+ verify(bouncerCallbackInteractor).dispatchStartingToHide()
+ }
+
+ @Test
+ fun testShowMessage() {
+ bouncerInteractor.showMessage("abc", null)
+ verify(repository).setShowMessage(BouncerShowMessageModel("abc", null))
+ }
+
+ @Test
+ fun testDismissAction() {
+ val onDismissAction = mock(ActivityStarter.OnDismissAction::class.java)
+ val cancelAction = mock(Runnable::class.java)
+ bouncerInteractor.setDismissAction(onDismissAction, cancelAction)
+ verify(repository)
+ .setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction))
+ }
+
+ @Test
+ fun testUpdateResources() {
+ bouncerInteractor.updateResources()
+ verify(repository).setResourceUpdateRequests(true)
+ }
+
+ @Test
+ fun testNotifyKeyguardAuthenticated() {
+ bouncerInteractor.notifyKeyguardAuthenticated(true)
+ verify(repository).setKeyguardAuthenticated(true)
+ }
+
+ @Test
+ fun testOnScreenTurnedOff() {
+ bouncerInteractor.onScreenTurnedOff()
+ verify(repository).setOnScreenTurnedOff(true)
+ }
+
+ @Test
+ fun testSetKeyguardPosition() {
+ bouncerInteractor.setKeyguardPosition(0f)
+ verify(repository).setKeyguardPosition(0f)
+ }
+
+ @Test
+ fun testNotifyKeyguardAuthenticatedHandled() {
+ bouncerInteractor.notifyKeyguardAuthenticatedHandled()
+ verify(repository).setKeyguardAuthenticated(null)
+ }
+
+ @Test
+ fun testNotifyUpdatedResources() {
+ bouncerInteractor.notifyUpdatedResources()
+ verify(repository).setResourceUpdateRequests(false)
+ }
+
+ @Test
+ fun testSetBackButtonEnabled() {
+ bouncerInteractor.setBackButtonEnabled(true)
+ verify(repository).setIsBackButtonEnabled(true)
+ }
+
+ @Test
+ fun testStartDisappearAnimation() {
+ val runnable = mock(Runnable::class.java)
+ bouncerInteractor.startDisappearAnimation(runnable)
+ verify(repository).setStartDisappearAnimation(any(Runnable::class.java))
+ }
+
+ @Test
+ fun testIsFullShowing() {
+ `when`(repository.isVisible.value).thenReturn(true)
+ `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE)
+ `when`(repository.startingDisappearAnimation.value).thenReturn(null)
+ assertThat(bouncerInteractor.isFullyShowing()).isTrue()
+ `when`(repository.isVisible.value).thenReturn(false)
+ assertThat(bouncerInteractor.isFullyShowing()).isFalse()
+ }
+
+ @Test
+ fun testIsScrimmed() {
+ `when`(repository.isScrimmed.value).thenReturn(true)
+ assertThat(bouncerInteractor.isScrimmed()).isTrue()
+ `when`(repository.isScrimmed.value).thenReturn(false)
+ assertThat(bouncerInteractor.isScrimmed()).isFalse()
+ }
+
+ @Test
+ fun testIsInTransit() {
+ `when`(repository.showingSoon.value).thenReturn(true)
+ assertThat(bouncerInteractor.isInTransit()).isTrue()
+ `when`(repository.showingSoon.value).thenReturn(false)
+ assertThat(bouncerInteractor.isInTransit()).isFalse()
+ `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ assertThat(bouncerInteractor.isInTransit()).isTrue()
+ }
+
+ @Test
+ fun testIsAnimatingAway() {
+ `when`(repository.startingDisappearAnimation.value).thenReturn(Runnable {})
+ assertThat(bouncerInteractor.isAnimatingAway()).isTrue()
+ `when`(repository.startingDisappearAnimation.value).thenReturn(null)
+ assertThat(bouncerInteractor.isAnimatingAway()).isFalse()
+ }
+
+ @Test
+ fun testWillDismissWithAction() {
+ `when`(repository.onDismissAction.value?.onDismissAction)
+ .thenReturn(mock(ActivityStarter.OnDismissAction::class.java))
+ assertThat(bouncerInteractor.willDismissWithAction()).isTrue()
+ `when`(repository.onDismissAction.value?.onDismissAction).thenReturn(null)
+ assertThat(bouncerInteractor.willDismissWithAction()).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
index 5a50a9f64487..5ad354247a04 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.media
+import android.app.PendingIntent
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
@@ -26,9 +27,9 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.statusbar.notification.collection.provider.OnReorderingAllowedListener
import com.android.systemui.statusbar.notification.collection.provider.VisualStabilityProvider
import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.util.animation.TransitionLayout
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
@@ -42,8 +43,8 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@@ -70,14 +71,11 @@ class MediaCarouselControllerTest : SysuiTestCase() {
@Mock lateinit var dumpManager: DumpManager
@Mock lateinit var logger: MediaUiEventLogger
@Mock lateinit var debugLogger: MediaCarouselControllerLogger
- @Mock lateinit var mediaViewHolder: MediaViewHolder
- @Mock lateinit var player: TransitionLayout
- @Mock lateinit var recommendationViewHolder: RecommendationViewHolder
- @Mock lateinit var recommendations: TransitionLayout
@Mock lateinit var mediaPlayer: MediaControlPanel
@Mock lateinit var mediaViewController: MediaViewController
@Mock lateinit var smartspaceMediaData: SmartspaceMediaData
@Captor lateinit var listener: ArgumentCaptor<MediaDataManager.Listener>
+ @Captor lateinit var visualStabilityCallback: ArgumentCaptor<OnReorderingAllowedListener>
private val clock = FakeSystemClock()
private lateinit var mediaCarouselController: MediaCarouselController
@@ -102,6 +100,8 @@ class MediaCarouselControllerTest : SysuiTestCase() {
debugLogger
)
verify(mediaDataManager).addListener(capture(listener))
+ verify(visualStabilityProvider)
+ .addPersistentReorderingAllowedListener(capture(visualStabilityCallback))
whenever(mediaControlPanelFactory.get()).thenReturn(mediaPlayer)
whenever(mediaPlayer.mediaViewController).thenReturn(mediaViewController)
whenever(mediaDataManager.smartspaceMediaData).thenReturn(smartspaceMediaData)
@@ -276,46 +276,6 @@ class MediaCarouselControllerTest : SysuiTestCase() {
verify(logger).logRecommendationRemoved(eq(packageName), eq(instanceId!!))
}
- @Test
- fun testSetSquishinessFractionForMedia_setPlayerBottom() {
- whenever(panel.mediaViewHolder).thenReturn(mediaViewHolder)
- whenever(mediaViewHolder.player).thenReturn(player)
- whenever(player.measuredHeight).thenReturn(100)
-
- val playingLocal = Triple("playing local",
- DATA.copy(active = true, isPlaying = true,
- playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = false),
- 4500L)
- MediaPlayerData.addMediaPlayer(playingLocal.first, playingLocal.second, panel, clock,
- false, debugLogger)
-
- mediaCarouselController.squishinessFraction = 0.0f
- verify(player).bottom = 50
- verifyNoMoreInteractions(recommendationViewHolder)
-
- mediaCarouselController.squishinessFraction = 0.5f
- verify(player).bottom = 75
- verifyNoMoreInteractions(recommendationViewHolder)
- }
-
- @Test
- fun testSetSquishinessFractionForRecommendation_setPlayerBottom() {
- whenever(panel.recommendationViewHolder).thenReturn(recommendationViewHolder)
- whenever(recommendationViewHolder.recommendations).thenReturn(recommendations)
- whenever(recommendations.measuredHeight).thenReturn(100)
-
- MediaPlayerData.addMediaRecommendation(SMARTSPACE_KEY, EMPTY_SMARTSPACE_MEDIA_DATA, panel,
- false, clock)
-
- mediaCarouselController.squishinessFraction = 0.0f
- verifyNoMoreInteractions(mediaViewHolder)
- verify(recommendationViewHolder.recommendations).bottom = 50
-
- mediaCarouselController.squishinessFraction = 0.5f
- verifyNoMoreInteractions(mediaViewHolder)
- verify(recommendationViewHolder.recommendations).bottom = 75
- }
-
fun testMediaLoaded_ScrollToActivePlayer() {
listener.value.onMediaDataLoaded("playing local",
null,
@@ -362,7 +322,7 @@ class MediaCarouselControllerTest : SysuiTestCase() {
playerIndex,
mediaCarouselController.mediaCarouselScrollHandler.visibleMediaIndex
)
- assertEquals( playerIndex, 0)
+ assertEquals(playerIndex, 0)
// Replaying the same media player one more time.
// And check that the card stays in its position.
@@ -374,4 +334,68 @@ class MediaCarouselControllerTest : SysuiTestCase() {
playerIndex = MediaPlayerData.getMediaPlayerIndex("playing local")
assertEquals(playerIndex, 0)
}
+
+ @Test
+ fun testRecommendationRemovedWhileNotVisible_updateHostVisibility() {
+ var result = false
+ mediaCarouselController.updateHostVisibility = { result = true }
+
+ whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(true)
+ listener.value.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY, false)
+
+ assertEquals(true, result)
+ }
+
+ @Test
+ fun testRecommendationRemovedWhileVisible_thenReorders_updateHostVisibility() {
+ var result = false
+ mediaCarouselController.updateHostVisibility = { result = true }
+
+ whenever(visualStabilityProvider.isReorderingAllowed).thenReturn(false)
+ listener.value.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY, false)
+ assertEquals(false, result)
+
+ visualStabilityCallback.value.onReorderingAllowed()
+ assertEquals(true, result)
+ }
+
+ @Test
+ fun testGetCurrentVisibleMediaContentIntent() {
+ val clickIntent1 = mock(PendingIntent::class.java)
+ val player1 = Triple("player1",
+ DATA.copy(clickIntent = clickIntent1),
+ 1000L)
+ clock.setCurrentTimeMillis(player1.third)
+ MediaPlayerData.addMediaPlayer(player1.first,
+ player1.second.copy(notificationKey = player1.first),
+ panel, clock, isSsReactivated = false)
+
+ assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent1)
+
+ val clickIntent2 = mock(PendingIntent::class.java)
+ val player2 = Triple("player2",
+ DATA.copy(clickIntent = clickIntent2),
+ 2000L)
+ clock.setCurrentTimeMillis(player2.third)
+ MediaPlayerData.addMediaPlayer(player2.first,
+ player2.second.copy(notificationKey = player2.first),
+ panel, clock, isSsReactivated = false)
+
+ // mediaCarouselScrollHandler.visibleMediaIndex is unchanged (= 0), and the new player is
+ // added to the front because it was active more recently.
+ assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2)
+
+ val clickIntent3 = mock(PendingIntent::class.java)
+ val player3 = Triple("player3",
+ DATA.copy(clickIntent = clickIntent3),
+ 500L)
+ clock.setCurrentTimeMillis(player3.third)
+ MediaPlayerData.addMediaPlayer(player3.first,
+ player3.second.copy(notificationKey = player3.first),
+ panel, clock, isSsReactivated = false)
+
+ // mediaCarouselScrollHandler.visibleMediaIndex is unchanged (= 0), and the new player is
+ // added to the end because it was active less recently.
+ assertEquals(mediaCarouselController.getCurrentVisibleMediaContentIntent(), clickIntent2)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index bef46953395b..7de5719c03ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -591,14 +591,20 @@ public class MediaControlPanelTest : SysuiTestCase() {
@Test
fun bindAlbumView_bitmapInLaterStates_setAfterExecutors() {
- val bmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
- val canvas = Canvas(bmp)
- canvas.drawColor(Color.RED)
- val albumArt = Icon.createWithBitmap(bmp)
+ val redBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+ val redCanvas = Canvas(redBmp)
+ redCanvas.drawColor(Color.RED)
+ val redArt = Icon.createWithBitmap(redBmp)
+
+ val greenBmp = Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888)
+ val greenCanvas = Canvas(greenBmp)
+ greenCanvas.drawColor(Color.GREEN)
+ val greenArt = Icon.createWithBitmap(greenBmp)
val state0 = mediaData.copy(artwork = null)
- val state1 = mediaData.copy(artwork = albumArt)
- val state2 = mediaData.copy(artwork = albumArt)
+ val state1 = mediaData.copy(artwork = redArt)
+ val state2 = mediaData.copy(artwork = redArt)
+ val state3 = mediaData.copy(artwork = greenArt)
player.attachPlayer(viewHolder)
// First binding sets (empty) drawable
@@ -627,6 +633,12 @@ public class MediaControlPanelTest : SysuiTestCase() {
bgExecutor.runAllReady()
mainExecutor.runAllReady()
verify(albumView, times(2)).setImageDrawable(any(Drawable::class.java))
+
+ // Fourth binding to new image runs transition due to color scheme change
+ player.bindPlayer(state3, PACKAGE)
+ bgExecutor.runAllReady()
+ mainExecutor.runAllReady()
+ verify(albumView, times(3)).setImageDrawable(any(Drawable::class.java))
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 22ecb4b93743..5f643363bd2e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -23,6 +23,8 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.WallpaperColors;
+import android.graphics.Bitmap;
import android.graphics.drawable.Icon;
import android.testing.AndroidTestingRunner;
import android.view.View;
@@ -102,6 +104,18 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
}
@Test
+ public void getItemId_validPosition_returnCorrespondingId() {
+ assertThat(mMediaOutputAdapter.getItemId(0)).isEqualTo(mMediaDevices.get(
+ 0).getId().hashCode());
+ }
+
+ @Test
+ public void getItemId_invalidPosition_returnPosition() {
+ int invalidPosition = mMediaDevices.size() + 1;
+ assertThat(mMediaOutputAdapter.getItemId(invalidPosition)).isEqualTo(invalidPosition);
+ }
+
+ @Test
public void onBindViewHolder_bindPairNew_verifyView() {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2);
@@ -155,6 +169,33 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
}
@Test
+ public void onBindViewHolder_bindConnectedDeviceWithMutingExpectedDeviceExist_verifyView() {
+ when(mMediaOutputController.hasMutingExpectedDevice()).thenReturn(true);
+ when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(false);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
+
+ @Test
+ public void onBindViewHolder_isMutingExpectedDevice_verifyView() {
+ when(mMediaDevice1.isMutingExpectedDevice()).thenReturn(true);
+ when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(false);
+ when(mMediaOutputController.isActiveRemoteDevice(mMediaDevice1)).thenReturn(false);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
+ }
+
+ @Test
public void onBindViewHolder_initSeekbar_setsVolume() {
when(mMediaDevice1.getMaxVolume()).thenReturn(TEST_MAX_VOLUME);
when(mMediaDevice1.getCurrentVolume()).thenReturn(TEST_CURRENT_VOLUME);
@@ -165,6 +206,20 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
}
@Test
+ public void onBindViewHolder_bindSelectableDevice_verifyView() {
+ List<MediaDevice> selectableDevices = new ArrayList<>();
+ selectableDevices.add(mMediaDevice2);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_2);
+ }
+
+ @Test
public void onBindViewHolder_bindNonActiveConnectedDevice_verifyView() {
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
@@ -223,6 +278,22 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
}
@Test
+ public void onBindViewHolder_bindGroupingDevice_verifyView() {
+ when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(false);
+ when(mMediaDevice1.getState()).thenReturn(
+ LocalMediaManager.MediaDeviceState.STATE_GROUPING);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
+ }
+
+ @Test
public void onBindViewHolder_inTransferring_bindNonTransferringDevice_verifyView() {
when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(true);
when(mMediaDevice2.getState()).thenReturn(
@@ -256,6 +327,31 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
}
@Test
+ public void onItemClick_clicksWithMutingExpectedDeviceExist_cancelsMuteAwaitConnection() {
+ when(mMediaOutputController.isAnyDeviceTransferring()).thenReturn(false);
+ when(mMediaOutputController.hasMutingExpectedDevice()).thenReturn(true);
+ when(mMediaOutputController.isCurrentConnectedDeviceRemote()).thenReturn(false);
+ when(mMediaDevice1.isMutingExpectedDevice()).thenReturn(false);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
+
+ mViewHolder.mContainerLayout.performClick();
+
+ verify(mMediaOutputController).cancelMuteAwaitConnection();
+ }
+
+ @Test
+ public void onItemClick_clicksSelectableDevice_triggerGrouping() {
+ List<MediaDevice> selectableDevices = new ArrayList<>();
+ selectableDevices.add(mMediaDevice2);
+ when(mMediaOutputController.getSelectableMediaDevice()).thenReturn(selectableDevices);
+ mMediaOutputAdapter.onBindViewHolder(mViewHolder, 1);
+
+ mViewHolder.mContainerLayout.performClick();
+
+ verify(mMediaOutputController).addDeviceToPlayMedia(mMediaDevice2);
+ }
+
+ @Test
public void onItemClick_onGroupActionTriggered_verifySeekbarDisabled() {
when(mMediaOutputController.getSelectedMediaDevice()).thenReturn(mMediaDevices);
List<MediaDevice> selectableDevices = new ArrayList<>();
@@ -280,4 +376,14 @@ public class MediaOutputAdapterTest extends SysuiTestCase {
assertThat(mViewHolder.mSeekBar.isEnabled()).isTrue();
}
+
+ @Test
+ public void updateColorScheme_triggerController() {
+ WallpaperColors wallpaperColors = WallpaperColors.fromBitmap(
+ Bitmap.createBitmap(10, 10, Bitmap.Config.ARGB_8888));
+
+ mMediaOutputAdapter.updateColorScheme(wallpaperColors, true);
+
+ verify(mMediaOutputController).setCurrentColorScheme(wallpaperColors, true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
index 6dcf8024eca8..cb31fde26bf2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputControllerTest.java
@@ -35,6 +35,7 @@ import android.app.KeyguardManager;
import android.app.Notification;
import android.content.Context;
import android.graphics.drawable.Icon;
+import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.MediaDescription;
import android.media.MediaMetadata;
@@ -279,6 +280,203 @@ public class MediaOutputControllerTest extends SysuiTestCase {
}
@Test
+ public void onDeviceListUpdate_isRefreshing_updatesNeedRefreshToTrue() {
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+ mMediaOutputController.mIsRefreshing = true;
+
+ mMediaOutputController.onDeviceListUpdate(mMediaDevices);
+
+ assertThat(mMediaOutputController.mNeedRefresh).isTrue();
+ }
+
+ @Test
+ public void cancelMuteAwaitConnection_cancelsWithMediaManager() {
+ when(mAudioManager.getMutingExpectedDevice()).thenReturn(mock(AudioDeviceAttributes.class));
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+
+ mMediaOutputController.cancelMuteAwaitConnection();
+
+ verify(mAudioManager).cancelMuteAwaitConnection(any());
+ }
+
+ @Test
+ public void cancelMuteAwaitConnection_audioManagerIsNull_noAction() {
+ when(mAudioManager.getMutingExpectedDevice()).thenReturn(null);
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+ mMediaOutputController.cancelMuteAwaitConnection();
+
+ verify(mAudioManager, never()).cancelMuteAwaitConnection(any());
+ }
+
+ @Test
+ public void getAppSourceName_packageNameIsNull_returnsNull() {
+ MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
+ "",
+ mMediaSessionManager, mLocalBluetoothManager, mStarter,
+ mNotifCollection, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mKeyguardManager);
+ testMediaOutputController.start(mCb);
+ reset(mCb);
+
+ testMediaOutputController.getAppSourceName();
+
+ assertThat(testMediaOutputController.getAppSourceName()).isNull();
+ }
+
+ @Test
+ public void isActiveItem_deviceNotConnected_returnsFalse() {
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2);
+
+ assertThat(mMediaOutputController.isActiveItem(mMediaDevice1)).isFalse();
+ }
+
+ @Test
+ public void getNotificationSmallIcon_packageNameIsNull_returnsNull() {
+ MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
+ "",
+ mMediaSessionManager, mLocalBluetoothManager, mStarter,
+ mNotifCollection, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mKeyguardManager);
+ testMediaOutputController.start(mCb);
+ reset(mCb);
+
+ testMediaOutputController.getAppSourceName();
+
+ assertThat(testMediaOutputController.getNotificationSmallIcon()).isNull();
+ }
+
+ @Test
+ public void refreshDataSetIfNeeded_needRefreshIsTrue_setsToFalse() {
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+ mMediaOutputController.mNeedRefresh = true;
+
+ mMediaOutputController.refreshDataSetIfNeeded();
+
+ assertThat(mMediaOutputController.mNeedRefresh).isFalse();
+ }
+
+ @Test
+ public void isCurrentConnectedDeviceRemote_containsFeatures_returnsTrue() {
+ when(mMediaDevice1.getFeatures()).thenReturn(
+ ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_PLAYBACK));
+ when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1);
+
+ assertThat(mMediaOutputController.isCurrentConnectedDeviceRemote()).isTrue();
+ }
+
+ @Test
+ public void addDeviceToPlayMedia_triggersFromLocalMediaManager() {
+ MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
+ null,
+ mMediaSessionManager, mLocalBluetoothManager, mStarter,
+ mNotifCollection, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mKeyguardManager);
+
+ LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager);
+ testMediaOutputController.mLocalMediaManager = testLocalMediaManager;
+
+ testMediaOutputController.addDeviceToPlayMedia(mMediaDevice2);
+
+ verify(testLocalMediaManager).addDeviceToPlayMedia(mMediaDevice2);
+ }
+
+ @Test
+ public void removeDeviceFromPlayMedia_triggersFromLocalMediaManager() {
+ MediaOutputController testMediaOutputController = new MediaOutputController(mSpyContext,
+ null,
+ mMediaSessionManager, mLocalBluetoothManager, mStarter,
+ mNotifCollection, mDialogLaunchAnimator,
+ Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager,
+ mKeyguardManager);
+
+ LocalMediaManager testLocalMediaManager = spy(testMediaOutputController.mLocalMediaManager);
+ testMediaOutputController.mLocalMediaManager = testLocalMediaManager;
+
+ testMediaOutputController.removeDeviceFromPlayMedia(mMediaDevice2);
+
+ verify(testLocalMediaManager).removeDeviceFromPlayMedia(mMediaDevice2);
+ }
+
+ @Test
+ public void getDeselectableMediaDevice_triggersFromLocalMediaManager() {
+ mMediaOutputController.getDeselectableMediaDevice();
+
+ verify(mLocalMediaManager).getDeselectableMediaDevice();
+ }
+
+ @Test
+ public void adjustSessionVolume_adjustWithoutId_triggersFromLocalMediaManager() {
+ int testVolume = 10;
+ mMediaOutputController.adjustSessionVolume(testVolume);
+
+ verify(mLocalMediaManager).adjustSessionVolume(testVolume);
+ }
+
+ @Test
+ public void getSessionVolumeMax_triggersFromLocalMediaManager() {
+ mMediaOutputController.getSessionVolumeMax();
+
+ verify(mLocalMediaManager).getSessionVolumeMax();
+ }
+
+ @Test
+ public void getSessionVolume_triggersFromLocalMediaManager() {
+ mMediaOutputController.getSessionVolume();
+
+ verify(mLocalMediaManager).getSessionVolume();
+ }
+
+ @Test
+ public void getSessionName_triggersFromLocalMediaManager() {
+ mMediaOutputController.getSessionName();
+
+ verify(mLocalMediaManager).getSessionName();
+ }
+
+ @Test
+ public void releaseSession_triggersFromLocalMediaManager() {
+ mMediaOutputController.releaseSession();
+
+ verify(mLocalMediaManager).releaseSession();
+ }
+
+ @Test
+ public void isAnyDeviceTransferring_noDevicesStateIsConnecting_returnsFalse() {
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+
+ mMediaOutputController.onDeviceListUpdate(mMediaDevices);
+
+ assertThat(mMediaOutputController.isAnyDeviceTransferring()).isFalse();
+ }
+
+ @Test
+ public void isAnyDeviceTransferring_deviceStateIsConnecting_returnsTrue() {
+ when(mMediaDevice1.getState()).thenReturn(
+ LocalMediaManager.MediaDeviceState.STATE_CONNECTING);
+ mMediaOutputController.start(mCb);
+ reset(mCb);
+
+ mMediaOutputController.onDeviceListUpdate(mMediaDevices);
+
+ assertThat(mMediaOutputController.isAnyDeviceTransferring()).isTrue();
+ }
+
+ @Test
+ public void isPlaying_stateIsNull() {
+ when(mMediaController.getPlaybackState()).thenReturn(null);
+
+ assertThat(mMediaOutputController.isPlaying()).isFalse();
+ }
+
+ @Test
public void onSelectedDeviceStateChanged_verifyCallback() {
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice2);
mMediaOutputController.start(mCb);
@@ -535,6 +733,44 @@ public class MediaOutputControllerTest extends SysuiTestCase {
}
@Test
+ public void getNotificationSmallIcon_withoutSmallIcon_returnsNull() {
+ final List<NotificationEntry> entryList = new ArrayList<>();
+ final NotificationEntry entry = mock(NotificationEntry.class);
+ final StatusBarNotification sbn = mock(StatusBarNotification.class);
+ final Notification notification = mock(Notification.class);
+ entryList.add(entry);
+
+ when(mNotifCollection.getAllNotifs()).thenReturn(entryList);
+ when(entry.getSbn()).thenReturn(sbn);
+ when(sbn.getNotification()).thenReturn(notification);
+ when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(notification.isMediaNotification()).thenReturn(true);
+ when(notification.getSmallIcon()).thenReturn(null);
+
+ assertThat(mMediaOutputController.getNotificationSmallIcon()).isNull();
+ }
+
+ @Test
+ public void getNotificationSmallIcon_withPackageNameAndMediaSession_returnsIconCompat() {
+ final List<NotificationEntry> entryList = new ArrayList<>();
+ final NotificationEntry entry = mock(NotificationEntry.class);
+ final StatusBarNotification sbn = mock(StatusBarNotification.class);
+ final Notification notification = mock(Notification.class);
+ final Icon icon = mock(Icon.class);
+ entryList.add(entry);
+
+ when(mNotifCollection.getAllNotifs()).thenReturn(entryList);
+ when(entry.getSbn()).thenReturn(sbn);
+ when(sbn.getNotification()).thenReturn(notification);
+ when(sbn.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(notification.isMediaNotification()).thenReturn(true);
+ when(notification.getSmallIcon()).thenReturn(icon);
+
+ assertThat(mMediaOutputController.getNotificationSmallIcon()).isInstanceOf(
+ IconCompat.class);
+ }
+
+ @Test
public void isVolumeControlEnabled_isCastWithVolumeFixed_returnsFalse() {
when(mMediaDevice1.getDeviceType()).thenReturn(
MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
index 9557513775f8..bae3569cdc93 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputDialogTest.java
@@ -25,7 +25,10 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.KeyguardManager;
+import android.graphics.Bitmap;
import android.media.AudioManager;
+import android.media.MediaDescription;
+import android.media.MediaMetadata;
import android.media.MediaRoute2Info;
import android.media.session.MediaController;
import android.media.session.MediaSessionManager;
@@ -43,6 +46,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.MediaDevice;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastSender;
@@ -82,6 +86,8 @@ public class MediaOutputDialogTest extends SysuiTestCase {
private final CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
private final UiEventLogger mUiEventLogger = mock(UiEventLogger.class);
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
+ private final MediaMetadata mMediaMetadata = mock(MediaMetadata.class);
+ private final MediaDescription mMediaDescription = mock(MediaDescription.class);
private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
NearbyMediaDevicesManager.class);
private final AudioManager mAudioManager = mock(AudioManager.class);
@@ -100,6 +106,8 @@ public class MediaOutputDialogTest extends SysuiTestCase {
when(mMediaController.getPlaybackState()).thenReturn(mPlaybackState);
when(mPlaybackState.getState()).thenReturn(PlaybackState.STATE_NONE);
when(mMediaController.getPackageName()).thenReturn(TEST_PACKAGE);
+ when(mMediaController.getMetadata()).thenReturn(mMediaMetadata);
+ when(mMediaMetadata.getDescription()).thenReturn(mMediaDescription);
mMediaControllers.add(mMediaController);
when(mMediaSessionManager.getActiveSessions(any())).thenReturn(mMediaControllers);
@@ -207,6 +215,80 @@ public class MediaOutputDialogTest extends SysuiTestCase {
}
@Test
+ public void getHeaderIcon_getFromMediaControllerMetaData() {
+ int testWidth = 10;
+ int testHeight = 20;
+ when(mMediaDescription.getIconBitmap())
+ .thenReturn(Bitmap.createBitmap(testWidth, testHeight, Bitmap.Config.ARGB_8888));
+
+ assertThat(mMediaOutputDialog.getHeaderIcon().getBitmap().getHeight()).isEqualTo(
+ testHeight);
+ assertThat(mMediaOutputDialog.getHeaderIcon().getBitmap().getWidth()).isEqualTo(testWidth);
+ }
+
+ @Test
+ public void getHeaderText_getFromMediaControllerMetaData() {
+ String testTitle = "test title";
+ when(mMediaDescription.getTitle())
+ .thenReturn(testTitle);
+ assertThat(mMediaOutputDialog.getHeaderText().toString()).isEqualTo(testTitle);
+ }
+
+ @Test
+ public void getHeaderSubtitle_getFromMediaControllerMetaData() {
+ String testSubtitle = "test title";
+ when(mMediaDescription.getSubtitle())
+ .thenReturn(testSubtitle);
+
+ assertThat(mMediaOutputDialog.getHeaderSubtitle().toString()).isEqualTo(testSubtitle);
+ }
+
+ @Test
+ public void getStopButtonText_notSupportsBroadcast_returnsDefaultText() {
+ String stopText = mContext.getText(R.string.keyboard_key_media_stop).toString();
+ MediaOutputController mockMediaOutputController = mock(MediaOutputController.class);
+ when(mockMediaOutputController.isBroadcastSupported()).thenReturn(false);
+
+ MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false, mBroadcastSender,
+ mockMediaOutputController, mUiEventLogger);
+ testDialog.show();
+
+ assertThat(testDialog.getStopButtonText().toString()).isEqualTo(stopText);
+ }
+
+ @Test
+ public void getStopButtonText_supportsBroadcast_returnsBroadcastText() {
+ String stopText = mContext.getText(R.string.media_output_broadcast).toString();
+ MediaDevice mMediaDevice = mock(MediaDevice.class);
+ MediaOutputController mockMediaOutputController = mock(MediaOutputController.class);
+ when(mockMediaOutputController.isBroadcastSupported()).thenReturn(true);
+ when(mockMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(mMediaDevice);
+ when(mockMediaOutputController.isBluetoothLeDevice(any())).thenReturn(true);
+ when(mockMediaOutputController.isPlaying()).thenReturn(true);
+ when(mockMediaOutputController.isBluetoothLeBroadcastEnabled()).thenReturn(false);
+ MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false, mBroadcastSender,
+ mockMediaOutputController, mUiEventLogger);
+ testDialog.show();
+
+ assertThat(testDialog.getStopButtonText().toString()).isEqualTo(stopText);
+ }
+
+ @Test
+ public void onStopButtonClick_notPlaying_releaseSession() {
+ MediaOutputController mockMediaOutputController = mock(MediaOutputController.class);
+ when(mockMediaOutputController.isBroadcastSupported()).thenReturn(false);
+ when(mockMediaOutputController.getCurrentConnectedMediaDevice()).thenReturn(null);
+ when(mockMediaOutputController.isPlaying()).thenReturn(false);
+ MediaOutputDialog testDialog = new MediaOutputDialog(mContext, false, mBroadcastSender,
+ mockMediaOutputController, mUiEventLogger);
+ testDialog.show();
+
+ testDialog.onStopButtonClick();
+
+ verify(mockMediaOutputController).releaseSession();
+ }
+
+ @Test
// Check the visibility metric logging by creating a new MediaOutput dialog,
// and verify if the calling times increases.
public void onCreate_ShouldLogVisibility() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
index 0bfc0344b521..2f52950a9ee4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dream/MediaDreamSentinelTest.java
@@ -16,7 +16,7 @@
package com.android.systemui.media.dream;
-import static com.android.systemui.flags.Flags.MEDIA_DREAM_COMPLICATION;
+import static com.android.systemui.flags.Flags.DREAM_MEDIA_COMPLICATION;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
@@ -68,7 +68,7 @@ public class MediaDreamSentinelTest extends SysuiTestCase {
public void setup() {
MockitoAnnotations.initMocks(this);
- when(mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)).thenReturn(true);
+ when(mFeatureFlags.isEnabled(DREAM_MEDIA_COMPLICATION)).thenReturn(true);
}
@Test
@@ -137,7 +137,7 @@ public class MediaDreamSentinelTest extends SysuiTestCase {
@Test
public void testOnMediaDataLoaded_mediaComplicationDisabled_doesNotAddComplication() {
- when(mFeatureFlags.isEnabled(MEDIA_DREAM_COMPLICATION)).thenReturn(false);
+ when(mFeatureFlags.isEnabled(DREAM_MEDIA_COMPLICATION)).thenReturn(false);
final MediaDreamSentinel sentinel = new MediaDreamSentinel(mContext, mMediaDataManager,
mDreamOverlayStateController, mMediaEntryComplication, mFeatureFlags);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
index d95e5c48256c..1078cdaa57c4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerTest.kt
@@ -23,11 +23,11 @@ import com.android.systemui.log.LogBuffer
import com.android.systemui.log.LogBufferFactory
import com.android.systemui.log.LogcatEchoTracker
import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.mock
-import java.io.PrintWriter
-import java.io.StringWriter
@SmallTest
class MediaTttLoggerTest : SysuiTestCase() {
@@ -43,32 +43,46 @@ class MediaTttLoggerTest : SysuiTestCase() {
}
@Test
- fun logStateChange_bufferHasDeviceTypeTagAndStateNameAndId() {
+ fun logStateChange_bufferHasDeviceTypeTagAndParamInfo() {
val stateName = "test state name"
val id = "test id"
+ val packageName = "this.is.a.package"
- logger.logStateChange(stateName, id)
-
- val stringWriter = StringWriter()
- buffer.dump(PrintWriter(stringWriter), tailLength = 0)
- val actualString = stringWriter.toString()
+ logger.logStateChange(stateName, id, packageName)
+ val actualString = getStringFromBuffer()
assertThat(actualString).contains(DEVICE_TYPE_TAG)
assertThat(actualString).contains(stateName)
assertThat(actualString).contains(id)
+ assertThat(actualString).contains(packageName)
}
@Test
- fun logChipRemoval_bufferHasDeviceTypeAndReason() {
- val reason = "test reason"
- logger.logChipRemoval(reason)
+ fun logPackageNotFound_bufferHasPackageName() {
+ val packageName = "this.is.a.package"
+ logger.logPackageNotFound(packageName)
+
+ val actualString = getStringFromBuffer()
+ assertThat(actualString).contains(packageName)
+ }
+
+ @Test
+ fun logRemovalBypass_bufferHasReasons() {
+ val removalReason = "fakeRemovalReason"
+ val bypassReason = "fakeBypassReason"
+
+ logger.logRemovalBypass(removalReason, bypassReason)
+
+ val actualString = getStringFromBuffer()
+ assertThat(actualString).contains(removalReason)
+ assertThat(actualString).contains(bypassReason)
+ }
+
+ private fun getStringFromBuffer(): String {
val stringWriter = StringWriter()
buffer.dump(PrintWriter(stringWriter), tailLength = 0)
- val actualString = stringWriter.toString()
-
- assertThat(actualString).contains(DEVICE_TYPE_TAG)
- assertThat(actualString).contains(reason)
+ return stringWriter.toString()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
new file mode 100644
index 000000000000..37f6434ea069
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/common/MediaTttUtilsTest.kt
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.media.taptotransfer.common
+
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+import android.widget.FrameLayout
+import androidx.test.filters.SmallTest
+import com.android.internal.widget.CachingIconView
+import com.android.systemui.R
+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.Test
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class MediaTttUtilsTest : SysuiTestCase() {
+
+ private lateinit var appIconFromPackageName: Drawable
+ @Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var applicationInfo: ApplicationInfo
+ @Mock private lateinit var logger: MediaTttLogger
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ // Set up our package manager to give valid information for [PACKAGE_NAME] only
+ appIconFromPackageName = context.getDrawable(R.drawable.ic_cake)!!
+ whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(appIconFromPackageName)
+ whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
+ whenever(
+ packageManager.getApplicationInfo(any(), any<PackageManager.ApplicationInfoFlags>())
+ )
+ .thenThrow(PackageManager.NameNotFoundException())
+ whenever(
+ packageManager.getApplicationInfo(
+ Mockito.eq(PACKAGE_NAME),
+ any<PackageManager.ApplicationInfoFlags>()
+ )
+ )
+ .thenReturn(applicationInfo)
+ context.setMockPackageManager(packageManager)
+ }
+
+ @Test
+ fun getIconInfoFromPackageName_nullPackageName_returnsDefault() {
+ val iconInfo =
+ MediaTttUtils.getIconInfoFromPackageName(context, appPackageName = null, logger)
+
+ assertThat(iconInfo.isAppIcon).isFalse()
+ assertThat(iconInfo.contentDescription)
+ .isEqualTo(context.getString(R.string.media_output_dialog_unknown_launch_app_name))
+ }
+
+ @Test
+ fun getIconInfoFromPackageName_invalidPackageName_returnsDefault() {
+ val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, "fakePackageName", logger)
+
+ assertThat(iconInfo.isAppIcon).isFalse()
+ assertThat(iconInfo.contentDescription)
+ .isEqualTo(context.getString(R.string.media_output_dialog_unknown_launch_app_name))
+ }
+
+ @Test
+ fun getIconInfoFromPackageName_validPackageName_returnsAppInfo() {
+ val iconInfo = MediaTttUtils.getIconInfoFromPackageName(context, PACKAGE_NAME, logger)
+
+ assertThat(iconInfo.isAppIcon).isTrue()
+ assertThat(iconInfo.drawable).isEqualTo(appIconFromPackageName)
+ assertThat(iconInfo.contentDescription).isEqualTo(APP_NAME)
+ }
+
+ @Test
+ fun setIcon_viewHasIconAndContentDescription() {
+ val view = CachingIconView(context)
+ val icon = context.getDrawable(R.drawable.ic_celebration)!!
+ val contentDescription = "Happy birthday!"
+
+ MediaTttUtils.setIcon(view, icon, contentDescription)
+
+ assertThat(view.drawable).isEqualTo(icon)
+ assertThat(view.contentDescription).isEqualTo(contentDescription)
+ }
+
+ @Test
+ fun setIcon_iconSizeNull_viewSizeDoesNotChange() {
+ val view = CachingIconView(context)
+ val size = 456
+ view.layoutParams = FrameLayout.LayoutParams(size, size)
+
+ MediaTttUtils.setIcon(view, context.getDrawable(R.drawable.ic_cake)!!, "desc")
+
+ assertThat(view.layoutParams.width).isEqualTo(size)
+ assertThat(view.layoutParams.height).isEqualTo(size)
+ }
+
+ @Test
+ fun setIcon_iconSizeProvided_viewSizeUpdates() {
+ val view = CachingIconView(context)
+ val size = 456
+ view.layoutParams = FrameLayout.LayoutParams(size, size)
+
+ val newSize = 40
+ MediaTttUtils.setIcon(
+ view,
+ context.getDrawable(R.drawable.ic_cake)!!,
+ "desc",
+ iconSize = newSize
+ )
+
+ assertThat(view.layoutParams.width).isEqualTo(newSize)
+ assertThat(view.layoutParams.height).isEqualTo(newSize)
+ }
+}
+
+private const val PACKAGE_NAME = "com.android.systemui"
+private const val APP_NAME = "Fake App Name"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index e7b4593b0ebb..d41ad48676b4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -173,37 +173,72 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
null
)
- verify(logger).logStateChange(any(), any())
+ verify(logger).logStateChange(any(), any(), any())
}
@Test
- fun setIcon_isAppIcon_usesAppIconSize() {
- controllerReceiver.displayView(getChipReceiverInfo())
+ fun updateView_noOverrides_usesInfoFromAppIcon() {
+ controllerReceiver.displayView(
+ ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appNameOverride = null)
+ )
+
+ val view = getChipView()
+ assertThat(view.getAppIconView().drawable).isEqualTo(fakeAppIconDrawable)
+ assertThat(view.getAppIconView().contentDescription).isEqualTo(APP_NAME)
+ }
+
+ @Test
+ fun updateView_appIconOverride_usesOverride() {
+ val drawableOverride = context.getDrawable(R.drawable.ic_celebration)!!
+
+ controllerReceiver.displayView(
+ ChipReceiverInfo(routeInfo, drawableOverride, appNameOverride = null)
+ )
+
+ val view = getChipView()
+ assertThat(view.getAppIconView().drawable).isEqualTo(drawableOverride)
+ }
+
+ @Test
+ fun updateView_appNameOverride_usesOverride() {
+ val appNameOverride = "Sweet New App"
+
+ controllerReceiver.displayView(
+ ChipReceiverInfo(routeInfo, appIconDrawableOverride = null, appNameOverride)
+ )
+
+ val view = getChipView()
+ assertThat(view.getAppIconView().contentDescription).isEqualTo(appNameOverride)
+ }
+
+ @Test
+ fun updateView_isAppIcon_usesAppIconSize() {
+ controllerReceiver.displayView(getChipReceiverInfo(packageName = PACKAGE_NAME))
val chipView = getChipView()
- controllerReceiver.setIcon(chipView, PACKAGE_NAME)
chipView.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
- val expectedSize = controllerReceiver.getIconSize(isAppIcon = true)
+ val expectedSize =
+ context.resources.getDimensionPixelSize(R.dimen.media_ttt_icon_size_receiver)
assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(expectedSize)
assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(expectedSize)
}
@Test
- fun setIcon_notAppIcon_usesGenericIconSize() {
- controllerReceiver.displayView(getChipReceiverInfo())
+ fun updateView_notAppIcon_usesGenericIconSize() {
+ controllerReceiver.displayView(getChipReceiverInfo(packageName = null))
val chipView = getChipView()
- controllerReceiver.setIcon(chipView, appPackageName = null)
chipView.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
)
- val expectedSize = controllerReceiver.getIconSize(isAppIcon = false)
+ val expectedSize =
+ context.resources.getDimensionPixelSize(R.dimen.media_ttt_generic_icon_size_receiver)
assertThat(chipView.getAppIconView().measuredWidth).isEqualTo(expectedSize)
assertThat(chipView.getAppIconView().measuredHeight).isEqualTo(expectedSize)
}
@@ -226,8 +261,13 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
return viewCaptor.value as ViewGroup
}
- private fun getChipReceiverInfo(): ChipReceiverInfo =
- ChipReceiverInfo(routeInfo, null, null)
+ private fun getChipReceiverInfo(packageName: String?): ChipReceiverInfo {
+ val routeInfo = MediaRoute2Info.Builder("id", "Test route name")
+ .addFeature("feature")
+ .setClientPackageName(packageName)
+ .build()
+ return ChipReceiverInfo(routeInfo, null, null)
+ }
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 52b6eed9a14d..ff0faf98fe1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -299,7 +299,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
null
)
- verify(logger).logStateChange(any(), any())
+ verify(logger).logStateChange(any(), any(), any())
}
@Test
@@ -587,15 +587,29 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
+ verify(logger).logRemovalBypass(any(), any())
}
@Test
- fun transferToReceiverTriggeredThenFarFromReceiver_eventuallyTimesOut() {
- val state = transferToReceiverTriggered()
- controllerSender.displayView(state)
- fakeClock.advanceTime(1000L)
- controllerSender.removeView("fakeRemovalReason")
+ fun transferToReceiverTriggeredThenFarFromReceiver_viewStillDisplayed() {
+ controllerSender.displayView(transferToReceiverTriggered())
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+ fakeExecutor.runAllReady()
+ verify(windowManager, never()).removeView(any())
+ verify(logger).logRemovalBypass(any(), any())
+ }
+
+ @Test
+ fun transferToReceiverTriggeredThenRemoveView_eventuallyTimesOut() {
+ controllerSender.displayView(transferToReceiverTriggered())
+
+ controllerSender.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
@@ -610,20 +624,106 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
+ verify(logger).logRemovalBypass(any(), any())
}
@Test
- fun transferToThisDeviceTriggeredThenFarFromReceiver_eventuallyTimesOut() {
- val state = transferToThisDeviceTriggered()
- controllerSender.displayView(state)
- fakeClock.advanceTime(1000L)
+ fun transferToThisDeviceTriggeredThenRemoveView_eventuallyTimesOut() {
+ controllerSender.displayView(transferToThisDeviceTriggered())
+
controllerSender.removeView("fakeRemovalReason")
+ fakeClock.advanceTime(TIMEOUT + 1L)
+
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun transferToThisDeviceTriggeredThenFarFromReceiver_viewStillDisplayed() {
+ controllerSender.displayView(transferToThisDeviceTriggered())
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+ fakeExecutor.runAllReady()
+
+ verify(windowManager, never()).removeView(any())
+ verify(logger).logRemovalBypass(any(), any())
+ }
+
+ @Test
+ fun transferToReceiverSucceededThenRemoveView_viewStillDisplayed() {
+ controllerSender.displayView(transferToReceiverSucceeded())
+
+ controllerSender.removeView("fakeRemovalReason")
+ fakeExecutor.runAllReady()
+ verify(windowManager, never()).removeView(any())
+ verify(logger).logRemovalBypass(any(), any())
+ }
+
+ @Test
+ fun transferToReceiverSucceededThenRemoveView_eventuallyTimesOut() {
+ controllerSender.displayView(transferToReceiverSucceeded())
+
+ controllerSender.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
+ @Test
+ fun transferToReceiverSucceededThenFarFromReceiver_viewStillDisplayed() {
+ controllerSender.displayView(transferToReceiverSucceeded())
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+ fakeExecutor.runAllReady()
+
+ verify(windowManager, never()).removeView(any())
+ verify(logger).logRemovalBypass(any(), any())
+ }
+
+ @Test
+ fun transferToThisDeviceSucceededThenRemoveView_viewStillDisplayed() {
+ controllerSender.displayView(transferToThisDeviceSucceeded())
+
+ controllerSender.removeView("fakeRemovalReason")
+ fakeExecutor.runAllReady()
+
+ verify(windowManager, never()).removeView(any())
+ verify(logger).logRemovalBypass(any(), any())
+ }
+
+ @Test
+ fun transferToThisDeviceSucceededThenRemoveView_eventuallyTimesOut() {
+ controllerSender.displayView(transferToThisDeviceSucceeded())
+
+ controllerSender.removeView("fakeRemovalReason")
+ fakeClock.advanceTime(TIMEOUT + 1L)
+
+ verify(windowManager).removeView(any())
+ }
+
+ @Test
+ fun transferToThisDeviceSucceededThenFarFromReceiver_viewStillDisplayed() {
+ controllerSender.displayView(transferToThisDeviceSucceeded())
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+ fakeExecutor.runAllReady()
+
+ verify(windowManager, never()).removeView(any())
+ verify(logger).logRemovalBypass(any(), any())
+ }
+
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
private fun ViewGroup.getChipText(): String =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
new file mode 100644
index 000000000000..00b1f3268bae
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/MediaProjectionAppSelectorControllerTest.kt
@@ -0,0 +1,120 @@
+package com.android.systemui.mediaprojection.appselector
+
+import android.content.ComponentName
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.mediaprojection.appselector.data.RecentTask
+import com.android.systemui.mediaprojection.appselector.data.RecentTaskListProvider
+import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class MediaProjectionAppSelectorControllerTest : SysuiTestCase() {
+
+ private val taskListProvider = TestRecentTaskListProvider()
+ private val scope = CoroutineScope(Dispatchers.Unconfined)
+ private val appSelectorComponentName = ComponentName("com.test", "AppSelector")
+
+ private val view: MediaProjectionAppSelectorView = mock()
+
+ private val controller = MediaProjectionAppSelectorController(
+ taskListProvider,
+ scope,
+ appSelectorComponentName
+ )
+
+ @Test
+ fun initNoRecentTasks_bindsEmptyList() {
+ taskListProvider.tasks = emptyList()
+
+ controller.init(view)
+
+ verify(view).bind(emptyList())
+ }
+
+ @Test
+ fun initOneRecentTask_bindsList() {
+ taskListProvider.tasks = listOf(
+ createRecentTask(taskId = 1)
+ )
+
+ controller.init(view)
+
+ verify(view).bind(
+ listOf(
+ createRecentTask(taskId = 1)
+ )
+ )
+ }
+
+ @Test
+ fun initMultipleRecentTasksWithoutAppSelectorTask_bindsListInTheSameOrder() {
+ val tasks = listOf(
+ createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2),
+ createRecentTask(taskId = 3),
+ )
+ taskListProvider.tasks = tasks
+
+ controller.init(view)
+
+ verify(view).bind(
+ listOf(
+ createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2),
+ createRecentTask(taskId = 3),
+ )
+ )
+ }
+
+ @Test
+ fun initRecentTasksWithAppSelectorTasks_bindsAppSelectorTasksAtTheEnd() {
+ val tasks = listOf(
+ createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
+ createRecentTask(taskId = 3),
+ createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
+ createRecentTask(taskId = 5),
+ )
+ taskListProvider.tasks = tasks
+
+ controller.init(view)
+
+ verify(view).bind(
+ listOf(
+ createRecentTask(taskId = 1),
+ createRecentTask(taskId = 3),
+ createRecentTask(taskId = 5),
+ createRecentTask(taskId = 2, topActivityComponent = appSelectorComponentName),
+ createRecentTask(taskId = 4, topActivityComponent = appSelectorComponentName),
+ )
+ )
+ }
+
+ private fun createRecentTask(
+ taskId: Int,
+ topActivityComponent: ComponentName? = null
+ ): RecentTask {
+ return RecentTask(
+ taskId = taskId,
+ topActivityComponent = topActivityComponent,
+ baseIntentComponent = ComponentName("com", "Test"),
+ userId = 0,
+ colorBackground = 0
+ )
+ }
+
+ private class TestRecentTaskListProvider : RecentTaskListProvider {
+
+ var tasks: List<RecentTask> = emptyList()
+
+ override suspend fun loadRecentTasks(): List<RecentTask> = tasks
+
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
new file mode 100644
index 000000000000..939af16d81cd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/appselector/data/ShellRecentTaskListProviderTest.kt
@@ -0,0 +1,112 @@
+package com.android.systemui.mediaprojection.appselector.data
+
+import android.app.ActivityManager.RecentTaskInfo
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.wm.shell.recents.RecentTasks
+import com.android.wm.shell.util.GroupedRecentTaskInfo
+import com.google.common.truth.Truth.assertThat
+import java.util.*
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import org.junit.Test
+import org.junit.runner.RunWith
+import java.util.function.Consumer
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class ShellRecentTaskListProviderTest : SysuiTestCase() {
+
+ private val dispatcher = Dispatchers.Unconfined
+ private val recentTasks: RecentTasks = mock()
+ private val recentTaskListProvider =
+ ShellRecentTaskListProvider(dispatcher, Runnable::run, Optional.of(recentTasks))
+
+ @Test
+ fun loadRecentTasks_oneTask_returnsTheSameTask() {
+ givenRecentTasks(createSingleTask(taskId = 1))
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result).containsExactly(createRecentTask(taskId = 1))
+ }
+
+ @Test
+ fun loadRecentTasks_multipleTasks_returnsTheSameTasks() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ createSingleTask(taskId = 2),
+ createSingleTask(taskId = 3),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result)
+ .containsExactly(
+ createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2),
+ createRecentTask(taskId = 3),
+ )
+ }
+
+ @Test
+ fun loadRecentTasks_groupedTask_returnsUngroupedTasks() {
+ givenRecentTasks(createTaskPair(taskId1 = 1, taskId2 = 2))
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result)
+ .containsExactly(createRecentTask(taskId = 1), createRecentTask(taskId = 2))
+ }
+
+ @Test
+ fun loadRecentTasks_mixedSingleAndGroupedTask_returnsUngroupedTasks() {
+ givenRecentTasks(
+ createSingleTask(taskId = 1),
+ createTaskPair(taskId1 = 2, taskId2 = 3),
+ createSingleTask(taskId = 4),
+ createTaskPair(taskId1 = 5, taskId2 = 6),
+ )
+
+ val result = runBlocking { recentTaskListProvider.loadRecentTasks() }
+
+ assertThat(result)
+ .containsExactly(
+ createRecentTask(taskId = 1),
+ createRecentTask(taskId = 2),
+ createRecentTask(taskId = 3),
+ createRecentTask(taskId = 4),
+ createRecentTask(taskId = 5),
+ createRecentTask(taskId = 6),
+ )
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ private fun givenRecentTasks(vararg tasks: GroupedRecentTaskInfo) {
+ whenever(recentTasks.getRecentTasks(any(), any(), any(), any(), any())).thenAnswer {
+ val consumer = it.arguments.last() as Consumer<List<GroupedRecentTaskInfo>>
+ consumer.accept(tasks.toList())
+ }
+ }
+
+ private fun createRecentTask(taskId: Int): RecentTask =
+ RecentTask(
+ taskId = taskId,
+ userId = 0,
+ topActivityComponent = null,
+ baseIntentComponent = null,
+ colorBackground = null
+ )
+
+ private fun createSingleTask(taskId: Int): GroupedRecentTaskInfo =
+ GroupedRecentTaskInfo.forSingleTask(createTaskInfo(taskId))
+
+ private fun createTaskPair(taskId1: Int, taskId2: Int): GroupedRecentTaskInfo =
+ GroupedRecentTaskInfo.forSplitTasks(createTaskInfo(taskId1), createTaskInfo(taskId2), null)
+
+ private fun createTaskInfo(taskId: Int) = RecentTaskInfo().apply { this.taskId = taskId }
+}
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 ecc845720717..3cad2a005882 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -44,7 +44,6 @@ import com.android.internal.logging.testing.UiEventLoggerFake;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.media.MediaCarouselController;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
@@ -87,7 +86,6 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
@Mock
private QSLogger mQSLogger;
private DumpManager mDumpManager = new DumpManager();
- private MediaCarouselController mMediaCarouselController;
@Mock
QSTileImpl mQSTile;
@Mock
@@ -110,9 +108,9 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host,
QSCustomizerController qsCustomizerController, MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
- DumpManager dumpManager, MediaCarouselController mediaCarouselController) {
+ DumpManager dumpManager) {
super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger,
- qsLogger, dumpManager, mediaCarouselController);
+ qsLogger, dumpManager);
}
@Override
@@ -146,7 +144,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
mController.init();
reset(mQSTileRevealController);
@@ -158,7 +156,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel,
mQSTileHost, mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController) {
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager) {
@Override
protected QSTileRevealController createTileRevealController() {
return mQSTileRevealController;
@@ -226,7 +224,9 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
+ " Tile records:\n"
+ " " + mockTileString + "\n"
+ " " + mockTileViewString + "\n"
- + " media bounds: null\n";
+ + " media bounds: null\n"
+ + " horizontal layout: false\n"
+ + " last orientation: 0\n";
assertEquals(expected, w.getBuffer().toString());
}
@@ -251,7 +251,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
when(mQSPanel.getDumpableTag()).thenReturn("QSPanelLandscape");
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
mController.init();
assertThat(mController.shouldUseHorizontalLayout()).isTrue();
@@ -260,7 +260,7 @@ public class QSPanelControllerBaseTest extends SysuiTestCase {
when(mQSPanel.getDumpableTag()).thenReturn("QSPanelPortrait");
mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost,
mQSCustomizerController, mMediaHost,
- mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mMediaCarouselController);
+ mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager);
mController.init();
assertThat(mController.shouldUseHorizontalLayout()).isFalse();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
index 98d499a70fa7..5eb9a9862340 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.kt
@@ -6,7 +6,6 @@ import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.UiEventLogger
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.MediaCarouselController
import com.android.systemui.media.MediaHost
import com.android.systemui.media.MediaHostState
import com.android.systemui.plugins.FalsingManager
@@ -41,7 +40,6 @@ class QSPanelControllerTest : SysuiTestCase() {
@Mock private lateinit var qsCustomizerController: QSCustomizerController
@Mock private lateinit var qsTileRevealControllerFactory: QSTileRevealController.Factory
@Mock private lateinit var dumpManager: DumpManager
- @Mock private lateinit var mediaCarouselController: MediaCarouselController
@Mock private lateinit var metricsLogger: MetricsLogger
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var qsLogger: QSLogger
@@ -78,7 +76,6 @@ class QSPanelControllerTest : SysuiTestCase() {
mediaHost,
qsTileRevealControllerFactory,
dumpManager,
- mediaCarouselController,
metricsLogger,
uiEventLogger,
qsLogger,
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 39f27d43ed12..6af8e4904a1e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -23,7 +23,6 @@ import com.android.internal.logging.MetricsLogger
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
-import com.android.systemui.media.MediaCarouselController
import com.android.systemui.media.MediaHost
import com.android.systemui.media.MediaHostState
import com.android.systemui.plugins.qs.QSTile
@@ -60,7 +59,6 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
@Mock private lateinit var tileLayout: TileLayout
@Mock private lateinit var tileView: QSTileView
@Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
- @Mock private lateinit var mediaCarouselController: MediaCarouselController
private val uiEventLogger = UiEventLoggerFake()
private val dumpManager = DumpManager()
@@ -90,8 +88,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
metricsLogger,
uiEventLogger,
qsLogger,
- dumpManager,
- mediaCarouselController)
+ dumpManager)
controller.init()
}
@@ -123,8 +120,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
@Test
fun mediaExpansion_afterConfigChange_inLandscape_collapsedInLandscapeTrue_updatesToCollapsed() {
- // times(2) because both controller and base controller are registering their listeners
- verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture())
+ verify(quickQSPanel).addOnConfigurationChangedListener(captor.capture())
// verify that media starts in the expanded state by default
verify(mediaHost).expansion = MediaHostState.EXPANDED
@@ -139,8 +135,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
@Test
fun mediaExpansion_afterConfigChange_landscape_collapsedInLandscapeFalse_remainsExpanded() {
- // times(2) because both controller and base controller are registering their listeners
- verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture())
+ verify(quickQSPanel).addOnConfigurationChangedListener(captor.capture())
reset(mediaHost)
usingCollapsedLandscapeMedia = false
@@ -160,8 +155,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
metricsLogger: MetricsLogger,
uiEventLogger: UiEventLoggerFake,
qsLogger: QSLogger,
- dumpManager: DumpManager,
- mediaCarouselController: MediaCarouselController
+ dumpManager: DumpManager
) :
QuickQSPanelController(
view,
@@ -173,8 +167,7 @@ class QuickQSPanelControllerTest : SysuiTestCase() {
metricsLogger,
uiEventLogger,
qsLogger,
- dumpManager,
- mediaCarouselController) {
+ dumpManager) {
private var rotation = RotationUtils.ROTATION_NONE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
index bf682a8d4e0a..3fd25019e2a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
@@ -33,12 +33,15 @@ import androidx.test.filters.SmallTest;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.settings.FakeSettings;
import com.android.systemui.util.settings.SecureSettings;
@@ -54,6 +57,8 @@ import org.mockito.MockitoAnnotations;
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
public class ColorInversionTileTest extends SysuiTestCase {
+ private static final Integer COLOR_INVERSION_DISABLED = 0;
+ private static final Integer COLOR_INVERSION_ENABLED = 1;
@Mock
private QSTileHost mHost;
@@ -113,4 +118,24 @@ public class ColorInversionTileTest extends SysuiTestCase {
assertThat(IntentCaptor.getValue().getAction()).isEqualTo(
Settings.ACTION_COLOR_INVERSION_SETTINGS);
}
+
+ @Test
+ public void testIcon_whenColorInversionDisabled_isOffState() {
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, COLOR_INVERSION_DISABLED);
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_invert_colors_icon_off));
+ }
+
+ @Test
+ public void testIcon_whenColorInversionEnabled_isOnState() {
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, COLOR_INVERSION_ENABLED);
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_invert_colors_icon_on));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
new file mode 100644
index 000000000000..ce62f2d1cf36
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DataSaverTileTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.DataSaverController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class DataSaverTileTest : SysuiTestCase() {
+
+ @Mock private lateinit var mHost: QSHost
+ @Mock private lateinit var mMetricsLogger: MetricsLogger
+ @Mock private lateinit var mStatusBarStateController: StatusBarStateController
+ @Mock private lateinit var mActivityStarter: ActivityStarter
+ @Mock private lateinit var mQsLogger: QSLogger
+ private val falsingManager = FalsingManagerFake()
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var dataSaverController: DataSaverController
+ @Mock private lateinit var dialogLaunchAnimator: DialogLaunchAnimator
+
+ private val uiEventLogger = UiEventLoggerFake()
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: DataSaverTile
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+
+ Mockito.`when`(mHost.context).thenReturn(mContext)
+ Mockito.`when`(mHost.uiEventLogger).thenReturn(uiEventLogger)
+
+ tile =
+ DataSaverTile(
+ mHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ falsingManager,
+ mMetricsLogger,
+ statusBarStateController,
+ activityStarter,
+ mQsLogger,
+ dataSaverController,
+ dialogLaunchAnimator
+ )
+ }
+
+ @Test
+ fun testIcon_whenDataSaverEnabled_isOnState() {
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, true)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_data_saver_icon_on))
+ }
+
+ @Test
+ fun testIcon_whenDataSaverDisabled_isOffState() {
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, false)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_data_saver_icon_off))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
new file mode 100644
index 000000000000..d0f851bded75
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/FlashlightTileTest.kt
@@ -0,0 +1,108 @@
+package com.android.systemui.qs.tiles
+
+import android.content.Context
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.FlashlightController
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class FlashlightTileTest : SysuiTestCase() {
+
+ @Mock private lateinit var mockContext: Context
+
+ @Mock private lateinit var qsLogger: QSLogger
+
+ @Mock private lateinit var qsHost: QSTileHost
+
+ @Mock private lateinit var metricsLogger: MetricsLogger
+
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ @Mock private lateinit var flashlightController: FlashlightController
+
+ private val uiEventLogger = UiEventLoggerFake()
+ private val falsingManager = FalsingManagerFake()
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: FlashlightTile
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+
+ Mockito.`when`(qsHost.context).thenReturn(mockContext)
+ Mockito.`when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+ tile =
+ FlashlightTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ flashlightController
+ )
+ }
+
+ @Test
+ fun testIcon_whenFlashlightEnabled_isOnState() {
+ Mockito.`when`(flashlightController.isAvailable).thenReturn(true)
+ Mockito.`when`(flashlightController.isEnabled).thenReturn(true)
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ Truth.assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_on))
+ }
+
+ @Test
+ fun testIcon_whenFlashlightDisabled_isOffState() {
+ Mockito.`when`(flashlightController.isAvailable).thenReturn(true)
+ Mockito.`when`(flashlightController.isEnabled).thenReturn(false)
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ Truth.assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_off))
+ }
+
+ @Test
+ fun testIcon_whenFlashlightUnavailable_isOffState() {
+ Mockito.`when`(flashlightController.isAvailable).thenReturn(false)
+ val state = QSTile.BooleanState()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ Truth.assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_flashlight_icon_off))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
index b86713d0890b..451e9119f297 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HotspotTileTest.java
@@ -39,6 +39,7 @@ import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.HotspotController;
@@ -122,4 +123,40 @@ public class HotspotTileTest extends SysuiTestCase {
.isEqualTo(mContext.getString(R.string.wifitrackerlib_admin_restricted_network));
mockitoSession.finishMocking();
}
+
+ @Test
+ public void testIcon_whenDisabled_isOffState() {
+ QSTile.BooleanState state = new QSTile.BooleanState();
+ when(mHotspotController.isHotspotTransient()).thenReturn(false);
+ when(mHotspotController.isHotspotEnabled()).thenReturn(false);
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_hotspot_icon_off));
+ }
+
+ @Test
+ public void testIcon_whenTransient_isSearchState() {
+ QSTile.BooleanState state = new QSTile.BooleanState();
+ when(mHotspotController.isHotspotTransient()).thenReturn(true);
+ when(mHotspotController.isHotspotEnabled()).thenReturn(true);
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_hotspot_icon_search));
+ }
+
+ @Test
+ public void testIcon_whenEnabled_isOnState() {
+ QSTile.BooleanState state = new QSTile.BooleanState();
+ when(mHotspotController.isHotspotTransient()).thenReturn(false);
+ when(mHotspotController.isHotspotEnabled()).thenReturn(true);
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_hotspot_icon_on));
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
new file mode 100644
index 000000000000..188c3a3d9e42
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NightDisplayTileTest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.hardware.display.ColorDisplayManager
+import android.hardware.display.NightDisplayListener
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.dagger.NightDisplayListenerModule
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.LocationController
+import com.google.common.truth.Truth
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class NightDisplayTileTest : SysuiTestCase() {
+ @Mock
+ private lateinit var mHost: QSHost
+
+ @Mock
+ private lateinit var mMetricsLogger: MetricsLogger
+
+ @Mock
+ private lateinit var mStatusBarStateController: StatusBarStateController
+
+ @Mock
+ private lateinit var mActivityStarter: ActivityStarter
+
+ @Mock
+ private lateinit var mQsLogger: QSLogger
+
+ @Mock
+ private lateinit var mLocationController: LocationController
+
+ @Mock
+ private lateinit var mColorDisplayManager: ColorDisplayManager
+
+ @Mock
+ private lateinit var mNightDisplayListenerBuilder: NightDisplayListenerModule.Builder
+
+ @Mock
+ private lateinit var mNightDisplayListener: NightDisplayListener
+
+ private lateinit var mTestableLooper: TestableLooper
+ private lateinit var mTile: NightDisplayTile
+
+ private val mUiEventLogger: UiEventLogger = UiEventLoggerFake()
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ mTestableLooper = TestableLooper.get(this)
+ whenever(mHost.context).thenReturn(mContext)
+ whenever(mHost.uiEventLogger).thenReturn(mUiEventLogger)
+ whenever(mHost.userContext).thenReturn(mContext)
+ whenever(mNightDisplayListenerBuilder.setUser(anyInt())).thenReturn(
+ mNightDisplayListenerBuilder
+ )
+ whenever(mNightDisplayListenerBuilder.build()).thenReturn(mNightDisplayListener)
+
+ mTile = NightDisplayTile(
+ mHost,
+ mTestableLooper.looper,
+ Handler(mTestableLooper.looper),
+ FalsingManagerFake(),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQsLogger,
+ mLocationController,
+ mColorDisplayManager,
+ mNightDisplayListenerBuilder
+ )
+ }
+
+ @Test
+ fun testIcon_whenDisabled_showsOffState() {
+ whenever(mColorDisplayManager.isNightDisplayActivated).thenReturn(false)
+ val state = QSTile.BooleanState()
+
+ mTile.handleUpdateState(state, /* arg= */ null)
+
+ Truth.assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_nightlight_icon_off))
+ }
+
+ @Test
+ fun testIcon_whenEnabled_showsOnState() {
+ whenever(mColorDisplayManager.isNightDisplayActivated).thenReturn(true)
+ val state = QSTile.BooleanState()
+
+ mTile.handleUpdateState(state, /* arg= */ null)
+
+ Truth.assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_nightlight_icon_on))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
index 9eb688de3511..8601d6c0a357 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ReduceBrightColorsTileTest.java
@@ -32,13 +32,16 @@ import androidx.test.filters.SmallTest;
import com.android.internal.R;
import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R.drawable;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.ReduceBrightColorsController;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.settings.UserTracker;
import org.junit.Before;
@@ -130,4 +133,26 @@ public class ReduceBrightColorsTileTest extends SysuiTestCase {
.setReduceBrightColorsActivated(eq(true));
}
+ @Test
+ public void testIcon_whenTileEnabled_isOnState() {
+ when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(true);
+ mTile.refreshState();
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(drawable.qs_extra_dim_icon_on));
+ }
+
+ @Test
+ public void testIcon_whenTileDisabled_isOffState() {
+ when(mReduceBrightColorsController.isReduceBrightColorsActivated()).thenReturn(false);
+ mTile.refreshState();
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(drawable.qs_extra_dim_icon_off));
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index e6bd396ebc6c..30debdf4b744 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -41,9 +41,11 @@ import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.screenrecord.RecordingController;
import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -229,4 +231,38 @@ public class ScreenRecordTileTest extends SysuiTestCase {
assertFalse(mTile.getState().forceExpandIcon);
}
+
+ @Test
+ public void testIcon_whenRecording_isOnState() {
+ when(mController.isStarting()).thenReturn(false);
+ when(mController.isRecording()).thenReturn(true);
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_on));
+ }
+
+ @Test
+ public void testIcon_whenStarting_isOnState() {
+ when(mController.isStarting()).thenReturn(true);
+ when(mController.isRecording()).thenReturn(false);
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_on));
+ }
+
+ @Test
+ public void testIcon_whenRecordingOff_isOffState() {
+ when(mController.isStarting()).thenReturn(false);
+ when(mController.isRecording()).thenReturn(false);
+ QSTile.BooleanState state = new QSTile.BooleanState();
+
+ mTile.handleUpdateState(state, /* arg= */ null);
+
+ assertEquals(state.icon, QSTileImpl.ResourceIcon.get(R.drawable.qs_screen_record_icon_off));
+ }
+
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
new file mode 100644
index 000000000000..0c070da1fcb9
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UiModeNightTileTest.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.tiles
+
+import android.app.UiModeManager
+import android.content.Context
+import android.content.res.Configuration
+import android.content.res.Resources
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.qs.QSTile
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.tileimpl.QSTileImpl
+import com.android.systemui.statusbar.policy.BatteryController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.LocationController
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class UiModeNightTileTest : SysuiTestCase() {
+
+ @Mock private lateinit var mockContext: Context
+ @Mock private lateinit var uiModeManager: UiModeManager
+ @Mock private lateinit var resources: Resources
+ @Mock private lateinit var qsLogger: QSLogger
+ @Mock private lateinit var qsHost: QSTileHost
+ @Mock private lateinit var metricsLogger: MetricsLogger
+ @Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var activityStarter: ActivityStarter
+ @Mock private lateinit var configurationController: ConfigurationController
+ @Mock private lateinit var batteryController: BatteryController
+ @Mock private lateinit var locationController: LocationController
+
+ private val uiEventLogger = UiEventLoggerFake()
+ private val falsingManager = FalsingManagerFake()
+ private lateinit var testableLooper: TestableLooper
+ private lateinit var tile: UiModeNightTile
+ private lateinit var configuration: Configuration
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testableLooper = TestableLooper.get(this)
+ configuration = Configuration()
+ mContext.addMockSystemService(UiModeManager::class.java, uiModeManager)
+
+ `when`(qsHost.context).thenReturn(mockContext)
+ `when`(qsHost.userContext).thenReturn(mContext)
+ `when`(mockContext.resources).thenReturn(resources)
+ `when`(resources.configuration).thenReturn(configuration)
+ `when`(qsHost.uiEventLogger).thenReturn(uiEventLogger)
+
+ tile =
+ UiModeNightTile(
+ qsHost,
+ testableLooper.looper,
+ Handler(testableLooper.looper),
+ falsingManager,
+ metricsLogger,
+ statusBarStateController,
+ activityStarter,
+ qsLogger,
+ configurationController,
+ batteryController,
+ locationController
+ )
+ }
+
+ @Test
+ fun testIcon_whenNightModeOn_isOnState() {
+ val state = QSTile.BooleanState()
+ setNightModeOn()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_light_dark_theme_icon_on))
+ }
+
+ @Test
+ fun testIcon_whenNightModeOn_isOffState() {
+ val state = QSTile.BooleanState()
+ setNightModeOff()
+
+ tile.handleUpdateState(state, /* arg= */ null)
+
+ assertThat(state.icon)
+ .isEqualTo(QSTileImpl.ResourceIcon.get(R.drawable.qs_light_dark_theme_icon_off))
+ }
+
+ private fun setNightModeOn() {
+ `when`(uiModeManager.nightMode).thenReturn(UiModeManager.MODE_NIGHT_YES)
+ configuration.uiMode = Configuration.UI_MODE_NIGHT_YES
+ }
+
+ private fun setNightModeOff() {
+ `when`(uiModeManager.nightMode).thenReturn(UiModeManager.MODE_NIGHT_NO)
+ configuration.uiMode = Configuration.UI_MODE_NIGHT_NO
+ }
+}
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
index d09a5a11040f..f92247580df0 100644
--- 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
@@ -143,6 +143,12 @@ public class InternetDialogTest extends SysuiTestCase {
}
@Test
+ public void createInternetDialog_setAccessibilityPaneTitleToQuickSettings() {
+ assertThat(mDialogView.getAccessibilityPaneTitle())
+ .isEqualTo(mContext.getText(R.string.accessibility_desc_quick_settings));
+ }
+
+ @Test
public void hideWifiViews_WifiViewsGone() {
mInternetDialog.hideWifiViews();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
index 00f38081c5c9..5a4bafcb7ded 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageCaptureImplTest.kt
@@ -23,7 +23,7 @@ import android.os.Binder
import android.os.IBinder
import android.testing.AndroidTestingRunner
import android.view.Display
-import android.view.SurfaceControl.ScreenshotHardwareBuffer
+import android.window.ScreenCapture.ScreenshotHardwareBuffer
import com.android.systemui.SysuiTestCase
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
@@ -84,7 +84,12 @@ class ImageCaptureImplTest : SysuiTestCase() {
this.width = width
this.height = height
this.crop = crop
- return ScreenshotHardwareBuffer(null, null, false, false)
+ return ScreenshotHardwareBuffer(
+ null,
+ null,
+ false,
+ false
+ )
}
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 6ce9cff9a730..002ef2962b03 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -34,6 +34,7 @@ import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import android.view.WindowManager.TAKE_SCREENSHOT_SELECTED_REGION
+import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.ScreenshotHelper
import com.android.internal.util.ScreenshotHelper.ScreenshotRequest
@@ -65,6 +66,7 @@ private const val USER_ID = 1
private const val TASK_ID = 1
@RunWith(AndroidTestingRunner::class)
+@SmallTest
class TakeScreenshotServiceTest : SysuiTestCase() {
private val application = mock<Application>()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index e73071335c9d..b40d5ac69d7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -1022,7 +1022,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
FalsingManager.FalsingTapListener listener = getFalsingTapListener();
mStatusBarStateController.setState(KEYGUARD);
- listener.onDoubleTapRequired();
+ listener.onAdditionalTapRequired();
verify(mKeyguardIndicationController).showTransientIndication(anyInt());
}
@@ -1032,7 +1032,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
FalsingManager.FalsingTapListener listener = getFalsingTapListener();
mStatusBarStateController.setState(SHADE_LOCKED);
- listener.onDoubleTapRequired();
+ listener.onAdditionalTapRequired();
verify(mTapAgainViewController).show();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 2adc389a964e..481e4e9992d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -21,12 +21,16 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.MotionEvent
import android.view.ViewGroup
import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardHostViewController
import com.android.keyguard.LockIconViewController
+import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dock.DockManager
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler
import com.android.systemui.statusbar.LockscreenShadeTransitionController
import com.android.systemui.statusbar.NotificationShadeDepthController
@@ -51,9 +55,9 @@ import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
+@SmallTest
@RunWith(AndroidTestingRunner::class)
@RunWithLooper(setAsMainLooper = true)
-@SmallTest
class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var view: NotificationShadeWindowView
@@ -72,8 +76,12 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
@Mock
private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController
@Mock
+ private lateinit var featureFlags: FeatureFlags
+ @Mock
private lateinit var ambientState: AmbientState
@Mock
+ private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel
+ @Mock
private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController
@Mock
private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager
@@ -87,6 +95,10 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController
@Mock
private lateinit var pulsingGestureListener: PulsingGestureListener
+ @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory
+ @Mock lateinit var keyguardBouncerContainer: ViewGroup
+ @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent
+ @Mock lateinit var keyguardHostViewController: KeyguardHostViewController
private lateinit var interactionEventHandlerCaptor: ArgumentCaptor<InteractionEventHandler>
private lateinit var interactionEventHandler: InteractionEventHandler
@@ -97,7 +109,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(view.bottom).thenReturn(VIEW_BOTTOM)
-
underTest = NotificationShadeWindowViewController(
lockscreenShadeTransitionController,
FalsingCollectorFake(),
@@ -115,7 +126,10 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() {
notificationShadeWindowController,
keyguardUnlockAnimationController,
ambientState,
- pulsingGestureListener
+ pulsingGestureListener,
+ featureFlags,
+ keyguardBouncerViewModel,
+ keyguardBouncerComponentFactory
)
underTest.setupExpandedStatusBar()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index 001bfeeabe6f..4a7dec912895 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -33,11 +33,14 @@ import android.view.MotionEvent;
import androidx.test.filters.SmallTest;
import com.android.keyguard.LockIconViewController;
+import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.dock.DockManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel;
import com.android.systemui.statusbar.DragDownHelper;
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.NotificationShadeDepthController;
@@ -86,6 +89,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
@Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock private AmbientState mAmbientState;
@Mock private PulsingGestureListener mPulsingGestureListener;
+ @Mock private FeatureFlags mFeatureFlags;
+ @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel;
+ @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
@Captor private ArgumentCaptor<NotificationShadeWindowView.InteractionEventHandler>
mInteractionEventHandlerCaptor;
@@ -121,7 +127,10 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
mNotificationShadeWindowController,
mKeyguardUnlockAnimationController,
mAmbientState,
- mPulsingGestureListener
+ mPulsingGestureListener,
+ mFeatureFlags,
+ mKeyguardBouncerViewModel,
+ mKeyguardBouncerComponentFactory
);
mController.setupExpandedStatusBar();
mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
index 97c0bb2c09ca..09add652483e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
@@ -89,7 +89,7 @@ class PulsingGestureListenerTest : SysuiTestCase() {
@Test
fun testGestureDetector_singleTapEnabled() {
- whenever(statusBarStateController.isPulsing).thenReturn(true)
+ whenever(statusBarStateController.isDozing).thenReturn(true)
// GIVEN tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
@@ -100,7 +100,7 @@ class PulsingGestureListenerTest : SysuiTestCase() {
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(false)
// WHEN there's a tap
- underTest.onSingleTapConfirmed(downEv)
+ underTest.onSingleTapUp(upEv)
// THEN wake up device if dozing
verify(centralSurfaces).wakeUpIfDozing(anyLong(), anyObject(), anyString())
@@ -108,7 +108,7 @@ class PulsingGestureListenerTest : SysuiTestCase() {
@Test
fun testGestureDetector_doubleTapEnabled() {
- whenever(statusBarStateController.isPulsing).thenReturn(true)
+ whenever(statusBarStateController.isDozing).thenReturn(true)
// GIVEN double tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
@@ -119,15 +119,27 @@ class PulsingGestureListenerTest : SysuiTestCase() {
whenever(falsingManager.isFalseDoubleTap).thenReturn(false)
// WHEN there's a double tap
- underTest.onDoubleTap(downEv)
+ underTest.onDoubleTapEvent(upEv)
// THEN wake up device if dozing
verify(centralSurfaces).wakeUpIfDozing(anyLong(), anyObject(), anyString())
}
@Test
+ fun testGestureDetector_doubleTapEnabled_onDownEvent_noFalsingCheck() {
+ // GIVEN tap is enabled
+ whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
+
+ // WHEN there's a double tap on DOWN event
+ underTest.onDoubleTapEvent(downEv)
+
+ // THEN don't check the falsing manager, should only be checked on the UP event
+ verify(falsingManager, never()).isFalseDoubleTap()
+ }
+
+ @Test
fun testGestureDetector_singleTapEnabled_falsing() {
- whenever(statusBarStateController.isPulsing).thenReturn(true)
+ whenever(statusBarStateController.isDozing).thenReturn(true)
// GIVEN tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
@@ -138,29 +150,43 @@ class PulsingGestureListenerTest : SysuiTestCase() {
whenever(falsingManager.isFalseTap(anyInt())).thenReturn(true)
// WHEN there's a tap
- underTest.onSingleTapConfirmed(downEv)
+ underTest.onSingleTapUp(upEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
}
@Test
- fun testGestureDetector_notPulsing_noFalsingCheck() {
- whenever(statusBarStateController.isPulsing).thenReturn(false)
+ fun testSingleTap_notDozing_noFalsingCheck() {
+ whenever(statusBarStateController.isDozing).thenReturn(false)
- // GIVEN tap is enabled, prox not covered
+ // GIVEN tap is enabled
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
// WHEN there's a tap
- underTest.onSingleTapConfirmed(downEv)
+ underTest.onSingleTapUp(upEv)
- // THEN the falsing manager never gets a call (because the device wasn't pulsing
+ // THEN the falsing manager never gets a call (because the device wasn't dozing
+ // during the tap)
+ verify(falsingManager, never()).isFalseTap(anyInt())
+ }
+
+ @Test
+ fun testDoubleTap_notDozing_noFalsingCheck() {
+ whenever(statusBarStateController.isDozing).thenReturn(false)
+
+ // GIVEN tap is enabled
+ whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
+ // WHEN there's a tap
+ underTest.onDoubleTapEvent(upEv)
+
+ // THEN the falsing manager never gets a call (because the device wasn't dozing
// during the tap)
verify(falsingManager, never()).isFalseTap(anyInt())
}
@Test
fun testGestureDetector_doubleTapEnabled_falsing() {
- whenever(statusBarStateController.isPulsing).thenReturn(true)
+ whenever(statusBarStateController.isDozing).thenReturn(true)
// GIVEN double tap is enabled, prox not covered
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
@@ -170,8 +196,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
// GIVEN the falsing manager thinks the tap is a false tap
whenever(falsingManager.isFalseDoubleTap).thenReturn(true)
- // WHEN there's a tap
- underTest.onDoubleTap(downEv)
+ // WHEN there's a double tap ACTION_UP event
+ underTest.onDoubleTapEvent(upEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
@@ -179,7 +205,7 @@ class PulsingGestureListenerTest : SysuiTestCase() {
@Test
fun testGestureDetector_singleTapEnabled_proxCovered() {
- whenever(statusBarStateController.isPulsing).thenReturn(true)
+ whenever(statusBarStateController.isDozing).thenReturn(true)
// GIVEN tap is enabled, not a false tap based on classifiers
whenever(ambientDisplayConfiguration.tapGestureEnabled(anyInt())).thenReturn(true)
@@ -190,7 +216,7 @@ class PulsingGestureListenerTest : SysuiTestCase() {
whenever(falsingManager.isProximityNear()).thenReturn(true)
// WHEN there's a tap
- underTest.onSingleTapConfirmed(downEv)
+ underTest.onSingleTapUp(upEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
@@ -198,7 +224,7 @@ class PulsingGestureListenerTest : SysuiTestCase() {
@Test
fun testGestureDetector_doubleTapEnabled_proxCovered() {
- whenever(statusBarStateController.isPulsing).thenReturn(true)
+ whenever(statusBarStateController.isDozing).thenReturn(true)
// GIVEN double tap is enabled, not a false tap based on classifiers
whenever(ambientDisplayConfiguration.doubleTapGestureEnabled(anyInt())).thenReturn(true)
@@ -209,7 +235,7 @@ class PulsingGestureListenerTest : SysuiTestCase() {
whenever(falsingManager.isProximityNear()).thenReturn(true)
// WHEN there's a tap
- underTest.onDoubleTap(downEv)
+ underTest.onDoubleTapEvent(upEv)
// THEN the device doesn't wake up
verify(centralSurfaces, never()).wakeUpIfDozing(anyLong(), anyObject(), anyString())
@@ -227,3 +253,4 @@ class PulsingGestureListenerTest : SysuiTestCase() {
}
private val downEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0)
+private val upEv = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_UP, 0f, 0f, 0)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
new file mode 100644
index 000000000000..eb34561d15a0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/AnimatableClockViewTest.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.clocks
+
+import android.testing.AndroidTestingRunner
+import android.view.LayoutInflater
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.TextAnimator
+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.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class AnimatableClockViewTest : SysuiTestCase() {
+
+ @JvmField @Rule val mockito = MockitoJUnit.rule()
+
+ @Mock private lateinit var mockTextAnimator: TextAnimator
+ private lateinit var clockView: AnimatableClockView
+
+ @Before
+ fun setUp() {
+ val layoutInflater = LayoutInflater.from(context)
+ clockView =
+ layoutInflater.inflate(R.layout.clock_default_small, null) as AnimatableClockView
+ clockView.textAnimatorFactory = { _, _ -> mockTextAnimator }
+ }
+
+ @Test
+ fun validateColorAnimationRunsBeforeMeasure() {
+ clockView.setColors(100, 200)
+ clockView.animateAppearOnLockscreen()
+ clockView.measure(50, 50)
+
+ verify(mockTextAnimator).glyphFilter = null
+ verify(mockTextAnimator).setTextStyle(300, -1.0f, 200, false, 350L, null, 0L, null)
+ verifyNoMoreInteractions(mockTextAnimator)
+ }
+
+ @Test
+ fun validateColorAnimationRunsAfterMeasure() {
+ clockView.setColors(100, 200)
+ clockView.measure(50, 50)
+ clockView.animateAppearOnLockscreen()
+
+ verify(mockTextAnimator, times(2)).glyphFilter = null
+ verify(mockTextAnimator).setTextStyle(100, -1.0f, 200, false, 0L, null, 0L, null)
+ verify(mockTextAnimator).setTextStyle(300, -1.0f, 200, true, 350L, null, 0L, null)
+ verifyNoMoreInteractions(mockTextAnimator)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
index 131eac668af3..8be138a3b2be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/ClockRegistryTest.kt
@@ -113,7 +113,7 @@ class ClockRegistryTest : SysuiTestCase() {
registry.isEnabled = true
verify(mockPluginManager)
- .addPluginListener(captor.capture(), eq(ClockProviderPlugin::class.java))
+ .addPluginListener(captor.capture(), eq(ClockProviderPlugin::class.java), eq(true))
pluginListener = captor.value
}
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 74e274705e55..ec5d0894fbef 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -19,8 +19,10 @@ package com.android.systemui.statusbar;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
+import static android.hardware.biometrics.BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT;
+import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_HELP_FINGERPRINT_NOT_RECOGNIZED;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_ALIGNMENT;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_BATTERY;
@@ -86,6 +88,7 @@ import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.settingslib.fuelgauge.BatteryStatus;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.biometrics.FaceHelpMessageDeferral;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dock.DockManager;
import com.android.systemui.keyguard.KeyguardIndication;
@@ -167,6 +170,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
@Mock
private AccessibilityManager mAccessibilityManager;
@Mock
+ private FaceHelpMessageDeferral mFaceHelpMessageDeferral;
+ @Mock
private ScreenLifecycle mScreenLifecycle;
@Captor
private ArgumentCaptor<DockManager.AlignmentStateListener> mAlignmentListener;
@@ -259,7 +264,8 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mKeyguardStateController, mStatusBarStateController, mKeyguardUpdateMonitor,
mDockManager, mBroadcastDispatcher, mDevicePolicyManager, mIBatteryStats,
mUserManager, mExecutor, mExecutor, mFalsingManager, mLockPatternUtils,
- mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager);
+ mScreenLifecycle, mKeyguardBypassController, mAccessibilityManager,
+ mFaceHelpMessageDeferral);
mController.init();
mController.setIndicationArea(mIndicationArea);
verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
@@ -535,7 +541,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
mController.setVisible(true);
mController.getKeyguardCallback().onBiometricHelp(
- KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message,
+ BIOMETRIC_HELP_FACE_NOT_RECOGNIZED, message,
BiometricSourceType.FACE);
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, message);
reset(mRotateTextViewController);
@@ -582,7 +588,7 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
// WHEN there's a face not recognized message
mController.getKeyguardCallback().onBiometricHelp(
- KeyguardUpdateMonitor.BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
+ BIOMETRIC_HELP_FACE_NOT_RECOGNIZED,
message,
BiometricSourceType.FACE);
@@ -629,6 +635,19 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
}
@Test
+ public void transientIndication_visibleWhenDozing_ignoresPowerPressed() {
+ createController();
+
+ mController.setVisible(true);
+ reset(mRotateTextViewController);
+ mController.getKeyguardCallback().onBiometricError(
+ FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED, "foo",
+ BiometricSourceType.FINGERPRINT);
+
+ verifyNoMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE);
+ }
+
+ @Test
public void transientIndication_swipeUpToRetry() {
createController();
String message = mContext.getString(R.string.keyguard_retry);
@@ -748,8 +767,10 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
0)).thenReturn(false);
- // WHEN help message received
+ // WHEN help message received and deferred message is valid
final String helpString = "helpMsg";
+ when(mFaceHelpMessageDeferral.getDeferredMessage()).thenReturn(helpString);
+ when(mFaceHelpMessageDeferral.shouldDefer(FACE_ACQUIRED_TOO_DARK)).thenReturn(true);
mKeyguardUpdateMonitorCallback.onBiometricHelp(
BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK,
helpString,
@@ -777,8 +798,10 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
when(mKeyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
0)).thenReturn(true);
- // WHEN help message received
+ // WHEN help message received and deferredMessage is valid
final String helpString = "helpMsg";
+ when(mFaceHelpMessageDeferral.getDeferredMessage()).thenReturn(helpString);
+ when(mFaceHelpMessageDeferral.shouldDefer(FACE_ACQUIRED_TOO_DARK)).thenReturn(true);
mKeyguardUpdateMonitorCallback.onBiometricHelp(
BiometricFaceConstants.FACE_ACQUIRED_TOO_DARK,
helpString,
@@ -1126,7 +1149,6 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE_FOLLOW_UP, pressToOpen);
}
-
@Test
public void coEx_faceSuccess_touchExplorationEnabled_showsFaceUnlockedSwipeToOpen() {
// GIVEN bouncer isn't showing, can skip bouncer, udfps is supported, a11y enabled
@@ -1272,6 +1294,87 @@ public class KeyguardIndicationControllerTest extends SysuiTestCase {
verifyIndicationMessage(INDICATION_TYPE_BIOMETRIC_MESSAGE, swipeToOpen);
}
+ @Test
+ public void faceOnAcquired_processFrame() {
+ createController();
+
+ // WHEN face sends an acquired message
+ final int acquireInfo = 1;
+ mKeyguardUpdateMonitorCallback.onBiometricAcquired(BiometricSourceType.FACE, acquireInfo);
+
+ // THEN face help message deferral should process the acquired frame
+ verify(mFaceHelpMessageDeferral).processFrame(acquireInfo);
+ }
+
+ @Test
+ public void fingerprintOnAcquired_noProcessFrame() {
+ createController();
+
+ // WHEN fingerprint sends an acquired message
+ mKeyguardUpdateMonitorCallback.onBiometricAcquired(BiometricSourceType.FINGERPRINT, 1);
+
+ // THEN face help message deferral should NOT process any acquired frames
+ verify(mFaceHelpMessageDeferral, never()).processFrame(anyInt());
+ }
+
+ @Test
+ public void onBiometricHelp_fingerprint_faceHelpMessageDeferralDoesNothing() {
+ createController();
+
+ // WHEN fingerprint sends an onBiometricHelp
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ 1,
+ "placeholder",
+ BiometricSourceType.FINGERPRINT);
+
+ // THEN face help message deferral is NOT: reset, updated, or checked for shouldDefer
+ verify(mFaceHelpMessageDeferral, never()).reset();
+ verify(mFaceHelpMessageDeferral, never()).updateMessage(anyInt(), anyString());
+ verify(mFaceHelpMessageDeferral, never()).shouldDefer(anyInt());
+ }
+
+ @Test
+ public void onBiometricFailed_resetFaceHelpMessageDeferral() {
+ createController();
+
+ // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED
+ mKeyguardUpdateMonitorCallback.onBiometricAuthFailed(BiometricSourceType.FACE);
+
+ // THEN face help message deferral is reset
+ verify(mFaceHelpMessageDeferral).reset();
+ }
+
+ @Test
+ public void onBiometricError_resetFaceHelpMessageDeferral() {
+ createController();
+
+ // WHEN face has an error
+ mKeyguardUpdateMonitorCallback.onBiometricError(4, "string",
+ BiometricSourceType.FACE);
+
+ // THEN face help message deferral is reset
+ verify(mFaceHelpMessageDeferral).reset();
+ }
+
+ @Test
+ public void onBiometricHelp_faceAcquiredInfo_faceHelpMessageDeferral() {
+ createController();
+
+ // WHEN face sends an onBiometricHelp BIOMETRIC_HELP_FACE_NOT_RECOGNIZED
+ final int msgId = 1;
+ final String helpString = "test";
+ mKeyguardUpdateMonitorCallback.onBiometricHelp(
+ msgId,
+ "test",
+ BiometricSourceType.FACE);
+
+ // THEN face help message deferral is NOT reset and message IS updated
+ verify(mFaceHelpMessageDeferral, never()).reset();
+ verify(mFaceHelpMessageDeferral).updateMessage(msgId, helpString);
+ }
+
+
+
private void sendUpdateDisclosureBroadcast() {
mBroadcastReceiver.onReceive(mContext, new Intent());
}
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 d3c1dc9db218..a95a49c31adf 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
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.stack;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -32,8 +34,10 @@ import androidx.test.filters.SmallTest;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dump.DumpManager;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.logging.NotificationRoundnessLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -57,6 +61,7 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
private Runnable mRoundnessCallback = mock(Runnable.class);
private ExpandableNotificationRow mFirst;
private ExpandableNotificationRow mSecond;
+ private NotificationRoundnessLogger mLogger = mock(NotificationRoundnessLogger.class);
private float mSmallRadiusRatio;
@Before
@@ -66,7 +71,9 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
mSmallRadiusRatio = resources.getDimension(R.dimen.notification_corner_radius_small)
/ resources.getDimension(R.dimen.notification_corner_radius);
mRoundnessManager = new NotificationRoundnessManager(
- new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
+ new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext),
+ mLogger,
+ mock(DumpManager.class));
allowTestableLooperAsMainThread();
NotificationTestHelper testHelper = new NotificationTestHelper(
mContext,
@@ -337,6 +344,20 @@ public class NotificationRoundnessManagerTest extends SysuiTestCase {
Assert.assertTrue(mSecond.isLastInSection());
}
+ @Test
+ public void testLoggingOnRoundingUpdate() {
+ NotificationSection[] sections = new NotificationSection[]{
+ createSection(mFirst, mSecond),
+ createSection(null, null)
+ };
+ mRoundnessManager.updateRoundedChildren(sections);
+ verify(mLogger).onSectionCornersUpdated(sections, /*anyChanged=*/ true);
+ verify(mLogger, atLeast(1)).onCornersUpdated(eq(mFirst), anyBoolean(),
+ anyBoolean(), anyBoolean(), anyBoolean());
+ verify(mLogger, atLeast(1)).onCornersUpdated(eq(mSecond), anyBoolean(),
+ anyBoolean(), anyBoolean(), anyBoolean());
+ }
+
private NotificationSection createSection(ExpandableNotificationRow first,
ExpandableNotificationRow last) {
NotificationSection section = mock(NotificationSection.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 14b74712ec43..f510e48de5a5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.doAnswer;
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;
import static org.mockito.Mockito.when;
@@ -69,7 +70,11 @@ import android.util.DisplayMetrics;
import android.util.SparseArray;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
+import android.view.ViewRootImpl;
import android.view.WindowManager;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
+import android.window.WindowOnBackInvokedDispatcher;
import androidx.test.filters.SmallTest;
@@ -168,6 +173,7 @@ 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;
@@ -279,6 +285,15 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
@Mock private InteractionJankMonitor mJankMonitor;
@Mock private DeviceStateManager mDeviceStateManager;
@Mock private WiredChargingRippleController mWiredChargingRippleController;
+ /**
+ * The process of registering/unregistering a predictive back callback requires a
+ * ViewRootImpl, which is present IRL, but may be missing during a Mockito unit test.
+ * To prevent an NPE during test execution, we explicitly craft and provide a fake ViewRootImpl.
+ */
+ @Mock private ViewRootImpl mViewRootImpl;
+ @Mock private WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
+ @Captor private ArgumentCaptor<OnBackInvokedCallback> mOnBackInvokedCallback;
+
private ShadeController mShadeController;
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -368,10 +383,10 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
return null;
}).when(mNotificationShadeWindowController).batchApplyWindowLayoutParams(any());
- mShadeController = new ShadeControllerImpl(mCommandQueue,
+ mShadeController = spy(new ShadeControllerImpl(mCommandQueue,
mStatusBarStateController, mNotificationShadeWindowController,
mStatusBarKeyguardViewManager, mContext.getSystemService(WindowManager.class),
- () -> Optional.of(mCentralSurfaces), () -> mAssistManager);
+ () -> Optional.of(mCentralSurfaces), () -> mAssistManager));
when(mOperatorNameViewControllerFactory.create(any()))
.thenReturn(mOperatorNameViewController);
@@ -460,7 +475,14 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mActivityLaunchAnimator,
mJankMonitor,
mDeviceStateManager,
- mWiredChargingRippleController, mDreamManager);
+ mWiredChargingRippleController, mDreamManager) {
+ @Override
+ protected ViewRootImpl getViewRootImpl() {
+ return mViewRootImpl;
+ }
+ };
+ when(mViewRootImpl.getOnBackInvokedDispatcher())
+ .thenReturn(mOnBackInvokedDispatcher);
when(mKeyguardViewMediator.registerCentralSurfaces(
any(CentralSurfacesImpl.class),
any(NotificationPanelViewController.class),
@@ -738,6 +760,43 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
}
}
+ /**
+ * Do the following:
+ * 1. verify that a predictive back callback is registered when CSurf becomes visible
+ * 2. verify that the same callback is unregistered when CSurf becomes invisible
+ */
+ @Test
+ public void testPredictiveBackCallback_registration() {
+ mCentralSurfaces.handleVisibleToUserChanged(true);
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ mOnBackInvokedCallback.capture());
+
+ mCentralSurfaces.handleVisibleToUserChanged(false);
+ verify(mOnBackInvokedDispatcher).unregisterOnBackInvokedCallback(
+ eq(mOnBackInvokedCallback.getValue()));
+ }
+
+ /**
+ * Do the following:
+ * 1. capture the predictive back callback during registration
+ * 2. call the callback directly
+ * 3. verify that the ShadeController's panel collapse animation is invoked
+ */
+ @Test
+ public void testPredictiveBackCallback_invocationCollapsesPanel() {
+ mCentralSurfaces.setNotificationShadeWindowViewController(
+ mNotificationShadeWindowViewController);
+ mCentralSurfaces.handleVisibleToUserChanged(true);
+ verify(mOnBackInvokedDispatcher).registerOnBackInvokedCallback(
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ mOnBackInvokedCallback.capture());
+
+ when(mNotificationPanelViewController.canPanelBeCollapsed()).thenReturn(true);
+ mOnBackInvokedCallback.getValue().onBackInvoked();
+ verify(mShadeController).animateCollapsePanels();
+ }
+
@Test
public void testPanelOpenForHeadsUp() {
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
index 60a3d95e24f6..ab209d130b2f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardBouncerTest.java
@@ -408,6 +408,16 @@ public class KeyguardBouncerTest extends SysuiTestCase {
mBouncer.hide(false /* destroyView */);
verify(mHandler).removeCallbacks(eq(showRunnable.getValue()));
}
+
+ @Test
+ public void testShow_doesNotDelaysIfFaceAuthIsLockedOut() {
+ when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isFaceLockedOut()).thenReturn(true);
+ mBouncer.show(true /* reset */);
+
+ verify(mHandler, never()).postDelayed(any(), anyLong());
+ }
+
@Test
public void testShow_delaysIfFaceAuthIsRunning_unlessBypassEnabled() {
when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
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 e790d85763d0..ee4b9d9c93f2 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
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -40,11 +41,17 @@ import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardMessageAreaController;
+import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.keyguard.data.BouncerView;
+import com.android.systemui.keyguard.data.BouncerViewDelegate;
+import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor;
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
import com.android.systemui.navigationbar.NavigationModeController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.shade.NotificationPanelViewController;
@@ -100,6 +107,13 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Mock private SysUIUnfoldComponent mSysUiUnfoldComponent;
@Mock private DreamOverlayStateController mDreamOverlayStateController;
@Mock private LatencyTracker mLatencyTracker;
+ @Mock private FeatureFlags mFeatureFlags;
+ @Mock private KeyguardSecurityModel mKeyguardSecurityModel;
+ @Mock private BouncerCallbackInteractor mBouncerCallbackInteractor;
+ @Mock private BouncerInteractor mBouncerInteractor;
+ @Mock private BouncerView mBouncerView;
+// @Mock private WeakReference<BouncerViewDelegate> mBouncerViewDelegateWeakReference;
+ @Mock private BouncerViewDelegate mBouncerViewDelegate;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
@@ -114,6 +128,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea);
when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class)))
.thenReturn(mKeyguardMessageAreaController);
+ when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate);
+
mStatusBarKeyguardViewManager =
new StatusBarKeyguardViewManager(
getContext(),
@@ -132,7 +148,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mKeyguardMessageAreaFactory,
Optional.of(mSysUiUnfoldComponent),
() -> mShadeController,
- mLatencyTracker);
+ mLatencyTracker,
+ mKeyguardSecurityModel,
+ mFeatureFlags,
+ mBouncerCallbackInteractor,
+ mBouncerInteractor,
+ mBouncerView);
mStatusBarKeyguardViewManager.registerCentralSurfaces(
mCentralSurfaces,
mNotificationPanelView,
@@ -505,4 +526,21 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
mBouncerExpansionCallback.onVisibilityChanged(false);
verify(mCentralSurfaces).setBouncerShowingOverDream(false);
}
+
+ @Test
+ public void testSetDozing_Dozing() {
+ clearInvocations(mBouncer);
+ mStatusBarKeyguardViewManager.onDozingChanged(true);
+ // Once when shown and once with dozing changed.
+ verify(mBouncer, times(1)).hide(false);
+ }
+
+ @Test
+ public void testSetDozing_notDozing() {
+ mStatusBarKeyguardViewManager.onDozingChanged(true);
+ clearInvocations(mBouncer);
+ mStatusBarKeyguardViewManager.onDozingChanged(false);
+ // Once when shown and twice with dozing changed.
+ verify(mBouncer, times(1)).hide(false);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
index 37c0f3621b6f..bf432388ad28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
@@ -34,14 +34,14 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
@SmallTest
-class StatusBarUserSwitcherControllerTest : SysuiTestCase() {
+class StatusBarUserSwitcherControllerOldImplTest : SysuiTestCase() {
@Mock
private lateinit var tracker: StatusBarUserInfoTracker
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
new file mode 100644
index 000000000000..f3046477f4d1
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapterTest.kt
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.pm.UserInfo
+import android.graphics.Bitmap
+import android.os.UserHandle
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.user.data.source.UserRecord
+import com.android.systemui.util.mockito.kotlinArgumentCaptor
+import com.android.systemui.util.mockito.mock
+import com.google.common.truth.Truth.assertThat
+import java.lang.ref.WeakReference
+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.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class BaseUserSwitcherAdapterTest : SysuiTestCase() {
+
+ @Mock private lateinit var controller: UserSwitcherController
+
+ private lateinit var underTest: BaseUserSwitcherAdapter
+
+ private lateinit var users: ArrayList<UserRecord>
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ users =
+ ArrayList(
+ listOf(
+ createUserRecord(
+ id = 0,
+ picture = mock(),
+ isSelected = true,
+ isGuest = false,
+ ),
+ createUserRecord(
+ id = 1,
+ picture = mock(),
+ isSelected = false,
+ isGuest = false,
+ ),
+ createUserRecord(
+ id = UserHandle.USER_NULL,
+ picture = null,
+ isSelected = false,
+ isGuest = true,
+ ),
+ )
+ )
+
+ whenever(controller.users).thenAnswer { users }
+
+ underTest =
+ object : BaseUserSwitcherAdapter(controller) {
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
+ return mock()
+ }
+ }
+ }
+
+ @Test
+ fun `Adds self to controller in constructor`() {
+ val captor = kotlinArgumentCaptor<WeakReference<BaseUserSwitcherAdapter>>()
+ verify(controller).addAdapter(captor.capture())
+
+ assertThat(captor.value.get()).isEqualTo(underTest)
+ }
+
+ @Test
+ fun count() {
+ assertThat(underTest.count).isEqualTo(users.size)
+ }
+
+ @Test
+ fun `count - ignores restricted users when device is locked`() {
+ whenever(controller.isKeyguardShowing).thenReturn(true)
+ users =
+ ArrayList(
+ listOf(
+ createUserRecord(
+ id = 0,
+ picture = mock(),
+ isSelected = true,
+ isGuest = false,
+ isRestricted = false,
+ ),
+ createUserRecord(
+ id = 1,
+ picture = mock(),
+ isSelected = false,
+ isGuest = false,
+ isRestricted = true, // this one will be ignored.
+ ),
+ createUserRecord(
+ id = UserHandle.USER_NULL,
+ picture = null,
+ isSelected = false,
+ isGuest = true,
+ ),
+ )
+ )
+ assertThat(underTest.count).isEqualTo(users.size - 1)
+ }
+
+ @Test
+ fun `count - does not ignore restricted users when device is not locked`() {
+ whenever(controller.isKeyguardShowing).thenReturn(false)
+ users =
+ ArrayList(
+ listOf(
+ createUserRecord(
+ id = 0,
+ picture = mock(),
+ isSelected = true,
+ isGuest = false,
+ isRestricted = false,
+ ),
+ createUserRecord(
+ id = 1,
+ picture = mock(),
+ isSelected = false,
+ isGuest = false,
+ isRestricted = true,
+ ),
+ createUserRecord(
+ id = UserHandle.USER_NULL,
+ picture = null,
+ isSelected = false,
+ isGuest = true,
+ ),
+ )
+ )
+ assertThat(underTest.count).isEqualTo(users.size)
+ }
+
+ @Test
+ fun getItem() {
+ assertThat((0 until underTest.count).map { position -> underTest.getItem(position) })
+ .isEqualTo(users)
+ }
+
+ @Test
+ fun getItemId() {
+ (0 until underTest.count).map { position ->
+ assertThat(underTest.getItemId(position)).isEqualTo(position)
+ }
+ }
+
+ @Test
+ fun onUserListItemClicked() {
+ val userRecord = users[users.size / 2]
+ val dialogShower: UserSwitchDialogController.DialogShower = mock()
+
+ underTest.onUserListItemClicked(userRecord, dialogShower)
+
+ verify(controller).onUserListItemClicked(userRecord, dialogShower)
+ }
+
+ @Test
+ fun `getName - non guest - returns real name`() {
+ val userRecord =
+ createUserRecord(
+ id = 1,
+ picture = mock(),
+ )
+
+ assertThat(underTest.getName(context, userRecord)).isEqualTo(userRecord.info?.name)
+ }
+
+ @Test
+ fun `getName - guest and selected - returns exit guest action name`() {
+ val expected = "Exit guest"
+ context.orCreateTestableResources.addOverride(
+ com.android.settingslib.R.string.guest_exit_quick_settings_button,
+ expected,
+ )
+
+ val userRecord =
+ createUserRecord(
+ id = 2,
+ picture = null,
+ isGuest = true,
+ isSelected = true,
+ )
+
+ assertThat(underTest.getName(context, userRecord)).isEqualTo(expected)
+ }
+
+ @Test
+ fun `getName - guest and not selected - returns enter guest action name`() {
+ val expected = "Guest"
+ context.orCreateTestableResources.addOverride(
+ com.android.internal.R.string.guest_name,
+ expected,
+ )
+
+ val userRecord =
+ createUserRecord(
+ id = 2,
+ picture = null,
+ isGuest = true,
+ isSelected = false,
+ )
+
+ assertThat(underTest.getName(context, userRecord)).isEqualTo("Guest")
+ }
+
+ @Test
+ fun refresh() {
+ underTest.refresh()
+
+ verify(controller).refreshUsers(UserHandle.USER_NULL)
+ }
+
+ private fun createUserRecord(
+ id: Int,
+ picture: Bitmap? = null,
+ isSelected: Boolean = false,
+ isGuest: Boolean = false,
+ isAction: Boolean = false,
+ isRestricted: Boolean = false,
+ ): UserRecord {
+ return UserRecord(
+ info =
+ if (isAction) {
+ null
+ } else {
+ UserInfo(id, "name$id", 0)
+ },
+ picture = picture,
+ isCurrent = isSelected,
+ isGuest = isGuest,
+ isRestricted = isRestricted,
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
index fda80a2f9ed8..43d0fe9559b6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryControllerTest.java
@@ -42,6 +42,7 @@ import com.android.settingslib.fuelgauge.BatterySaverUtils;
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.power.EnhancedEstimates;
import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
@@ -80,6 +81,7 @@ public class BatteryControllerTest extends SysuiTestCase {
mPowerManager,
mBroadcastDispatcher,
mDemoModeController,
+ mock(DumpManager.class),
new Handler(),
new Handler());
// Can throw if updateEstimate is called on the main thread
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 3dd36d134cf7..d0391ac0795c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -41,6 +41,7 @@ import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfile;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bluetooth.BluetoothLogger;
import com.android.systemui.dump.DumpManager;
import org.junit.Before;
@@ -81,6 +82,7 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
mBluetoothControllerImpl = new BluetoothControllerImpl(mContext,
mMockDumpManager,
+ mock(BluetoothLogger.class),
mTestableLooper.getLooper(),
mTestableLooper.getLooper(),
mMockBluetoothManager);
@@ -233,4 +235,11 @@ public class BluetoothControllerImplTest extends SysuiTestCase {
assertTrue(mBluetoothControllerImpl.isBluetoothAudioActive());
assertTrue(mBluetoothControllerImpl.isBluetoothAudioProfileOnly());
}
+
+ /** Regression test for b/246876230. */
+ @Test
+ public void testOnActiveDeviceChanged_null_noCrash() {
+ mBluetoothControllerImpl.onActiveDeviceChanged(null, BluetoothProfile.HEADSET);
+ // No assert, just need no crash.
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
index b4f3987b2f95..b86ca6fc5375 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardQsUserSwitchControllerTest.kt
@@ -38,9 +38,9 @@ import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@SmallTest
@@ -102,8 +102,7 @@ class KeyguardQsUserSwitchControllerTest : SysuiTestCase() {
ViewUtils.attachView(view)
testableLooper.processAllMessages()
- `when`(userSwitcherController.keyguardStateController).thenReturn(keyguardStateController)
- `when`(userSwitcherController.keyguardStateController.isShowing).thenReturn(true)
+ `when`(userSwitcherController.isKeyguardShowing).thenReturn(true)
`when`(keyguardStateController.isShowing).thenReturn(true)
`when`(keyguardStateController.isKeyguardGoingAway).thenReturn(false)
keyguardQsUserSwitchController.init()
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 c47ea9cea75e..6ace4044b3f7 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
@@ -275,10 +275,9 @@ public class RemoteInputViewTest extends SysuiTestCase {
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.
+ // 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)));
+ Uri.parse("android.resource://android/" + android.R.drawable.btn_default)));
ContentInfo payload =
new ContentInfo.Builder(clip, SOURCE_CLIPBOARD).build();
view.setAttachment(payload);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt
index 8dcd4bb3b738..76ecc1c7f36d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/UserSwitcherControllerOldImplTest.kt
@@ -86,7 +86,7 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-class UserSwitcherControllerTest : SysuiTestCase() {
+class UserSwitcherControllerOldImplTest : SysuiTestCase() {
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var activityManager: IActivityManager
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@@ -118,7 +118,7 @@ class UserSwitcherControllerTest : SysuiTestCase() {
private lateinit var longRunningExecutor: FakeExecutor
private lateinit var uiExecutor: FakeExecutor
private lateinit var uiEventLogger: UiEventLoggerFake
- private lateinit var userSwitcherController: UserSwitcherController
+ private lateinit var userSwitcherController: UserSwitcherControllerOldImpl
private lateinit var picture: Bitmap
private val ownerId = UserHandle.USER_SYSTEM
private val ownerInfo = UserInfo(ownerId, "Owner", null,
@@ -205,7 +205,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
}
private fun setupController() {
- userSwitcherController = UserSwitcherController(
+ userSwitcherController =
+ UserSwitcherControllerOldImpl(
mContext,
activityManager,
userManager,
@@ -230,7 +231,8 @@ class UserSwitcherControllerTest : SysuiTestCase() {
dumpManager,
dialogLaunchAnimator,
guestResumeSessionReceiver,
- guestResetOrExitSessionReceiver)
+ guestResetOrExitSessionReceiver
+ )
userSwitcherController.init(notificationShadeWindowView)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
index e616c26377d2..921b7efc38eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewDisplayControllerTest.kt
@@ -17,20 +17,14 @@
package com.android.systemui.temporarydisplay
import android.content.Context
-import android.content.pm.ApplicationInfo
-import android.content.pm.PackageManager
-import android.graphics.drawable.Drawable
import android.os.PowerManager
-import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.view.accessibility.AccessibilityManager
-import android.widget.ImageView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -42,9 +36,7 @@ import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
-import org.mockito.ArgumentCaptor
import org.mockito.Mock
-import org.mockito.Mockito.eq
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@@ -58,13 +50,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
- private lateinit var appIconFromPackageName: Drawable
@Mock
- private lateinit var packageManager: PackageManager
- @Mock
- private lateinit var applicationInfo: ApplicationInfo
- @Mock
- private lateinit var logger: MediaTttLogger
+ private lateinit var logger: TemporaryViewLogger
@Mock
private lateinit var accessibilityManager: AccessibilityManager
@Mock
@@ -78,17 +65,6 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
- appIconFromPackageName = context.getDrawable(R.drawable.ic_cake)!!
- whenever(packageManager.getApplicationIcon(PACKAGE_NAME)).thenReturn(appIconFromPackageName)
- whenever(applicationInfo.loadLabel(packageManager)).thenReturn(APP_NAME)
- whenever(packageManager.getApplicationInfo(
- any(), any<PackageManager.ApplicationInfoFlags>()
- )).thenThrow(PackageManager.NameNotFoundException())
- whenever(packageManager.getApplicationInfo(
- eq(PACKAGE_NAME), any<PackageManager.ApplicationInfoFlags>()
- )).thenReturn(applicationInfo)
- context.setMockPackageManager(packageManager)
-
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any()))
.thenReturn(TIMEOUT_MS.toInt())
@@ -229,117 +205,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
verify(windowManager, never()).removeView(any())
}
- @Test
- fun setIcon_nullAppIconDrawableAndNullPackageName_stillHasIcon() {
- underTest.displayView(getState())
- val view = getView()
-
- underTest.setIcon(view, appPackageName = null, appIconDrawableOverride = null)
-
- assertThat(view.getAppIconView().drawable).isNotNull()
- }
-
- @Test
- fun setIcon_nullAppIconDrawableAndInvalidPackageName_stillHasIcon() {
- underTest.displayView(getState())
- val view = getView()
-
- underTest.setIcon(
- view, appPackageName = "fakePackageName", appIconDrawableOverride = null
- )
-
- assertThat(view.getAppIconView().drawable).isNotNull()
- }
-
- @Test
- fun setIcon_nullAppIconDrawable_iconIsFromPackageName() {
- underTest.displayView(getState())
- val view = getView()
-
- underTest.setIcon(view, PACKAGE_NAME, appIconDrawableOverride = null, null)
-
- assertThat(view.getAppIconView().drawable).isEqualTo(appIconFromPackageName)
- }
-
- @Test
- fun setIcon_hasAppIconDrawable_iconIsDrawable() {
- underTest.displayView(getState())
- val view = getView()
-
- val drawable = context.getDrawable(R.drawable.ic_alarm)!!
- underTest.setIcon(view, PACKAGE_NAME, drawable, null)
-
- assertThat(view.getAppIconView().drawable).isEqualTo(drawable)
- }
-
- @Test
- fun setIcon_nullAppNameAndNullPackageName_stillHasContentDescription() {
- underTest.displayView(getState())
- val view = getView()
-
- underTest.setIcon(view, appPackageName = null, appNameOverride = null)
-
- assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty()
- }
-
- @Test
- fun setIcon_nullAppNameAndInvalidPackageName_stillHasContentDescription() {
- underTest.displayView(getState())
- val view = getView()
-
- underTest.setIcon(
- view, appPackageName = "fakePackageName", appNameOverride = null
- )
-
- assertThat(view.getAppIconView().contentDescription.toString()).isNotEmpty()
- }
-
- @Test
- fun setIcon_nullAppName_iconContentDescriptionIsFromPackageName() {
- underTest.displayView(getState())
- val view = getView()
-
- underTest.setIcon(view, PACKAGE_NAME, null, appNameOverride = null)
-
- assertThat(view.getAppIconView().contentDescription).isEqualTo(APP_NAME)
- }
-
- @Test
- fun setIcon_hasAppName_iconContentDescriptionIsAppNameOverride() {
- underTest.displayView(getState())
- val view = getView()
-
- val appName = "Override App Name"
- underTest.setIcon(view, PACKAGE_NAME, null, appName)
-
- assertThat(view.getAppIconView().contentDescription).isEqualTo(appName)
- }
-
- @Test
- fun setIcon_iconSizeMatchesGetIconSize() {
- underTest.displayView(getState())
- val view = getView()
-
- underTest.setIcon(view, PACKAGE_NAME)
- view.measure(
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
- View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
- )
-
- assertThat(view.getAppIconView().measuredWidth).isEqualTo(ICON_SIZE)
- assertThat(view.getAppIconView().measuredHeight).isEqualTo(ICON_SIZE)
- }
-
private fun getState(name: String = "name") = ViewInfo(name)
- private fun getView(): ViewGroup {
- val viewCaptor = ArgumentCaptor.forClass(View::class.java)
- verify(windowManager).addView(viewCaptor.capture(), any())
- return viewCaptor.value as ViewGroup
- }
-
- private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
-
private fun getConfigurationListener(): ConfigurationListener {
val callbackCaptor = argumentCaptor<ConfigurationListener>()
verify(configurationController).addCallback(capture(callbackCaptor))
@@ -348,13 +215,13 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
inner class TestController(
context: Context,
- logger: MediaTttLogger,
+ logger: TemporaryViewLogger,
windowManager: WindowManager,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
powerManager: PowerManager,
- ) : TemporaryViewDisplayController<ViewInfo>(
+ ) : TemporaryViewDisplayController<ViewInfo, TemporaryViewLogger>(
context,
logger,
windowManager,
@@ -363,6 +230,8 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
configurationController,
powerManager,
R.layout.media_ttt_chip,
+ "Window Title",
+ "WAKE_REASON",
) {
var mostRecentViewInfo: ViewInfo? = null
@@ -371,7 +240,6 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
super.updateView(newInfo, currentView)
mostRecentViewInfo = newInfo
}
- override fun getIconSize(isAppIcon: Boolean): Int = ICON_SIZE
}
inner class ViewInfo(val name: String) : TemporaryViewInfo {
@@ -379,7 +247,4 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
}
}
-private const val PACKAGE_NAME = "com.android.systemui"
-private const val APP_NAME = "Fake App Name"
private const val TIMEOUT_MS = 10000L
-private const val ICON_SIZE = 47
diff --git a/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
new file mode 100644
index 000000000000..c9f2b4db81ef
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/temporarydisplay/TemporaryViewLoggerTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.temporarydisplay
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
+import com.android.systemui.log.LogcatEchoTracker
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito
+
+@SmallTest
+class TemporaryViewLoggerTest : SysuiTestCase() {
+ private lateinit var buffer: LogBuffer
+ private lateinit var logger: TemporaryViewLogger
+
+ @Before
+ fun setUp() {
+ buffer =
+ LogBufferFactory(DumpManager(), Mockito.mock(LogcatEchoTracker::class.java))
+ .create("buffer", 10)
+ logger = TemporaryViewLogger(buffer, TAG)
+ }
+
+ @Test
+ fun logChipAddition_bufferHasLog() {
+ logger.logChipAddition()
+
+ val stringWriter = StringWriter()
+ buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+ val actualString = stringWriter.toString()
+
+ assertThat(actualString).contains(TAG)
+ }
+
+ @Test
+ fun logChipRemoval_bufferHasTagAndReason() {
+ val reason = "test reason"
+ logger.logChipRemoval(reason)
+
+ val stringWriter = StringWriter()
+ buffer.dump(PrintWriter(stringWriter), tailLength = 0)
+ val actualString = stringWriter.toString()
+
+ assertThat(actualString).contains(TAG)
+ assertThat(actualString).contains(reason)
+ }
+}
+
+private const val TAG = "TestTag"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 6b466e1ac2d8..6fec343d036c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -60,7 +60,7 @@ class UserRepositoryImplTest : SysuiTestCase() {
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- whenever(controller.addUsersFromLockScreen).thenReturn(MutableStateFlow(false))
+ whenever(controller.isAddUsersFromLockScreenEnabled).thenReturn(MutableStateFlow(false))
whenever(controller.isGuestUserAutoCreated).thenReturn(false)
whenever(controller.isGuestUserResetting).thenReturn(false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
index e47acd8d3834..343437634b29 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/ImageWallpaperTest.java
@@ -41,6 +41,7 @@ import android.view.DisplayInfo;
import android.view.SurfaceHolder;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.wallpapers.gl.ImageWallpaperRenderer;
import org.junit.Before;
@@ -72,6 +73,8 @@ public class ImageWallpaperTest extends SysuiTestCase {
private Bitmap mWallpaperBitmap;
@Mock
private Handler mHandler;
+ @Mock
+ private FeatureFlags mFeatureFlags;
private CountDownLatch mEventCountdown;
@@ -100,7 +103,7 @@ public class ImageWallpaperTest extends SysuiTestCase {
}
private ImageWallpaper createImageWallpaper() {
- return new ImageWallpaper() {
+ return new ImageWallpaper(mFeatureFlags) {
@Override
public Engine onCreateEngine() {
return new GLEngine(mHandler) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRendererTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRendererTest.java
new file mode 100644
index 000000000000..93f4f8223955
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallpapers/canvas/ImageCanvasWallpaperRendererTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.wallpapers.canvas;
+
+import static org.hamcrest.Matchers.greaterThanOrEqualTo;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.clearInvocations;
+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.mockito.hamcrest.MockitoHamcrest.intThat;
+
+import android.graphics.Bitmap;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+import android.view.DisplayInfo;
+import android.view.SurfaceHolder;
+
+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)
+@TestableLooper.RunWithLooper
+public class ImageCanvasWallpaperRendererTest extends SysuiTestCase {
+
+ private static final int MOBILE_DISPLAY_WIDTH = 720;
+ private static final int MOBILE_DISPLAY_HEIGHT = 1600;
+
+ @Mock
+ private SurfaceHolder mMockSurfaceHolder;
+
+ @Mock
+ private DisplayInfo mMockDisplayInfo;
+
+ @Mock
+ private Bitmap mMockBitmap;
+
+ @Before
+ public void setUp() throws Exception {
+ allowTestableLooperAsMainThread();
+ MockitoAnnotations.initMocks(this);
+ }
+
+ private void setDimensions(
+ int bitmapWidth, int bitmapHeight,
+ int displayWidth, int displayHeight) {
+ when(mMockBitmap.getWidth()).thenReturn(bitmapWidth);
+ when(mMockBitmap.getHeight()).thenReturn(bitmapHeight);
+ mMockDisplayInfo.logicalWidth = displayWidth;
+ mMockDisplayInfo.logicalHeight = displayHeight;
+ }
+
+ private void testMinDimensions(
+ int bitmapWidth, int bitmapHeight) {
+
+ clearInvocations(mMockSurfaceHolder);
+ setDimensions(bitmapWidth, bitmapHeight,
+ ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_WIDTH,
+ ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_HEIGHT);
+
+ ImageCanvasWallpaperRenderer renderer =
+ new ImageCanvasWallpaperRenderer(mMockSurfaceHolder);
+ renderer.drawFrame(mMockBitmap, true);
+
+ verify(mMockSurfaceHolder, times(1)).setFixedSize(
+ intThat(greaterThanOrEqualTo(ImageCanvasWallpaperRenderer.MIN_SURFACE_WIDTH)),
+ intThat(greaterThanOrEqualTo(ImageCanvasWallpaperRenderer.MIN_SURFACE_HEIGHT)));
+ }
+
+ @Test
+ public void testMinSurface() {
+ // test that the surface is always at least MIN_SURFACE_WIDTH x MIN_SURFACE_HEIGHT
+ testMinDimensions(8, 8);
+
+ testMinDimensions(100, 2000);
+
+ testMinDimensions(200, 1);
+ }
+
+ private void testZeroDimensions(int bitmapWidth, int bitmapHeight) {
+
+ clearInvocations(mMockSurfaceHolder);
+ setDimensions(bitmapWidth, bitmapHeight,
+ ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_WIDTH,
+ ImageCanvasWallpaperRendererTest.MOBILE_DISPLAY_HEIGHT);
+
+ ImageCanvasWallpaperRenderer renderer =
+ new ImageCanvasWallpaperRenderer(mMockSurfaceHolder);
+ ImageCanvasWallpaperRenderer spyRenderer = spy(renderer);
+ spyRenderer.drawFrame(mMockBitmap, true);
+
+ verify(mMockSurfaceHolder, never()).setFixedSize(anyInt(), anyInt());
+ verify(spyRenderer, never()).drawWallpaperWithCanvas(any());
+ }
+
+ @Test
+ public void testZeroBitmap() {
+ // test that updateSurfaceSize is not called with a bitmap of width 0 or height 0
+ testZeroDimensions(
+ 0, 1
+ );
+
+ testZeroDimensions(1, 0
+ );
+
+ testZeroDimensions(0, 0
+ );
+ }
+}
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 da33fa62a9ab..cebe946a459c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java
@@ -34,6 +34,7 @@ import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.tracing.ProtoTracer;
import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.floating.FloatingTasks;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.onehanded.OneHandedEventCallback;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
@@ -74,16 +75,16 @@ public class WMShellTest extends SysuiTestCase {
@Mock ProtoTracer mProtoTracer;
@Mock UserTracker mUserTracker;
@Mock ShellExecutor mSysUiMainExecutor;
+ @Mock FloatingTasks mFloatingTasks;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
-
mWMShell = new WMShell(mContext, mShellInterface, Optional.of(mPip),
- Optional.of(mSplitScreen), Optional.of(mOneHanded), mCommandQueue,
- mConfigurationController, mKeyguardStateController, mKeyguardUpdateMonitor,
- mScreenLifecycle, mSysUiState, mProtoTracer, mWakefulnessLifecycle,
- mUserTracker, mSysUiMainExecutor);
+ Optional.of(mSplitScreen), Optional.of(mOneHanded), Optional.of(mFloatingTasks),
+ mCommandQueue, mConfigurationController, mKeyguardStateController,
+ mKeyguardUpdateMonitor, mScreenLifecycle, mSysUiState, mProtoTracer,
+ mWakefulnessLifecycle, mUserTracker, mSysUiMainExecutor);
}
@Test
diff --git a/packages/VpnDialogs/res/values-ro/strings.xml b/packages/VpnDialogs/res/values-ro/strings.xml
index 5bda87e5c257..94a79090bb51 100644
--- a/packages/VpnDialogs/res/values-ro/strings.xml
+++ b/packages/VpnDialogs/res/values-ro/strings.xml
@@ -17,21 +17,21 @@
<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">"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" 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. Acceptă numai dacă ai încredere în sursă. Când conexiunea VPN e 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>
<string name="data_transmitted" msgid="7988167672982199061">"Trimise:"</string>
<string name="data_received" msgid="4062776929376067820">"Primite:"</string>
- <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> octeți/<xliff:g id="NUMBER_1">%2$s</xliff:g> pachete"</string>
+ <string name="data_value_format" msgid="2192466557826897580">"<xliff:g id="NUMBER_0">%1$s</xliff:g> byți/<xliff:g id="NUMBER_1">%2$s</xliff:g> pachete"</string>
<string name="always_on_disconnected_title" msgid="1906740176262776166">"Nu se poate conecta la rețeaua VPN activată permanent"</string>
- <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> este setată să rămână conectată permanent, dar momentan nu se poate conecta. Telefonul dvs. va folosi o rețea publică până când se va putea reconecta la <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> este setată să rămână conectată permanent, dar momentan nu se poate conecta. Nu veți avea conexiune până când se va putea reconecta rețeaua VPN."</string>
+ <string name="always_on_disconnected_message" msgid="555634519845992917">"<xliff:g id="VPN_APP_0">%1$s</xliff:g> este setată să rămână conectată permanent, dar momentan nu se poate conecta. Telefonul va folosi o rețea publică până când se va putea reconecta la <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> este setată să rămână conectată permanent, dar momentan nu se poate conecta. Nu vei avea conexiune până când se va putea reconecta rețeaua VPN."</string>
<string name="always_on_disconnected_message_separator" msgid="3310614409322581371">" "</string>
- <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Modificați setările VPN"</string>
- <string name="configure" msgid="4905518375574791375">"Configurați"</string>
- <string name="disconnect" msgid="971412338304200056">"Deconectați"</string>
- <string name="open_app" msgid="3717639178595958667">"Deschideți aplicația"</string>
- <string name="dismiss" msgid="6192859333764711227">"Închideți"</string>
+ <string name="always_on_disconnected_message_settings_link" msgid="6172280302829992412">"Modifică setările VPN"</string>
+ <string name="configure" msgid="4905518375574791375">"Configurează"</string>
+ <string name="disconnect" msgid="971412338304200056">"Deconectează"</string>
+ <string name="open_app" msgid="3717639178595958667">"Deschide aplicația"</string>
+ <string name="dismiss" msgid="6192859333764711227">"Închide"</string>
</resources>
diff --git a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
index 6e5947c0d753..b9cc0b066dcf 100644
--- a/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
+++ b/packages/overlays/AvoidAppsInCutoutOverlay/res/values-ro/strings.xml
@@ -17,5 +17,5 @@
<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>
+ <string name="display_cutout_emulation_overlay" msgid="3814493834951357513">"Redă aplicațiile sub zona de decupaj"</string>
</resources>
diff --git a/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
index e6281fd09b1a..2d7aaf6a46d2 100644
--- a/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
+++ b/packages/overlays/NoCutoutOverlay/res/values-ro/strings.xml
@@ -17,5 +17,5 @@
<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>
+ <string name="display_cutout_emulation_overlay" msgid="9031691255599853162">"Ascunde"</string>
</resources>
diff --git a/services/Android.bp b/services/Android.bp
index decfa77e002a..672b3450dc1b 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -89,7 +89,6 @@ filegroup {
":services.autofill-sources",
":services.backup-sources",
":backuplib-sources",
- ":services.cloudsearch-sources",
":services.companion-sources",
":services.contentcapture-sources",
":services.contentsuggestions-sources",
@@ -143,7 +142,6 @@ java_library {
"services.appwidget",
"services.autofill",
"services.backup",
- "services.cloudsearch",
"services.companion",
"services.contentcapture",
"services.contentsuggestions",
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 3324c526ecc2..799c759b96d2 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -75,7 +75,6 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.KeyEvent;
import android.view.MagnificationSpec;
-import android.view.SurfaceControl.ScreenshotHardwareBuffer;
import android.view.View;
import android.view.WindowInfo;
import android.view.accessibility.AccessibilityCache;
@@ -84,6 +83,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.inputmethod.EditorInfo;
+import android.window.ScreenCapture.ScreenshotHardwareBuffer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.compat.IPlatformCompat;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index f55cfb1ab210..f17f8f7bd7e4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -731,7 +731,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- intentFilter.addAction(Intent.ACTION_USER_PRESENT);
intentFilter.addAction(Intent.ACTION_SETTING_RESTORED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@@ -749,14 +748,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
unlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
- } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
- // We will update when the automation service dies.
- synchronized (mLock) {
- AccessibilityUserState userState = getCurrentUserStateLocked();
- if (readConfigurationForUserStateLocked(userState)) {
- onUserStateChangedLocked(userState);
- }
- }
} else if (Intent.ACTION_SETTING_RESTORED.equals(action)) {
final String which = intent.getStringExtra(Intent.EXTRA_SETTING_NAME);
if (Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES.equals(which)) {
@@ -1113,7 +1104,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
final List<AccessibilityServiceInfo> result = new ArrayList<>(serviceCount);
for (int i = 0; i < serviceCount; ++i) {
final AccessibilityServiceConnection service = services.get(i);
- if ((service.mFeedbackType & feedbackType) != 0) {
+ if ((service.mFeedbackType & feedbackType) != 0
+ || feedbackType == AccessibilityServiceInfo.FEEDBACK_ALL_MASK) {
result.add(service.getServiceInfo());
}
}
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 5a35474207f7..cc29109d85c5 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -770,12 +770,14 @@ final class AutofillManagerServiceImpl
/**
* Updates the last fill selection when an authentication was selected.
*/
- void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState) {
+ void setAuthenticationSelected(int sessionId, @Nullable Bundle clientState,
+ int uiType) {
synchronized (mLock) {
if (isValidEventLocked("setAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_AUTHENTICATION_SELECTED, null, clientState, null, null,
- null, null, null, null, null, null));
+ null, null, null, null, null, null,
+ NO_SAVE_UI_REASON_NONE, uiType));
}
}
}
@@ -784,12 +786,13 @@ final class AutofillManagerServiceImpl
* Updates the last fill selection when an dataset authentication was selected.
*/
void logDatasetAuthenticationSelected(@Nullable String selectedDataset, int sessionId,
- @Nullable Bundle clientState) {
+ @Nullable Bundle clientState, int uiType) {
synchronized (mLock) {
if (isValidEventLocked("logDatasetAuthenticationSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_DATASET_AUTHENTICATION_SELECTED, selectedDataset,
- clientState, null, null, null, null, null, null, null, null));
+ clientState, null, null, null, null, null, null, null, null,
+ NO_SAVE_UI_REASON_NONE, uiType));
}
}
}
@@ -810,13 +813,13 @@ final class AutofillManagerServiceImpl
* Updates the last fill response when a dataset was selected.
*/
void logDatasetSelected(@Nullable String selectedDataset, int sessionId,
- @Nullable Bundle clientState, int presentationType) {
+ @Nullable Bundle clientState, int uiType) {
synchronized (mLock) {
if (isValidEventLocked("logDatasetSelected()", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_DATASET_SELECTED, selectedDataset, clientState, null,
null, null, null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
- presentationType));
+ uiType));
}
}
}
@@ -824,13 +827,13 @@ final class AutofillManagerServiceImpl
/**
* Updates the last fill response when a dataset is shown.
*/
- void logDatasetShown(int sessionId, @Nullable Bundle clientState, int presentationType) {
+ void logDatasetShown(int sessionId, @Nullable Bundle clientState, int uiType) {
synchronized (mLock) {
if (isValidEventLocked("logDatasetShown", sessionId)) {
mEventHistory.addEvent(
new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
null, null, null, null, null, NO_SAVE_UI_REASON_NONE,
- presentationType));
+ uiType));
}
}
}
diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
index b5fdaca838df..6bb19ce05813 100644
--- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
+++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java
@@ -32,6 +32,8 @@ import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__UNKNOWN_AUTOFILL_DISPLAY_PRESENTATION_TYPE;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_FILL_REQUEST_FAILED;
+import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_NO_FOCUS;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
import static com.android.internal.util.FrameworkStatsLog.AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
@@ -72,19 +74,32 @@ public final class PresentationStatsEventLogger {
NOT_SHOWN_REASON_VIEW_CHANGED,
NOT_SHOWN_REASON_ACTIVITY_FINISHED,
NOT_SHOWN_REASON_REQUEST_TIMEOUT,
+ NOT_SHOWN_REASON_REQUEST_FAILED,
+ NOT_SHOWN_REASON_NO_FOCUS,
NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY,
NOT_SHOWN_REASON_UNKNOWN
})
@Retention(RetentionPolicy.SOURCE)
public @interface NotShownReason {}
- public static final int NOT_SHOWN_REASON_ANY_SHOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
- public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
- public static final int NOT_SHOWN_REASON_VIEW_CHANGED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
- public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED;
- public static final int NOT_SHOWN_REASON_REQUEST_TIMEOUT = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
- public static final int NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
- public static final int NOT_SHOWN_REASON_UNKNOWN = AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
+ public static final int NOT_SHOWN_REASON_ANY_SHOWN =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__ANY_SHOWN;
+ public static final int NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_FOCUS_CHANGED;
+ public static final int NOT_SHOWN_REASON_VIEW_CHANGED =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_VIEW_CHANGED;
+ public static final int NOT_SHOWN_REASON_ACTIVITY_FINISHED =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_ACTIVITY_FINISHED;
+ public static final int NOT_SHOWN_REASON_REQUEST_TIMEOUT =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_REQUEST_TIMEOUT;
+ public static final int NOT_SHOWN_REASON_REQUEST_FAILED =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_FILL_REQUEST_FAILED;
+ public static final int NOT_SHOWN_REASON_NO_FOCUS =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_NO_FOCUS;
+ public static final int NOT_SHOWN_REASON_SESSION_COMMITTED_PREMATURELY =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_SESSION_COMMITTED_PREMATURELY;
+ public static final int NOT_SHOWN_REASON_UNKNOWN =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__PRESENTATION_EVENT_RESULT__NONE_SHOWN_UNKNOWN_REASON;
private final int mSessionId;
private Optional<PresentationStatsEventInternal> mEventInternal;
@@ -118,6 +133,14 @@ public final class PresentationStatsEventLogger {
});
}
+ public void maybeSetNoPresentationEventReasonIfNoReasonExists(@NotShownReason int reason) {
+ mEventInternal.ifPresent(event -> {
+ if (event.mCountShown == 0 && event.mNoPresentationReason == NOT_SHOWN_REASON_UNKNOWN) {
+ event.mNoPresentationReason = reason;
+ }
+ });
+ }
+
public void maybeSetAvailableCount(@Nullable List<Dataset> datasetList,
AutofillId currentViewId) {
mEventInternal.ifPresent(event -> {
@@ -180,7 +203,8 @@ public final class PresentationStatsEventLogger {
public void maybeSetInlinePresentationAndSuggestionHostUid(Context context, int userId) {
mEventInternal.ifPresent(event -> {
- event.mDisplayPresentationType = UI_TYPE_INLINE;
+ event.mDisplayPresentationType =
+ AUTOFILL_PRESENTATION_EVENT_REPORTED__DISPLAY_PRESENTATION_TYPE__INLINE;
String imeString = Settings.Secure.getStringForUser(context.getContentResolver(),
Settings.Secure.DEFAULT_INPUT_METHOD, userId);
if (TextUtils.isEmpty(imeString)) {
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d5945a5cb255..47ce5928c0be 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -44,6 +44,8 @@ import static com.android.server.autofill.Helper.getNumericValue;
import static com.android.server.autofill.Helper.sDebug;
import static com.android.server.autofill.Helper.sVerbose;
import static com.android.server.autofill.Helper.toArray;
+import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_NO_FOCUS;
+import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_FAILED;
import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_REQUEST_TIMEOUT;
import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_VIEW_CHANGED;
import static com.android.server.autofill.PresentationStatsEventLogger.NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED;
@@ -407,6 +409,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private PresentationStatsEventLogger mPresentationStatsEventLogger;
+ /**
+ * Fill dialog request would likely be sent slightly later.
+ */
+ @NonNull
+ @GuardedBy("mLock")
+ private boolean mPreviouslyFillDialogPotentiallyStarted;
+
void onSwitchInputMethodLocked() {
// One caveat is that for the case where the focus is on a field for which regular autofill
// returns null, and augmented autofill is triggered, and then the user switches the input
@@ -1179,6 +1188,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@Override
public void onFillRequestSuccess(int requestId, @Nullable FillResponse response,
@NonNull String servicePackageName, int requestFlags) {
+
final AutofillId[] fieldClassificationIds;
final LogMaker requestLog;
@@ -1357,9 +1367,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- // TODO(b/234185326): Add separate reason for failures.
- mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
- NOT_SHOWN_REASON_REQUEST_TIMEOUT);
+ if (timedOut) {
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ NOT_SHOWN_REASON_REQUEST_TIMEOUT);
+ } else {
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ NOT_SHOWN_REASON_REQUEST_FAILED);
+ }
mPresentationStatsEventLogger.logAndEndEvent();
}
notifyUnavailableToClient(AutofillManager.STATE_UNKNOWN_FAILED,
@@ -1468,7 +1482,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// AutoFillUiCallback
@Override
public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras,
- boolean authenticateInline) {
+ int uiType) {
if (sDebug) {
Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex
+ "; intentSender=" + intent);
@@ -1487,12 +1501,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- mService.setAuthenticationSelected(id, mClientState);
+ mService.setAuthenticationSelected(id, mClientState, uiType);
final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
mHandler.sendMessage(obtainMessage(
Session::startAuthentication,
- this, authenticationId, intent, fillInIntent, authenticateInline));
+ this, authenticationId, intent, fillInIntent,
+ /* authenticateInline= */ uiType == UI_TYPE_INLINE));
}
// AutoFillUiCallback
@@ -2887,6 +2902,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
@GuardedBy("mLock")
private boolean requestNewFillResponseOnViewEnteredIfNecessaryLocked(@NonNull AutofillId id,
@NonNull ViewState viewState, int flags) {
+ // Force new response for manual request
if ((flags & FLAG_MANUAL_REQUEST) != 0) {
mSessionFlags.mAugmentedAutofillOnly = false;
if (sDebug) Slog.d(TAG, "Re-starting session on view " + id + " and flags " + flags);
@@ -2894,7 +2910,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return true;
}
- // If it's not, then check if it it should start a partition.
+ // If it's not, then check if it should start a partition.
if (shouldStartNewPartitionLocked(id)) {
if (sDebug) {
Slog.d(TAG, "Starting partition or augmented request for view id " + id + ": "
@@ -2993,7 +3009,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (sDebug) {
Slog.d(TAG, "Set the response has expired.");
}
- mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReasonIfNoReasonExists(
NOT_SHOWN_REASON_VIEW_CHANGED);
mPresentationStatsEventLogger.logAndEndEvent();
return;
@@ -3038,11 +3054,20 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
// View is triggering autofill.
mCurrentViewId = viewState.id;
viewState.update(value, virtualBounds, flags);
+ mPresentationStatsEventLogger.startNewEvent();
+ mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
+ mPresentationStatsEventLogger.maybeSetIsNewRequest(true);
if (!isRequestSupportFillDialog(flags)) {
mSessionFlags.mFillDialogDisabled = true;
+ mPreviouslyFillDialogPotentiallyStarted = false;
+ } else {
+ // Set the default reason for now if the user doesn't trigger any focus event
+ // on the autofillable view. This can be changed downstream when more
+ // information is available or session is committed.
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ NOT_SHOWN_REASON_NO_FOCUS);
+ mPreviouslyFillDialogPotentiallyStarted = true;
}
- mPresentationStatsEventLogger.startNewEvent();
- mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
requestNewFillResponseLocked(viewState, ViewState.STATE_STARTED_SESSION, flags);
break;
case ACTION_VALUE_CHANGED:
@@ -3085,6 +3110,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
break;
case ACTION_VIEW_ENTERED:
+ boolean wasPreviouslyFillDialog = mPreviouslyFillDialogPotentiallyStarted;
+ mPreviouslyFillDialogPotentiallyStarted = false;
if (sVerbose && virtualBounds != null) {
Slog.v(TAG, "entered on virtual child " + id + ": " + virtualBounds);
}
@@ -3101,9 +3128,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
return;
}
- mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
- NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
- mPresentationStatsEventLogger.logAndEndEvent();
+ // Previously, fill request will only start whenever a view is entered.
+ // With Fill Dialog, request starts prior to view getting entered. So, we can't end
+ // the event at this moment, otherwise we will be wrongly attributing fill dialog
+ // event as concluded.
+ if (!wasPreviouslyFillDialog) {
+ mPresentationStatsEventLogger.maybeSetNoPresentationEventReason(
+ NOT_SHOWN_REASON_VIEW_FOCUS_CHANGED);
+ mPresentationStatsEventLogger.logAndEndEvent();
+ }
if ((flags & FLAG_MANUAL_REQUEST) == 0) {
// Not a manual request
@@ -3131,9 +3164,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
}
- mPresentationStatsEventLogger.startNewEvent();
- mPresentationStatsEventLogger.maybeSetAutofillServiceUid(getAutofillServiceUid());
+ if (!wasPreviouslyFillDialog) {
+ mPresentationStatsEventLogger.startNewEvent();
+ mPresentationStatsEventLogger.maybeSetAutofillServiceUid(
+ getAutofillServiceUid());
+ }
if (requestNewFillResponseOnViewEnteredIfNecessaryLocked(id, viewState, flags)) {
+ // If a new request was issued even if previously it was fill dialog request,
+ // we should end the log event, and start a new one. However, it leaves us
+ // susceptible to race condition. But since mPresentationStatsEventLogger is
+ // lock guarded, we should be safe.
+ if (wasPreviouslyFillDialog) {
+ mPresentationStatsEventLogger.logAndEndEvent();
+ mPresentationStatsEventLogger.startNewEvent();
+ mPresentationStatsEventLogger.maybeSetAutofillServiceUid(
+ getAutofillServiceUid());
+ }
return;
}
@@ -3369,7 +3415,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
if (requestShowInlineSuggestionsLocked(response, filterText)) {
final ViewState currentView = mViewStates.get(mCurrentViewId);
currentView.setState(ViewState.STATE_INLINE_SHOWN);
- // TODO(b/137800469): Fix it to log showed only when IME asks for inflation,
+ // TODO(b/248378401): Fix it to log showed only when IME asks for inflation,
// rather than here where framework sends back the response.
mService.logDatasetShown(id, mClientState, UI_TYPE_INLINE);
@@ -3390,7 +3436,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
synchronized (mLock) {
mService.logDatasetShown(id, mClientState, UI_TYPE_MENU);
-
mPresentationStatsEventLogger.maybeSetCountShown(
response.getDatasets(), mCurrentViewId);
mPresentationStatsEventLogger.maybeSetDisplayPresentationType(UI_TYPE_MENU);
@@ -3547,7 +3592,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
public void authenticate(int requestId, int datasetIndex) {
Session.this.authenticate(response.getRequestId(), datasetIndex,
response.getAuthentication(), response.getClientState(),
- /* authenticateInline= */ true);
+ UI_TYPE_INLINE);
}
@Override
@@ -4087,7 +4132,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState
}
// ...or handle authentication.
- mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState);
+ mService.logDatasetAuthenticationSelected(dataset.getId(), id, mClientState, uiType);
setViewStatesLocked(null, dataset, ViewState.STATE_WAITING_DATASET_AUTH, false);
final Intent fillInIntent = createAuthFillInIntentLocked(requestId, mClientState);
if (fillInIntent == null) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index e07f41204500..5f0f9a3f1577 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -84,7 +84,7 @@ public final class AutoFillUI {
public interface AutoFillUiCallback {
void authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent,
- @Nullable Bundle extras, boolean authenticateInline);
+ @Nullable Bundle extras, int uiType);
void fill(int requestId, int datasetIndex, @NonNull Dataset dataset,
@FillEventHistory.Event.UiType int uiType);
void save();
@@ -232,7 +232,7 @@ public final class AutoFillUI {
mCallback.authenticate(response.getRequestId(),
AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED,
response.getAuthentication(), response.getClientState(),
- /* authenticateInline= */ false);
+ UI_TYPE_MENU);
}
}
@@ -419,7 +419,7 @@ public final class AutoFillUI {
mCallback.authenticate(response.getRequestId(),
AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED,
response.getAuthentication(), response.getClientState(),
- /* authenticateInline= */ false);
+ UI_TYPE_DIALOG);
}
}
diff --git a/services/cloudsearch/Android.bp b/services/cloudsearch/Android.bp
deleted file mode 100644
index e38e6153016f..000000000000
--- a/services/cloudsearch/Android.bp
+++ /dev/null
@@ -1,22 +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.cloudsearch-sources",
- srcs: ["java/**/*.java"],
- path: "java",
- visibility: ["//frameworks/base/services"],
-}
-
-java_library_static {
- name: "services.cloudsearch",
- defaults: ["platform_service_defaults"],
- srcs: [":services.cloudsearch-sources"],
- libs: ["services.core"],
-}
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
deleted file mode 100644
index ac2d1dd95da1..000000000000
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerService.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.cloudsearch;
-
-import static android.Manifest.permission.MANAGE_CLOUDSEARCH;
-import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
-import static android.content.Context.CLOUDSEARCH_SERVICE;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.app.ActivityManagerInternal;
-import android.app.cloudsearch.ICloudSearchManager;
-import android.app.cloudsearch.ICloudSearchManagerCallback;
-import android.app.cloudsearch.SearchRequest;
-import android.app.cloudsearch.SearchResponse;
-import android.content.Context;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.ResultReceiver;
-import android.os.ShellCallback;
-import android.util.Slog;
-
-import com.android.internal.R;
-import com.android.server.LocalServices;
-import com.android.server.infra.AbstractMasterSystemService;
-import com.android.server.infra.FrameworkResourcesServiceNameResolver;
-import com.android.server.wm.ActivityTaskManagerInternal;
-
-import java.io.FileDescriptor;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Consumer;
-
-/**
- * A service used to return cloudsearch targets given a query.
- */
-public class CloudSearchManagerService extends
- AbstractMasterSystemService<CloudSearchManagerService, CloudSearchPerUserService> {
-
- private static final String TAG = CloudSearchManagerService.class.getSimpleName();
- private static final boolean DEBUG = false;
-
- private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
-
- private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
-
- private final Context mContext;
-
- public CloudSearchManagerService(Context context) {
- super(context, new FrameworkResourcesServiceNameResolver(context,
- R.array.config_defaultCloudSearchServices, true), null,
- PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
- mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
- mContext = context;
- }
-
- @Override
- protected CloudSearchPerUserService newServiceLocked(int resolvedUserId, boolean disabled) {
- return new CloudSearchPerUserService(this, mLock, resolvedUserId, "");
- }
-
- @Override
- protected List<CloudSearchPerUserService> newServiceListLocked(int resolvedUserId,
- boolean disabled, String[] serviceNames) {
- if (serviceNames == null) {
- return new ArrayList<>();
- }
- List<CloudSearchPerUserService> serviceList =
- new ArrayList<>(serviceNames.length);
- for (int i = 0; i < serviceNames.length; i++) {
- if (serviceNames[i] == null) {
- continue;
- }
- serviceList.add(new CloudSearchPerUserService(this, mLock, resolvedUserId,
- serviceNames[i]));
- }
- return serviceList;
- }
-
- @Override
- public void onStart() {
- publishBinderService(CLOUDSEARCH_SERVICE, new CloudSearchManagerStub());
- }
-
- @Override
- protected void enforceCallingPermissionForManagement() {
- getContext().enforceCallingPermission(MANAGE_CLOUDSEARCH, TAG);
- }
-
- @Override // from AbstractMasterSystemService
- protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
- final CloudSearchPerUserService service = peekServiceForUserLocked(userId);
- if (service != null) {
- service.onPackageUpdatedLocked();
- }
- }
-
- @Override // from AbstractMasterSystemService
- protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
- final CloudSearchPerUserService service = peekServiceForUserLocked(userId);
- if (service != null) {
- service.onPackageRestartedLocked();
- }
- }
-
- @Override
- protected int getMaximumTemporaryServiceDurationMs() {
- return MAX_TEMP_SERVICE_DURATION_MS;
- }
-
- private class CloudSearchManagerStub extends ICloudSearchManager.Stub {
-
- @Override
- public void search(@NonNull SearchRequest searchRequest,
- @NonNull ICloudSearchManagerCallback callBack) {
- searchRequest.setCallerPackageName(
- mContext.getPackageManager().getNameForUid(Binder.getCallingUid()));
- runForUser("search", (service) -> {
- synchronized (service.mLock) {
- service.onSearchLocked(searchRequest, callBack);
- }
- });
- }
-
- @Override
- public void returnResults(IBinder token, String requestId, SearchResponse response) {
- runForUser("returnResults", (service) -> {
- synchronized (service.mLock) {
- service.onReturnResultsLocked(token, requestId, response);
- }
- });
- }
-
- public void destroy(@NonNull SearchRequest searchRequest) {
- runForUser("destroyCloudSearchSession", (service) -> {
- synchronized (service.mLock) {
- service.onDestroyLocked(searchRequest.getRequestId());
- }
- });
- }
-
- public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
- @Nullable FileDescriptor err,
- @NonNull String[] args, @Nullable ShellCallback callback,
- @NonNull ResultReceiver resultReceiver) {
- new CloudSearchManagerServiceShellCommand(CloudSearchManagerService.this)
- .exec(this, in, out, err, args, callback, resultReceiver);
- }
-
- private void runForUser(@NonNull final String func,
- @NonNull final Consumer<CloudSearchPerUserService> c) {
- ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
- final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- Binder.getCallingUserHandle().getIdentifier(), false, ALLOW_NON_FULL,
- null, null);
-
- if (DEBUG) {
- Slog.d(TAG, "runForUser:" + func + " from pid=" + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- }
- Context ctx = getContext();
- if (!(ctx.checkCallingPermission(MANAGE_CLOUDSEARCH) == PERMISSION_GRANTED
- || mServiceNameResolver.isTemporary(userId)
- || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
-
- String msg = "Permission Denial: Cannot call " + func + " from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
- Slog.w(TAG, msg);
- throw new SecurityException(msg);
- }
-
- final long origId = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- final List<CloudSearchPerUserService> services =
- getServiceListForUserLocked(userId);
- for (int i = 0; i < services.size(); i++) {
- c.accept(services.get(i));
- }
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
- }
-}
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
deleted file mode 100644
index c64982d94d6d..000000000000
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchManagerServiceShellCommand.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.cloudsearch;
-
-import android.annotation.NonNull;
-import android.os.ShellCommand;
-
-import java.io.PrintWriter;
-
-/**
- * The shell command implementation for the CloudSearchManagerService.
- */
-public class CloudSearchManagerServiceShellCommand extends ShellCommand {
-
- private static final String TAG =
- CloudSearchManagerServiceShellCommand.class.getSimpleName();
-
- private final CloudSearchManagerService mService;
-
- public CloudSearchManagerServiceShellCommand(@NonNull CloudSearchManagerService service) {
- mService = service;
- }
-
- @Override
- public int onCommand(String cmd) {
- if (cmd == null) {
- return handleDefaultCommands(cmd);
- }
- final PrintWriter pw = getOutPrintWriter();
- switch (cmd) {
- case "set": {
- final String what = getNextArgRequired();
- switch (what) {
- case "temporary-service": {
- final int userId = Integer.parseInt(getNextArgRequired());
- String serviceName = getNextArg();
- if (serviceName == null) {
- mService.resetTemporaryService(userId);
- pw.println("CloudSearchService temporarily reset. ");
- return 0;
- }
- final int duration = Integer.parseInt(getNextArgRequired());
- String[] services = serviceName.split(";");
- if (services.length == 0) {
- return 0;
- } else {
- mService.setTemporaryServices(userId, services, duration);
- }
- pw.println("CloudSearchService temporarily set to " + serviceName
- + " for " + duration + "ms");
- break;
- }
- }
- }
- break;
- default:
- return handleDefaultCommands(cmd);
- }
- return 0;
- }
-
- @Override
- public void onHelp() {
- try (PrintWriter pw = getOutPrintWriter()) {
- pw.println("CloudSearchManagerService commands:");
- pw.println(" help");
- pw.println(" Prints this help text.");
- pw.println("");
- pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]");
- pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
- pw.println(" To reset, call with just the USER_ID argument.");
- pw.println("");
- }
- }
-}
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java b/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
deleted file mode 100644
index 222d779f20e8..000000000000
--- a/services/cloudsearch/java/com/android/server/cloudsearch/CloudSearchPerUserService.java
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.cloudsearch;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.AppGlobals;
-import android.app.cloudsearch.ICloudSearchManagerCallback;
-import android.app.cloudsearch.SearchRequest;
-import android.app.cloudsearch.SearchResponse;
-import android.content.ComponentName;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ServiceInfo;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.service.cloudsearch.CloudSearchService;
-import android.service.cloudsearch.ICloudSearchService;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.infra.AbstractRemoteService;
-import com.android.server.CircularQueue;
-import com.android.server.infra.AbstractPerUserSystemService;
-
-/**
- * Per-user instance of {@link CloudSearchManagerService}.
- */
-public class CloudSearchPerUserService extends
- AbstractPerUserSystemService<CloudSearchPerUserService, CloudSearchManagerService>
- implements RemoteCloudSearchService.RemoteCloudSearchServiceCallbacks {
-
- private static final String TAG = CloudSearchPerUserService.class.getSimpleName();
- private static final int QUEUE_SIZE = 10;
- @GuardedBy("mLock")
- private final CircularQueue<String, CloudSearchCallbackInfo> mCallbackQueue =
- new CircularQueue<>(QUEUE_SIZE);
- private final String mServiceName;
- private final ComponentName mRemoteComponentName;
- @Nullable
- @GuardedBy("mLock")
- private RemoteCloudSearchService mRemoteService;
- /**
- * When {@code true}, remote service died but service state is kept so it's restored after
- * the system re-binds to it.
- */
- @GuardedBy("mLock")
- private boolean mZombie;
-
- protected CloudSearchPerUserService(CloudSearchManagerService master,
- Object lock, int userId, String serviceName) {
- super(master, lock, userId);
- mServiceName = serviceName;
- mRemoteComponentName = ComponentName.unflattenFromString(mServiceName);
- }
-
- @Override // from PerUserSystemService
- protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
- throws NameNotFoundException {
-
- ServiceInfo si;
- try {
- si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
- PackageManager.GET_META_DATA, mUserId);
- } catch (RemoteException e) {
- throw new NameNotFoundException("Could not get service for " + serviceComponent);
- }
- // TODO(b/177858728): must check that either the service is from a system component,
- // or it matches a service set by shell cmd (so it can be used on CTS tests and when
- // OEMs are implementing the real service and also verify the proper permissions
- return si;
- }
-
- @GuardedBy("mLock")
- @Override // from PerUserSystemService
- protected boolean updateLocked(boolean disabled) {
- final boolean enabledChanged = super.updateLocked(disabled);
- if (enabledChanged) {
- if (isEnabledLocked()) {
- // Send the pending sessions over to the service
- resurrectSessionsLocked();
- } else {
- // Clear the remote service for the next call
- updateRemoteServiceLocked();
- }
- }
- return enabledChanged;
- }
-
- /**
- * Notifies the service of a new cloudsearch session.
- */
- @GuardedBy("mLock")
- public void onSearchLocked(@NonNull SearchRequest searchRequest,
- @NonNull ICloudSearchManagerCallback callback) {
- if (mRemoteComponentName == null) {
- return;
- }
-
- String filterList = searchRequest.getSearchConstraints().containsKey(
- SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER)
- ? searchRequest.getSearchConstraints().getString(
- SearchRequest.CONSTRAINT_SEARCH_PROVIDER_FILTER) : "";
-
- String remoteServicePackageName = mRemoteComponentName.getPackageName();
- // By default, all providers are marked as wanted.
- boolean wantedProvider = true;
- if (filterList.length() > 0) {
- // If providers are specified by the client,
- wantedProvider = false;
- String[] providersSpecified = filterList.split(";");
- for (int i = 0; i < providersSpecified.length; i++) {
- if (providersSpecified[i].equals(remoteServicePackageName)) {
- wantedProvider = true;
- break;
- }
- }
- }
- // If the provider was not requested by the Client, the request will not be sent to the
- // provider.
- if (!wantedProvider) {
- // TODO(216520546) Send a failure callback to the client.
- return;
- }
- final boolean serviceExists = resolveService(searchRequest,
- s -> s.onSearch(searchRequest));
- String requestId = searchRequest.getRequestId();
- if (serviceExists && !mCallbackQueue.containsKey(requestId)) {
- final CloudSearchCallbackInfo sessionInfo = new CloudSearchCallbackInfo(
- requestId, searchRequest, callback, callback.asBinder(), () -> {
- synchronized (mLock) {
- onDestroyLocked(requestId);
- }
- });
- if (sessionInfo.linkToDeath()) {
- CloudSearchCallbackInfo removedInfo = mCallbackQueue.put(requestId, sessionInfo);
- if (removedInfo != null) {
- removedInfo.destroy();
- }
- } else {
- // destroy the session if calling process is already dead
- onDestroyLocked(requestId);
- }
- }
- }
-
- /**
- * Used to return results back to the clients.
- */
- @GuardedBy("mLock")
- public void onReturnResultsLocked(@NonNull IBinder token,
- @NonNull String requestId,
- @NonNull SearchResponse response) {
- if (mRemoteService == null) {
- return;
- }
- ICloudSearchService serviceInterface = mRemoteService.getServiceInterface();
- if (serviceInterface == null || token != serviceInterface.asBinder()) {
- return;
- }
- if (mCallbackQueue.containsKey(requestId)) {
- response.setSource(mServiceName);
- final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.getElement(requestId);
- try {
- if (response.getStatusCode() == SearchResponse.SEARCH_STATUS_OK) {
- sessionInfo.mCallback.onSearchSucceeded(response);
- } else {
- sessionInfo.mCallback.onSearchFailed(response);
- }
- } catch (RemoteException e) {
- if (mMaster.debug) {
- Slog.e(TAG, "Exception in posting results");
- e.printStackTrace();
- }
- onDestroyLocked(requestId);
- }
- }
- }
-
- /**
- * Notifies the server about the end of an existing cloudsearch session.
- */
- @GuardedBy("mLock")
- public void onDestroyLocked(@NonNull String requestId) {
- if (isDebug()) {
- Slog.d(TAG, "onDestroyLocked(): requestId=" + requestId);
- }
- final CloudSearchCallbackInfo sessionInfo = mCallbackQueue.removeElement(requestId);
- if (sessionInfo != null) {
- sessionInfo.destroy();
- }
- }
-
- @Override
- public void onFailureOrTimeout(boolean timedOut) {
- if (isDebug()) {
- Slog.d(TAG, "onFailureOrTimeout(): timed out=" + timedOut);
- }
- // Do nothing, we are just proxying to the cloudsearch service
- }
-
- @Override
- public void onConnectedStateChanged(boolean connected) {
- if (isDebug()) {
- Slog.d(TAG, "onConnectedStateChanged(): connected=" + connected);
- }
- if (connected) {
- synchronized (mLock) {
- if (mZombie) {
- // Validation check - shouldn't happen
- if (mRemoteService == null) {
- Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
- return;
- }
- mZombie = false;
- resurrectSessionsLocked();
- }
- }
- }
- }
-
- @Override
- public void onServiceDied(RemoteCloudSearchService service) {
- if (isDebug()) {
- Slog.w(TAG, "onServiceDied(): service=" + service);
- }
- synchronized (mLock) {
- mZombie = true;
- }
- updateRemoteServiceLocked();
- }
-
- @GuardedBy("mLock")
- private void updateRemoteServiceLocked() {
- if (mRemoteService != null) {
- mRemoteService.destroy();
- mRemoteService = null;
- }
- }
-
- void onPackageUpdatedLocked() {
- if (isDebug()) {
- Slog.v(TAG, "onPackageUpdatedLocked()");
- }
- destroyAndRebindRemoteService();
- }
-
- void onPackageRestartedLocked() {
- if (isDebug()) {
- Slog.v(TAG, "onPackageRestartedLocked()");
- }
- destroyAndRebindRemoteService();
- }
-
- private void destroyAndRebindRemoteService() {
- if (mRemoteService == null) {
- return;
- }
-
- if (isDebug()) {
- Slog.d(TAG, "Destroying the old remote service.");
- }
- mRemoteService.destroy();
- mRemoteService = null;
-
- synchronized (mLock) {
- mZombie = true;
- }
- mRemoteService = getRemoteServiceLocked();
- if (mRemoteService != null) {
- if (isDebug()) {
- Slog.d(TAG, "Rebinding to the new remote service.");
- }
- mRemoteService.reconnect();
- }
- }
-
- /**
- * Called after the remote service connected, it's used to restore state from a 'zombie'
- * service (i.e., after it died).
- */
- private void resurrectSessionsLocked() {
- final int numCallbacks = mCallbackQueue.size();
- if (isDebug()) {
- Slog.d(TAG, "Resurrecting remote service (" + mRemoteService + ") on "
- + numCallbacks + " requests.");
- }
-
- for (CloudSearchCallbackInfo callbackInfo : mCallbackQueue.values()) {
- callbackInfo.resurrectSessionLocked(this, callbackInfo.mToken);
- }
- }
-
- @GuardedBy("mLock")
- @Nullable
- protected boolean resolveService(
- @NonNull final SearchRequest requestId,
- @NonNull final AbstractRemoteService.AsyncRequest<ICloudSearchService> cb) {
-
- final RemoteCloudSearchService service = getRemoteServiceLocked();
- if (service != null) {
- service.executeOnResolvedService(cb);
- }
- return service != null;
- }
-
- @GuardedBy("mLock")
- @Nullable
- private RemoteCloudSearchService getRemoteServiceLocked() {
- if (mRemoteService == null) {
- final String serviceName = getComponentNameForMultipleLocked(mServiceName);
- if (serviceName == null) {
- if (mMaster.verbose) {
- Slog.v(TAG, "getRemoteServiceLocked(): not set");
- }
- return null;
- }
- ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
-
- mRemoteService = new RemoteCloudSearchService(getContext(),
- CloudSearchService.SERVICE_INTERFACE, serviceComponent, mUserId, this,
- mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
- }
-
- return mRemoteService;
- }
-
- private static final class CloudSearchCallbackInfo {
- private static final boolean DEBUG = false; // Do not submit with true
- @NonNull
- final IBinder mToken;
- @NonNull
- final IBinder.DeathRecipient mDeathRecipient;
- @NonNull
- private final String mRequestId;
- @NonNull
- private final SearchRequest mSearchRequest;
- private final ICloudSearchManagerCallback mCallback;
-
- CloudSearchCallbackInfo(
- @NonNull final String id,
- @NonNull final SearchRequest request,
- @NonNull final ICloudSearchManagerCallback callback,
- @NonNull final IBinder token,
- @NonNull final IBinder.DeathRecipient deathRecipient) {
- if (DEBUG) {
- Slog.d(TAG, "Creating CloudSearchSessionInfo for session Id=" + id);
- }
- mRequestId = id;
- mSearchRequest = request;
- mCallback = callback;
- mToken = token;
- mDeathRecipient = deathRecipient;
- }
-
- boolean linkToDeath() {
- try {
- mToken.linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException e) {
- if (DEBUG) {
- Slog.w(TAG, "Caller is dead before session can be started, requestId: "
- + mRequestId);
- }
- return false;
- }
- return true;
- }
-
- void destroy() {
- if (DEBUG) {
- Slog.d(TAG, "Removing callback for Request Id=" + mRequestId);
- }
- if (mToken != null) {
- mToken.unlinkToDeath(mDeathRecipient, 0);
- }
- mCallback.asBinder().unlinkToDeath(mDeathRecipient, 0);
- }
-
- void resurrectSessionLocked(CloudSearchPerUserService service, IBinder token) {
- if (DEBUG) {
- Slog.d(TAG, "Resurrecting remote service (" + service.getRemoteServiceLocked()
- + ") for request Id=" + mRequestId);
- }
- service.onSearchLocked(mSearchRequest, mCallback);
- }
- }
-}
diff --git a/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java b/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java
deleted file mode 100644
index d1c0482b4be6..000000000000
--- a/services/cloudsearch/java/com/android/server/cloudsearch/RemoteCloudSearchService.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS 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.cloudsearch;
-
-import android.annotation.NonNull;
-import android.content.ComponentName;
-import android.content.Context;
-import android.os.IBinder;
-import android.service.cloudsearch.ICloudSearchService;
-import android.text.format.DateUtils;
-
-import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
-
-
-/**
- * Proxy to the {@link android.service.cloudsearch.CloudSearchService} implementation in another
- * process.
- */
-public class RemoteCloudSearchService extends
- AbstractMultiplePendingRequestsRemoteService<RemoteCloudSearchService,
- ICloudSearchService> {
-
- private static final String TAG = "RemoteCloudSearchService";
-
- private static final long TIMEOUT_IDLE_BOUND_TIMEOUT_MS = 10 * DateUtils.MINUTE_IN_MILLIS;
- private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
-
- private final RemoteCloudSearchServiceCallbacks mCallback;
-
- public RemoteCloudSearchService(Context context, String serviceInterface,
- ComponentName componentName, int userId,
- RemoteCloudSearchServiceCallbacks callback, boolean bindInstantServiceAllowed,
- boolean verbose) {
- super(context, serviceInterface, componentName, userId, callback,
- context.getMainThreadHandler(),
- bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
- verbose, /* initialCapacity= */ 1);
- mCallback = callback;
- }
-
- @Override
- protected ICloudSearchService getServiceInterface(IBinder service) {
- return ICloudSearchService.Stub.asInterface(service);
- }
-
- @Override
- protected long getTimeoutIdleBindMillis() {
- return TIMEOUT_IDLE_BOUND_TIMEOUT_MS;
- }
-
- @Override
- protected long getRemoteRequestMillis() {
- return TIMEOUT_REMOTE_REQUEST_MILLIS;
- }
-
- /**
- * Schedules a request to bind to the remote service.
- */
- public void reconnect() {
- super.scheduleBind();
- }
-
- /**
- * Schedule async request on remote service.
- */
- public void scheduleOnResolvedService(@NonNull AsyncRequest<ICloudSearchService> request) {
- scheduleAsyncRequest(request);
- }
-
- /**
- * Execute async request on remote service immediately instead of sending it to Handler queue.
- */
- public void executeOnResolvedService(@NonNull AsyncRequest<ICloudSearchService> request) {
- executeAsyncRequest(request);
- }
-
- /**
- * Failure callback
- */
- public interface RemoteCloudSearchServiceCallbacks
- extends VultureCallback<RemoteCloudSearchService> {
-
- /**
- * Notifies a the failure or timeout of a remote call.
- */
- void onFailureOrTimeout(boolean timedOut);
-
- /**
- * Notifies change in connected state of the remote service.
- */
- void onConnectedStateChanged(boolean connected);
- }
-
- @Override // from AbstractRemoteService
- protected void handleOnConnectedStateChanged(boolean connected) {
- if (mCallback != null) {
- mCallback.onConnectedStateChanged(connected);
- }
- }
-}
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
index 93cc611c803f..82628a6c0b72 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceServiceConnector.java
@@ -46,6 +46,9 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe
private static final String TAG = "CDM_CompanionServiceConnector";
private static final boolean DEBUG = false;
+ /* Unbinding before executing the callbacks can cause problems. Wait 5-seconds before unbind. */
+ private static final long UNBIND_POST_DELAY_MS = 5_000;
+
/** Listener for changes to the state of the {@link CompanionDeviceServiceConnector} */
interface Listener {
void onBindingDied(@UserIdInt int userId, @NonNull String packageName,
@@ -110,9 +113,15 @@ class CompanionDeviceServiceConnector extends ServiceConnector.Impl<ICompanionDe
* IMPORTANT: use this method instead of invoking {@link ServiceConnector#unbind()} directly,
* because the latter may cause previously posted callback, such as
* {@link ICompanionDeviceService#onDeviceDisappeared(AssociationInfo)} to be dropped.
+ *
+ * {@link ICompanionDeviceService} is a non-blocking interface and doesn't wait for job
+ * completion, which makes {@link ServiceConnector#post(VoidJob)} obsolete for ensuring the
+ * order of execution. Give 5 seconds for all the callbacks to finish before unbinding. They
+ * may or may not have finished executing, but we shouldn't let user-overridden methods block
+ * the service from unbinding indefinitely.
*/
void postUnbind() {
- post(it -> unbind());
+ getJobHandler().postDelayed(this::unbind, UNBIND_POST_DELAY_MS);
}
boolean isPrimary() {
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index 0bba3c9cba6b..9f27f721ea83 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -150,8 +150,9 @@ public class SystemDataTransferProcessor {
// Create a PendingIntent
final long token = Binder.clearCallingIdentity();
try {
- return PendingIntent.getActivity(mContext, /*requestCode */ associationId, intent,
- FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE);
+ return PendingIntent.getActivityAsUser(mContext, /*requestCode */ associationId, intent,
+ FLAG_ONE_SHOT | FLAG_CANCEL_CURRENT | FLAG_IMMUTABLE, /* options= */ null,
+ UserHandle.CURRENT);
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
index ec0da490adcf..2904f28fca01 100644
--- a/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
+++ b/services/companion/java/com/android/server/companion/virtual/CameraAccessController.java
@@ -22,14 +22,19 @@ import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraInjectionSession;
import android.hardware.camera2.CameraManager;
+import android.os.Process;
+import android.os.UserManager;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import java.util.List;
import java.util.Set;
/**
@@ -45,6 +50,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen
private final CameraAccessBlockedCallback mBlockedCallback;
private final CameraManager mCameraManager;
private final PackageManager mPackageManager;
+ private final UserManager mUserManager;
@GuardedBy("mLock")
private int mObserverCount = 0;
@@ -66,7 +72,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen
static class OpenCameraInfo {
public String packageName;
- public int packageUid;
+ public Set<Integer> packageUids;
}
interface CameraAccessBlockedCallback {
@@ -85,6 +91,7 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen
mBlockedCallback = blockedCallback;
mCameraManager = mContext.getSystemService(CameraManager.class);
mPackageManager = mContext.getPackageManager();
+ mUserManager = mContext.getSystemService(UserManager.class);
}
/**
@@ -125,16 +132,18 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen
for (int i = 0; i < mAppsToBlockOnVirtualDevice.size(); i++) {
final String cameraId = mAppsToBlockOnVirtualDevice.keyAt(i);
final OpenCameraInfo openCameraInfo = mAppsToBlockOnVirtualDevice.get(cameraId);
- int packageUid = openCameraInfo.packageUid;
- if (runningUids.contains(packageUid)) {
- final String packageName = openCameraInfo.packageName;
- InjectionSessionData data = mPackageToSessionData.get(packageName);
- if (data == null) {
- data = new InjectionSessionData();
- data.appUid = packageUid;
- mPackageToSessionData.put(packageName, data);
+ final String packageName = openCameraInfo.packageName;
+ for (int packageUid : openCameraInfo.packageUids) {
+ if (runningUids.contains(packageUid)) {
+ InjectionSessionData data = mPackageToSessionData.get(packageName);
+ if (data == null) {
+ data = new InjectionSessionData();
+ data.appUid = packageUid;
+ mPackageToSessionData.put(packageName, data);
+ }
+ startBlocking(packageName, cameraId);
+ break;
}
- startBlocking(packageName, cameraId);
}
}
}
@@ -155,37 +164,41 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen
@Override
public void onCameraOpened(@NonNull String cameraId, @NonNull String packageName) {
synchronized (mLock) {
- try {
- final ApplicationInfo ainfo = mPackageManager.getApplicationInfo(packageName, 0);
- InjectionSessionData data = mPackageToSessionData.get(packageName);
- if (!mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(ainfo.uid)) {
- OpenCameraInfo openCameraInfo = new OpenCameraInfo();
- openCameraInfo.packageName = packageName;
- openCameraInfo.packageUid = ainfo.uid;
- mAppsToBlockOnVirtualDevice.put(cameraId, openCameraInfo);
- CameraInjectionSession existingSession =
- (data != null) ? data.cameraIdToSession.get(cameraId) : null;
- if (existingSession != null) {
- existingSession.close();
- data.cameraIdToSession.remove(cameraId);
- if (data.cameraIdToSession.isEmpty()) {
- mPackageToSessionData.remove(packageName);
- }
+ InjectionSessionData data = mPackageToSessionData.get(packageName);
+ List<UserInfo> aliveUsers = mUserManager.getAliveUsers();
+ ArraySet<Integer> packageUids = new ArraySet<>();
+ for (UserInfo user : aliveUsers) {
+ int userId = user.getUserHandle().getIdentifier();
+ int appUid = queryUidFromPackageName(userId, packageName);
+ if (mVirtualDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(appUid)) {
+ if (data == null) {
+ data = new InjectionSessionData();
+ data.appUid = appUid;
+ mPackageToSessionData.put(packageName, data);
+ }
+ if (data.cameraIdToSession.containsKey(cameraId)) {
+ return;
}
+ startBlocking(packageName, cameraId);
return;
+ } else {
+ if (appUid != Process.INVALID_UID) {
+ packageUids.add(appUid);
+ }
}
- if (data == null) {
- data = new InjectionSessionData();
- data.appUid = ainfo.uid;
- mPackageToSessionData.put(packageName, data);
- }
- if (data.cameraIdToSession.containsKey(cameraId)) {
- return;
+ }
+ OpenCameraInfo openCameraInfo = new OpenCameraInfo();
+ openCameraInfo.packageName = packageName;
+ openCameraInfo.packageUids = packageUids;
+ mAppsToBlockOnVirtualDevice.put(cameraId, openCameraInfo);
+ CameraInjectionSession existingSession =
+ (data != null) ? data.cameraIdToSession.get(cameraId) : null;
+ if (existingSession != null) {
+ existingSession.close();
+ data.cameraIdToSession.remove(cameraId);
+ if (data.cameraIdToSession.isEmpty()) {
+ mPackageToSessionData.remove(packageName);
}
- startBlocking(packageName, cameraId);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "onCameraOpened - unknown package " + packageName, e);
- return;
}
}
}
@@ -274,4 +287,16 @@ class CameraAccessController extends CameraManager.AvailabilityCallback implemen
}
}
}
+
+ private int queryUidFromPackageName(int userId, String packageName) {
+ try {
+ final ApplicationInfo ainfo =
+ mPackageManager.getApplicationInfoAsUser(packageName,
+ PackageManager.GET_ACTIVITIES, userId);
+ return ainfo.uid;
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.w(TAG, "queryUidFromPackageName - unknown package " + packageName, e);
+ return Process.INVALID_UID;
+ }
+ }
}
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 05e85e367be8..ec30369bd099 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -24,7 +24,6 @@ import android.graphics.PointF;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -42,6 +41,7 @@ import android.view.WindowManager;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
@@ -61,10 +61,12 @@ class InputController {
private static final AtomicLong sNextPhysId = new AtomicLong(1);
+ static final String PHYS_TYPE_DPAD = "Dpad";
static final String PHYS_TYPE_KEYBOARD = "Keyboard";
static final String PHYS_TYPE_MOUSE = "Mouse";
static final String PHYS_TYPE_TOUCHSCREEN = "Touchscreen";
@StringDef(prefix = { "PHYS_TYPE_" }, value = {
+ PHYS_TYPE_DPAD,
PHYS_TYPE_KEYBOARD,
PHYS_TYPE_MOUSE,
PHYS_TYPE_TOUCHSCREEN,
@@ -121,6 +123,22 @@ class InputController {
}
}
+ void createDpad(@NonNull String deviceName,
+ int vendorId,
+ int productId,
+ @NonNull IBinder deviceToken,
+ int displayId) {
+ final String phys = createPhys(PHYS_TYPE_DPAD);
+ try {
+ createDeviceInternal(InputDeviceDescriptor.TYPE_DPAD, deviceName, vendorId,
+ productId, deviceToken, displayId, phys,
+ () -> mNativeWrapper.openUinputDpad(deviceName, vendorId, productId, phys));
+ } catch (DeviceCreationException e) {
+ throw new RuntimeException(
+ "Failed to create virtual dpad device '" + deviceName + "'.", e);
+ }
+ }
+
void createKeyboard(@NonNull String deviceName,
int vendorId,
int productId,
@@ -253,6 +271,19 @@ class InputController {
InputManager.getInstance().addUniqueIdAssociation(phys, displayUniqueId);
}
+ boolean sendDpadKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
+ synchronized (mLock) {
+ final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
+ token);
+ if (inputDeviceDescriptor == null) {
+ throw new IllegalArgumentException(
+ "Could not send key event to input device for given token");
+ }
+ return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getFileDescriptor(),
+ event.getKeyCode(), event.getAction());
+ }
+ }
+
boolean sendKeyEvent(@NonNull IBinder token, @NonNull VirtualKeyEvent event) {
synchronized (mLock) {
final InputDeviceDescriptor inputDeviceDescriptor = mInputDeviceDescriptors.get(
@@ -366,6 +397,8 @@ class InputController {
}
}
+ private static native int nativeOpenUinputDpad(String deviceName, int vendorId,
+ int productId, String phys);
private static native int nativeOpenUinputKeyboard(String deviceName, int vendorId,
int productId, String phys);
private static native int nativeOpenUinputMouse(String deviceName, int vendorId, int productId,
@@ -373,6 +406,7 @@ class InputController {
private static native int nativeOpenUinputTouchscreen(String deviceName, int vendorId,
int productId, String phys, int height, int width);
private static native boolean nativeCloseUinput(int fd);
+ private static native boolean nativeWriteDpadKeyEvent(int fd, int androidKeyCode, int action);
private static native boolean nativeWriteKeyEvent(int fd, int androidKeyCode, int action);
private static native boolean nativeWriteButtonEvent(int fd, int buttonCode, int action);
private static native boolean nativeWriteTouchEvent(int fd, int pointerId, int toolType,
@@ -385,6 +419,10 @@ class InputController {
/** Wrapper around the static native methods for tests. */
@VisibleForTesting
protected static class NativeWrapper {
+ public int openUinputDpad(String deviceName, int vendorId, int productId, String phys) {
+ return nativeOpenUinputDpad(deviceName, vendorId, productId, phys);
+ }
+
public int openUinputKeyboard(String deviceName, int vendorId, int productId, String phys) {
return nativeOpenUinputKeyboard(deviceName, vendorId, productId, phys);
}
@@ -403,6 +441,10 @@ class InputController {
return nativeCloseUinput(fd);
}
+ public boolean writeDpadKeyEvent(int fd, int androidKeyCode, int action) {
+ return nativeWriteDpadKeyEvent(fd, androidKeyCode, action);
+ }
+
public boolean writeKeyEvent(int fd, int androidKeyCode, int action) {
return nativeWriteKeyEvent(fd, androidKeyCode, action);
}
@@ -433,10 +475,12 @@ class InputController {
static final int TYPE_KEYBOARD = 1;
static final int TYPE_MOUSE = 2;
static final int TYPE_TOUCHSCREEN = 3;
+ static final int TYPE_DPAD = 4;
@IntDef(prefix = { "TYPE_" }, value = {
TYPE_KEYBOARD,
TYPE_MOUSE,
TYPE_TOUCHSCREEN,
+ TYPE_DPAD,
})
@Retention(RetentionPolicy.SOURCE)
@interface Type {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index cca3212703f0..4204162f3d98 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -344,6 +344,33 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
}
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @Override // Binder call
+ public void createVirtualDpad(
+ int displayId,
+ @NonNull String deviceName,
+ int vendorId,
+ int productId,
+ @NonNull IBinder deviceToken) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ "Permission required to create a virtual dpad");
+ synchronized (mVirtualDeviceLock) {
+ if (!mVirtualDisplayIds.contains(displayId)) {
+ throw new SecurityException(
+ "Cannot create a virtual dpad for a display not associated with "
+ + "this virtual device");
+ }
+ }
+ final long token = Binder.clearCallingIdentity();
+ try {
+ mInputController.createDpad(deviceName, vendorId, productId, deviceToken,
+ displayId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
@Override // Binder call
public void createVirtualKeyboard(
int displayId,
@@ -437,6 +464,16 @@ final class VirtualDeviceImpl extends IVirtualDevice.Stub
}
@Override // Binder call
+ public boolean sendDpadKeyEvent(IBinder token, VirtualKeyEvent event) {
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ return mInputController.sendDpadKeyEvent(token, event);
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
+ @Override // Binder call
public boolean sendKeyEvent(IBinder token, VirtualKeyEvent event) {
final long binderToken = Binder.clearCallingIdentity();
try {
diff --git a/services/contentcapture/Android.bp b/services/contentcapture/Android.bp
index 434f239dbddc..5392c2cde3b8 100644
--- a/services/contentcapture/Android.bp
+++ b/services/contentcapture/Android.bp
@@ -17,6 +17,9 @@ filegroup {
java_library_static {
name: "services.contentcapture",
defaults: ["platform_service_defaults"],
- srcs: [":services.contentcapture-sources"],
+ srcs: [
+ ":services.contentcapture-sources",
+ "java/**/*.logtags",
+ ],
libs: ["services.core"],
}
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
index 7a95a8fe8c7f..a08687f86719 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCapturePerUserService.java
@@ -60,6 +60,7 @@ import android.service.contentcapture.SnapshotData;
import android.service.voice.VoiceInteractionManagerInternal;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -69,6 +70,7 @@ import android.view.contentcapture.DataShareRequest;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.LocalServices;
import com.android.server.contentcapture.RemoteContentCaptureService.ContentCaptureServiceCallbacks;
@@ -88,6 +90,10 @@ final class ContentCapturePerUserService
private static final String TAG = ContentCapturePerUserService.class.getSimpleName();
+ private static final int EVENT_LOG_CONNECT_STATE_DIED = 0;
+ static final int EVENT_LOG_CONNECT_STATE_CONNECTED = 1;
+ static final int EVENT_LOG_CONNECT_STATE_DISCONNECTED = 2;
+
@GuardedBy("mLock")
private final SparseArray<ContentCaptureServerSession> mSessions = new SparseArray<>();
@@ -190,9 +196,12 @@ final class ContentCapturePerUserService
Slog.w(TAG, "remote service died: " + service);
synchronized (mLock) {
mZombie = true;
+ ComponentName serviceComponent = getServiceComponentName();
writeServiceEvent(
FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_REMOTE_SERVICE_DIED,
- getServiceComponentName());
+ serviceComponent);
+ EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED, mUserId,
+ EVENT_LOG_CONNECT_STATE_DIED, 0);
}
}
@@ -529,6 +538,15 @@ final class ContentCapturePerUserService
return mConditionsByPkg.get(packageName);
}
+ @Nullable
+ ArraySet<String> getContentCaptureAllowlist() {
+ ArraySet<String> allowPackages;
+ synchronized (mLock) {
+ allowPackages = mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId);
+ }
+ return allowPackages;
+ }
+
@GuardedBy("mLock")
void onActivityEventLocked(@NonNull ComponentName componentName, @ActivityEventType int type) {
if (mRemoteService == null) {
@@ -617,8 +635,12 @@ final class ContentCapturePerUserService
ArraySet<String> oldList =
mMaster.mGlobalContentCaptureOptions.getWhitelistedPackages(mUserId);
+ EventLog.writeEvent(EventLogTags.CC_CURRENT_ALLOWLIST, mUserId,
+ CollectionUtils.size(oldList));
mMaster.mGlobalContentCaptureOptions.setWhitelist(mUserId, packages, activities);
+ EventLog.writeEvent(EventLogTags.CC_SET_ALLOWLIST, mUserId,
+ CollectionUtils.size(packages), CollectionUtils.size(activities));
writeSetWhitelistEvent(getServiceComponentName(), packages, activities);
updateContentCaptureOptions(oldList);
@@ -699,13 +721,15 @@ final class ContentCapturePerUserService
private void updateContentCaptureOptions(@Nullable ArraySet<String> oldList) {
ArraySet<String> adding = mMaster.mGlobalContentCaptureOptions
.getWhitelistedPackages(mUserId);
+ int addingCount = CollectionUtils.size(adding);
+ EventLog.writeEvent(EventLogTags.CC_CURRENT_ALLOWLIST, mUserId, addingCount);
if (oldList != null && adding != null) {
adding.removeAll(oldList);
}
- int N = adding != null ? adding.size() : 0;
- for (int i = 0; i < N; i++) {
+ EventLog.writeEvent(EventLogTags.CC_UPDATE_OPTIONS, mUserId, addingCount);
+ for (int i = 0; i < addingCount; i++) {
String packageName = adding.valueAt(i);
ContentCaptureOptions options = mMaster.mGlobalContentCaptureOptions
.getOptions(mUserId, packageName);
diff --git a/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags b/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags
new file mode 100644
index 000000000000..5218b26397df
--- /dev/null
+++ b/services/contentcapture/java/com/android/server/contentcapture/EventLogTags.logtags
@@ -0,0 +1,13 @@
+# See system/logging/logcat/event.logtags for a description of the format of this file.
+
+option java_package com.android.server.contentcapture
+
+# ContentCaptureService connection state change, refer to ContentCapturePerUserService
+# for type definition
+53200 cc_connect_state_changed (user|1|5),(type|1|5),(package_count|1|1)
+# Set the package and activity allowlist
+53201 cc_set_allowlist (user|1|5),(package_count|1|1),(activity_count|1|1)
+# Get the current allowlist
+53202 cc_current_allowlist (user|1|5),(count|1|1)
+# update content capture client option with new allow list count
+53203 cc_update_options (user|1|5),(count|1)
diff --git a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
index 1efe55aa0767..3907de4d903a 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/RemoteContentCaptureService.java
@@ -31,6 +31,7 @@ import android.service.contentcapture.IContentCaptureService;
import android.service.contentcapture.IContentCaptureServiceCallback;
import android.service.contentcapture.IDataShareCallback;
import android.service.contentcapture.SnapshotData;
+import android.util.EventLog;
import android.util.Slog;
import android.view.contentcapture.ContentCaptureContext;
import android.view.contentcapture.DataRemovalRequest;
@@ -38,6 +39,7 @@ import android.view.contentcapture.DataShareRequest;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.internal.os.IResultReceiver;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FrameworkStatsLog;
final class RemoteContentCaptureService
@@ -88,6 +90,10 @@ final class RemoteContentCaptureService
writeServiceEvent(
FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_CONNECTED,
mComponentName);
+ EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED,
+ mPerUserService.getUserId(),
+ ContentCapturePerUserService.EVENT_LOG_CONNECT_STATE_CONNECTED,
+ CollectionUtils.size(mPerUserService.getContentCaptureAllowlist()));
} finally {
// Update the system-service state, in case the service reconnected after
// dying
@@ -98,6 +104,9 @@ final class RemoteContentCaptureService
writeServiceEvent(
FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__ON_DISCONNECTED,
mComponentName);
+ EventLog.writeEvent(EventLogTags.CC_CONNECT_STATE_CHANGED,
+ mPerUserService.getUserId(),
+ ContentCapturePerUserService.EVENT_LOG_CONNECT_STATE_DISCONNECTED, 0);
}
} catch (Exception e) {
Slog.w(mTag, "Exception calling onConnectedStateChanged(" + connected + "): " + e);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 8b13cc8e8d88..3aed1678df58 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -134,6 +134,7 @@ java_library_static {
"service-permission.stubs.system_server",
"service-sdksandbox.stubs.system_server",
],
+ plugins: ["ImmutabilityAnnotationProcessor"],
required: [
"default_television.xml",
@@ -146,7 +147,8 @@ java_library_static {
"android.hardware.boot-V1.0-java",
"android.hardware.boot-V1.1-java",
"android.hardware.boot-V1.2-java",
- "android.hardware.broadcastradio-V2.0-java",
+ "android.hardware.broadcastradio-V2.0-java", // HIDL
+ "android.hardware.broadcastradio-V1-java", // AIDL
"android.hardware.health-V1.0-java", // HIDL
"android.hardware.health-V2.0-java", // HIDL
"android.hardware.health-V2.1-java", // HIDL
@@ -172,8 +174,16 @@ java_library_static {
"overlayable_policy_aidl-java",
"SurfaceFlingerProperties",
"com.android.sysprop.watchdog",
+ "ImmutabilityAnnotation",
],
javac_shard_size: 50,
+ javacflags: [
+ "-J--add-modules=jdk.compiler",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
}
java_genrule {
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 7ca62542aaca..9f3f761eac65 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -577,6 +577,14 @@ public abstract class PackageManagerInternal {
int filterCallingUid);
/**
+ * Resolves an exported activity intent, allowing instant apps to be resolved.
+ */
+ public abstract ResolveInfo resolveIntentExported(Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags,
+ @PrivateResolveFlags long privateResolveFlags, int userId, boolean resolveForStart,
+ int filterCallingUid);
+
+ /**
* Resolves a service intent, allowing instant apps to be resolved.
*/
public abstract ResolveInfo resolveService(Intent intent, String resolvedType,
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index b96d33c0f44b..4278b3edc3eb 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -22,9 +22,12 @@ import static com.android.server.health.Utils.copyV1Battery;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.database.ContentObserver;
import android.hardware.health.HealthInfo;
import android.hardware.health.V2_1.BatteryCapacityLevel;
@@ -185,6 +188,17 @@ public final class BatteryService extends SystemService {
private ArrayDeque<Bundle> mBatteryLevelsEventQueue;
private long mLastBatteryLevelChangedSentMs;
+ private Bundle mBatteryChangedOptions = BroadcastOptions.makeRemovingMatchingFilter(
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED)).toBundle();
+ private Bundle mPowerConnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
+ new IntentFilter(Intent.ACTION_POWER_DISCONNECTED)).toBundle();
+ private Bundle mPowerDisconnectedOptions = BroadcastOptions.makeRemovingMatchingFilter(
+ new IntentFilter(Intent.ACTION_POWER_CONNECTED)).toBundle();
+ private Bundle mBatteryLowOptions = BroadcastOptions.makeRemovingMatchingFilter(
+ new IntentFilter(Intent.ACTION_BATTERY_OKAY)).toBundle();
+ private Bundle mBatteryOkayOptions = BroadcastOptions.makeRemovingMatchingFilter(
+ new IntentFilter(Intent.ACTION_BATTERY_LOW)).toBundle();
+
private MetricsLogger mMetricsLogger;
public BatteryService(Context context) {
@@ -606,7 +620,8 @@ public final class BatteryService extends SystemService {
mHandler.post(new Runnable() {
@Override
public void run() {
- mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
+ mPowerConnectedOptions);
}
});
}
@@ -617,7 +632,8 @@ public final class BatteryService extends SystemService {
mHandler.post(new Runnable() {
@Override
public void run() {
- mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
+ mPowerDisconnectedOptions);
}
});
}
@@ -630,7 +646,8 @@ public final class BatteryService extends SystemService {
mHandler.post(new Runnable() {
@Override
public void run() {
- mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
+ mBatteryLowOptions);
}
});
} else if (mSentLowBatteryBroadcast &&
@@ -642,7 +659,8 @@ public final class BatteryService extends SystemService {
mHandler.post(new Runnable() {
@Override
public void run() {
- mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL);
+ mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
+ mBatteryOkayOptions);
}
});
}
@@ -712,7 +730,8 @@ public final class BatteryService extends SystemService {
+ ", info:" + mHealthInfo.toString());
}
- mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, UserHandle.USER_ALL));
+ mHandler.post(() -> ActivityManager.broadcastStickyIntent(intent, AppOpsManager.OP_NONE,
+ mBatteryChangedOptions, UserHandle.USER_ALL));
}
private void sendBatteryLevelChangedIntentLocked() {
diff --git a/services/core/java/com/android/server/HardwarePropertiesManagerService.java b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
index e21a3d7917d1..6b3f5e23cbd9 100644
--- a/services/core/java/com/android/server/HardwarePropertiesManagerService.java
+++ b/services/core/java/com/android/server/HardwarePropertiesManagerService.java
@@ -98,12 +98,17 @@ public class HardwarePropertiesManagerService extends IHardwarePropertiesManager
}
private String getCallingPackageName() {
- final String[] packages = mContext.getPackageManager().getPackagesForUid(
- Binder.getCallingUid());
+ final PackageManager pm = mContext.getPackageManager();
+ final int uid = Binder.getCallingUid();
+ final String[] packages = pm.getPackagesForUid(uid);
if (packages != null && packages.length > 0) {
return packages[0];
}
- return "unknown";
+ final String name = pm.getNameForUid(uid);
+ if (name != null) {
+ return name;
+ }
+ return String.valueOf(uid);
}
private void dumpTempValues(String pkg, PrintWriter pw, int type,
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 63e7563af6d1..9323d9536faa 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -192,8 +192,10 @@ public final class SystemServerInitThreadPool implements Dumpable {
private static void dumpStackTraces() {
final ArrayList<Integer> pids = new ArrayList<>();
pids.add(Process.myPid());
- ActivityManagerService.dumpStackTraces(pids, null, null,
- Watchdog.getInterestingNativePids(), null, null, null);
+ ActivityManagerService.dumpStackTraces(pids, /* processCpuTracker= */null,
+ /* lastPids= */null, Watchdog.getInterestingNativePids(),
+ /* logExceptionCreatingFile= */null, /* subject= */null,
+ /* criticalEventSection= */null, /* latencyTracker= */null);
}
@Override
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 5c84a628c00d..ae65dcb4d714 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -1,6 +1,9 @@
{
"presubmit": [
{
+ "name": "CtsLocationFineTestCases"
+ },
+ {
"name": "CtsLocationCoarseTestCases"
},
{
@@ -66,10 +69,5 @@
],
"file_patterns": ["ClipboardService\\.java"]
}
- ],
- "postsubmit": [
- {
- "name": "CtsLocationFineTestCases"
- }
]
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index f26d9f9f49e6..76cac934fdfe 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -24,6 +24,7 @@ import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+import static android.telephony.SubscriptionManager.isValidSubscriptionId;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -167,6 +168,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
static final String VCN_CONFIG_FILE =
new File(Environment.getDataSystemDirectory(), "vcn/configs.xml").getPath();
+ // TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final long CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
+
/* Binder context for this service */
@NonNull private final Context mContext;
@NonNull private final Dependencies mDeps;
@@ -360,15 +365,12 @@ public class VcnManagementService extends IVcnManagementService.Stub {
/** Notifies the VcnManagementService that external dependencies can be set up. */
public void systemReady() {
- // Always run on the handler thread to ensure consistency.
- mHandler.post(() -> {
- mNetworkProvider.register();
- mContext.getSystemService(ConnectivityManager.class)
- .registerNetworkCallback(
- new NetworkRequest.Builder().clearCapabilities().build(),
- mTrackingNetworkCallback);
- mTelephonySubscriptionTracker.register();
- });
+ mNetworkProvider.register();
+ mContext.getSystemService(ConnectivityManager.class)
+ .registerNetworkCallback(
+ new NetworkRequest.Builder().clearCapabilities().build(),
+ mTrackingNetworkCallback);
+ mTelephonySubscriptionTracker.register();
}
private void enforcePrimaryUser() {
@@ -509,15 +511,22 @@ public class VcnManagementService extends IVcnManagementService.Stub {
if (!mVcns.containsKey(subGrp)) {
startVcnLocked(subGrp, entry.getValue());
}
+
+ // Cancel any scheduled teardowns for active subscriptions
+ mHandler.removeCallbacksAndMessages(mVcns.get(subGrp));
}
}
- // Schedule teardown of any VCN instances that have lost carrier privileges
+ // Schedule teardown of any VCN instances that have lost carrier privileges (after a
+ // delay)
for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
final ParcelUuid subGrp = entry.getKey();
final VcnConfig config = mConfigs.get(subGrp);
final boolean isActiveSubGrp = isActiveSubGroup(subGrp, snapshot);
+ final boolean isValidActiveDataSubIdNotInVcnSubGrp =
+ isValidSubscriptionId(snapshot.getActiveDataSubscriptionId())
+ && !isActiveSubGroup(subGrp, snapshot);
// TODO(b/193687515): Support multiple VCNs active at the same time
if (config == null
@@ -527,12 +536,31 @@ public class VcnManagementService extends IVcnManagementService.Stub {
final ParcelUuid uuidToTeardown = subGrp;
final Vcn instanceToTeardown = entry.getValue();
- stopVcnLocked(uuidToTeardown);
-
- // TODO(b/181789060): invoke asynchronously after Vcn notifies
- // through VcnCallback
- notifyAllPermissionedStatusCallbacksLocked(
- uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
+ // TODO(b/193687515): Support multiple VCNs active at the same time
+ // If directly switching to a subscription not in the current group,
+ // teardown immediately to prevent other subscription's network from being
+ // outscored by the VCN. Otherwise, teardown after a delay to ensure that
+ // SIM profile switches do not trigger the VCN to cycle.
+ final long teardownDelayMs =
+ isValidActiveDataSubIdNotInVcnSubGrp
+ ? 0
+ : CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS;
+ mHandler.postDelayed(() -> {
+ synchronized (mLock) {
+ // Guard against case where this is run after a old instance was
+ // torn down, and a new instance was started. Verify to ensure
+ // correct instance is torn down. This could happen as a result of a
+ // Carrier App manually removing/adding a VcnConfig.
+ if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
+ stopVcnLocked(uuidToTeardown);
+
+ // TODO(b/181789060): invoke asynchronously after Vcn notifies
+ // through VcnCallback
+ notifyAllPermissionedStatusCallbacksLocked(
+ uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
+ }
+ }
+ }, instanceToTeardown, teardownDelayMs);
} else {
// If this VCN's status has not changed, update it with the new snapshot
entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index a6e5aa4b9ec7..b00dec00c030 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -895,7 +895,7 @@ public class Watchdog implements Dumpable {
StringWriter tracesFileException = new StringWriter();
final File stack = ActivityManagerService.dumpStackTraces(
pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids(),
- tracesFileException, subject, criticalEvents);
+ tracesFileException, subject, criticalEvents, /* latencyTracker= */null);
// Give some extra time to make sure the stack traces get written.
// The system's been hanging for a whlie, another second or two won't hurt much.
SystemClock.sleep(5000);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 3b299a201b7a..c677edc4f9be 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1227,31 +1227,39 @@ public final class ActiveServices {
}
private void stopServiceLocked(ServiceRecord service, boolean enqueueOomAdj) {
- if (service.delayed) {
- // If service isn't actually running, but is being held in the
- // delayed list, then we need to keep it started but note that it
- // should be stopped once no longer delayed.
- if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "Delaying stop of pending: " + service);
- service.delayedStop = true;
- return;
- }
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "stopServiceLocked()");
+ if (service.delayed) {
+ // If service isn't actually running, but is being held in the
+ // delayed list, then we need to keep it started but note that it
+ // should be stopped once no longer delayed.
+ if (DEBUG_DELAYED_STARTS) {
+ Slog.v(TAG_SERVICE, "Delaying stop of pending: " + service);
+ }
+ service.delayedStop = true;
+ return;
+ }
- final int uid = service.appInfo.uid;
- final String packageName = service.name.getPackageName();
- final String serviceName = service.name.getClassName();
- FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
- serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
- mAm.mBatteryStatsService.noteServiceStopRunning(uid, packageName, serviceName);
- service.startRequested = false;
- if (service.tracker != null) {
- synchronized (mAm.mProcessStats.mLock) {
- service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
- SystemClock.uptimeMillis());
+ final int uid = service.appInfo.uid;
+ final String packageName = service.name.getPackageName();
+ final String serviceName = service.name.getClassName();
+ FrameworkStatsLog.write(FrameworkStatsLog.SERVICE_STATE_CHANGED, uid, packageName,
+ serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__STOP);
+ mAm.mBatteryStatsService.noteServiceStopRunning(uid, packageName, serviceName);
+ service.startRequested = false;
+ if (service.tracker != null) {
+ synchronized (mAm.mProcessStats.mLock) {
+ service.tracker.setStarted(false, mAm.mProcessStats.getMemFactorLocked(),
+ SystemClock.uptimeMillis());
+ }
}
+ service.callStart = false;
+
+ bringDownServiceIfNeededLocked(service, false, false, enqueueOomAdj);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- service.callStart = false;
- bringDownServiceIfNeededLocked(service, false, false, enqueueOomAdj);
}
int stopServiceLocked(IApplicationThread caller, Intent service,
@@ -3303,7 +3311,7 @@ public final class ActiveServices {
}
public void run() {
- synchronized(mAm) {
+ synchronized (mAm) {
performServiceRestartLocked(mService);
}
}
@@ -5780,92 +5788,108 @@ public final class ActiveServices {
}
void serviceTimeout(ProcessRecord proc) {
- TimeoutRecord timeoutRecord = null;
- synchronized(mAm) {
- if (proc.isDebugging()) {
- // The app's being debugged, ignore timeout.
- return;
- }
- final ProcessServiceRecord psr = proc.mServices;
- if (psr.numberOfExecutingServices() == 0 || proc.getThread() == null) {
- return;
- }
- final long now = SystemClock.uptimeMillis();
- final long maxTime = now -
- (psr.shouldExecServicesFg() ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
- ServiceRecord timeout = null;
- long nextTime = 0;
- for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
- ServiceRecord sr = psr.getExecutingServiceAt(i);
- if (sr.executingStart < maxTime) {
- timeout = sr;
- break;
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceTimeout()");
+ TimeoutRecord timeoutRecord = null;
+ synchronized (mAm) {
+ if (proc.isDebugging()) {
+ // The app's being debugged, ignore timeout.
+ return;
+ }
+ final ProcessServiceRecord psr = proc.mServices;
+ if (psr.numberOfExecutingServices() == 0 || proc.getThread() == null) {
+ return;
+ }
+ final long now = SystemClock.uptimeMillis();
+ final long maxTime = now
+ - (psr.shouldExecServicesFg()
+ ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
+ ServiceRecord timeout = null;
+ long nextTime = 0;
+ for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
+ ServiceRecord sr = psr.getExecutingServiceAt(i);
+ if (sr.executingStart < maxTime) {
+ timeout = sr;
+ break;
+ }
+ if (sr.executingStart > nextTime) {
+ nextTime = sr.executingStart;
+ }
+ }
+ if (timeout != null && mAm.mProcessList.isInLruListLOSP(proc)) {
+ Slog.w(TAG, "Timeout executing service: " + timeout);
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new FastPrintWriter(sw, false, 1024);
+ pw.println(timeout);
+ timeout.dump(pw, " ");
+ pw.close();
+ mLastAnrDump = sw.toString();
+ mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
+ mAm.mHandler.postDelayed(mLastAnrDumpClearer,
+ LAST_ANR_LIFETIME_DURATION_MSECS);
+ String anrMessage = "executing service " + timeout.shortInstanceName;
+ timeoutRecord = TimeoutRecord.forServiceExec(anrMessage);
+ } else {
+ Message msg = mAm.mHandler.obtainMessage(
+ ActivityManagerService.SERVICE_TIMEOUT_MSG);
+ msg.obj = proc;
+ mAm.mHandler.sendMessageAtTime(msg, psr.shouldExecServicesFg()
+ ? (nextTime + SERVICE_TIMEOUT) :
+ (nextTime + SERVICE_BACKGROUND_TIMEOUT));
}
- if (sr.executingStart > nextTime) {
- nextTime = sr.executingStart;
- }
- }
- if (timeout != null && mAm.mProcessList.isInLruListLOSP(proc)) {
- Slog.w(TAG, "Timeout executing service: " + timeout);
- StringWriter sw = new StringWriter();
- PrintWriter pw = new FastPrintWriter(sw, false, 1024);
- pw.println(timeout);
- timeout.dump(pw, " ");
- pw.close();
- mLastAnrDump = sw.toString();
- mAm.mHandler.removeCallbacks(mLastAnrDumpClearer);
- mAm.mHandler.postDelayed(mLastAnrDumpClearer, LAST_ANR_LIFETIME_DURATION_MSECS);
- String anrMessage = "executing service " + timeout.shortInstanceName;
- timeoutRecord = TimeoutRecord.forServiceExec(anrMessage);
- } else {
- Message msg = mAm.mHandler.obtainMessage(
- ActivityManagerService.SERVICE_TIMEOUT_MSG);
- msg.obj = proc;
- mAm.mHandler.sendMessageAtTime(msg, psr.shouldExecServicesFg()
- ? (nextTime+SERVICE_TIMEOUT) : (nextTime + SERVICE_BACKGROUND_TIMEOUT));
}
- }
- if (timeoutRecord != null) {
- mAm.mAnrHelper.appNotResponding(proc, timeoutRecord);
+ if (timeoutRecord != null) {
+ mAm.mAnrHelper.appNotResponding(proc, timeoutRecord);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
void serviceForegroundTimeout(ServiceRecord r) {
- ProcessRecord app;
- // Grab a timestamp before lock is taken.
- long timeoutEndMs = SystemClock.uptimeMillis();
- synchronized (mAm) {
- if (!r.fgRequired || !r.fgWaiting || r.destroying) {
- return;
- }
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "serviceForegroundTimeout()");
+ ProcessRecord app;
+ // Create a TimeoutRecord .
+ final String annotation = "Context.startForegroundService() did not then call "
+ + "Service.startForeground(): " + r;
+ TimeoutRecord timeoutRecord = TimeoutRecord.forServiceStartWithEndTime(annotation,
+ SystemClock.uptimeMillis());
- app = r.app;
- if (app != null && app.isDebugging()) {
- // The app's being debugged; let it ride
- return;
- }
+ timeoutRecord.mLatencyTracker.waitingOnAMSLockEnded();
+ synchronized (mAm) {
+ timeoutRecord.mLatencyTracker.waitingOnAMSLockEnded();
+ if (!r.fgRequired || !r.fgWaiting || r.destroying) {
+ return;
+ }
- if (DEBUG_BACKGROUND_CHECK) {
- Slog.i(TAG, "Service foreground-required timeout for " + r);
+ app = r.app;
+ if (app != null && app.isDebugging()) {
+ // The app's being debugged; let it ride
+ return;
+ }
+
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Service foreground-required timeout for " + r);
+ }
+ r.fgWaiting = false;
+ stopServiceLocked(r, false);
}
- r.fgWaiting = false;
- stopServiceLocked(r, false);
- }
- if (app != null) {
- final String annotation = "Context.startForegroundService() did not then call "
- + "Service.startForeground(): " + r;
- Message msg = mAm.mHandler.obtainMessage(
- ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_ANR_MSG);
- TimeoutRecord timeoutRecord = TimeoutRecord.forServiceStartWithEndTime(annotation,
- timeoutEndMs);
- SomeArgs args = SomeArgs.obtain();
- args.arg1 = app;
- args.arg2 = timeoutRecord;
- msg.obj = args;
- mAm.mHandler.sendMessageDelayed(msg,
- mAm.mConstants.mServiceStartForegroundAnrDelayMs);
+ if (app != null) {
+
+ Message msg = mAm.mHandler.obtainMessage(
+ ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_ANR_MSG);
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = app;
+ args.arg2 = timeoutRecord;
+ msg.obj = args;
+ mAm.mHandler.sendMessageDelayed(msg,
+ mAm.mConstants.mServiceStartForegroundAnrDelayMs);
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6e52d21e6b43..7d85c1333b1b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22,6 +22,7 @@ import static android.Manifest.permission.FILTER_EVENTS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
+import static android.Manifest.permission.MANAGE_USERS;
import static android.Manifest.permission.START_ACTIVITIES_FROM_BACKGROUND;
import static android.Manifest.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND;
import static android.app.ActivityManager.INSTR_FLAG_ALWAYS_CHECK_SIGNATURE;
@@ -215,6 +216,7 @@ import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetManagerInternal;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledAfter;
import android.content.AttributionSource;
import android.content.AutofillOptions;
import android.content.BroadcastReceiver;
@@ -244,6 +246,7 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionInfo;
+import android.content.pm.PermissionMethod;
import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ProviderInfoList;
@@ -256,6 +259,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Rect;
+import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
import android.media.audiofx.AudioEffect;
import android.net.ConnectivityManager;
@@ -330,6 +334,7 @@ import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoUtils;
+import android.view.Display;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -361,6 +366,7 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.TransferPipe;
import com.android.internal.os.Zygote;
+import com.android.internal.os.anr.AnrLatencyTracker;
import com.android.internal.policy.AttributeCache;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
@@ -585,6 +591,17 @@ public class ActivityManagerService extends IActivityManager.Stub
private static final long DYNAMIC_RECEIVER_EXPLICIT_EXPORT_REQUIRED = 161145287L;
/**
+ * Apps targeting Android U and above will need to export components in order to invoke them
+ * through implicit intents.
+ *
+ * If a component is not exported and invoked, it will be removed from the list of receivers.
+ * This applies specifically to activities and broadcasts.
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ public static final long IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS = 229362273;
+
+ /**
* The maximum number of bytes that {@link #setProcessStateSummary} accepts.
*
* @see {@link android.app.ActivityManager#setProcessStateSummary(byte[])}
@@ -655,10 +672,17 @@ public class ActivityManagerService extends IActivityManager.Stub
// we still create this new offload queue, but never ever put anything on it.
final boolean mEnableOffloadQueue;
- final BroadcastQueue mFgBroadcastQueue;
- final BroadcastQueue mBgBroadcastQueue;
- final BroadcastQueue mBgOffloadBroadcastQueue;
- final BroadcastQueue mFgOffloadBroadcastQueue;
+ /**
+ * Flag indicating if we should use {@link BroadcastQueueModernImpl} instead
+ * of the default {@link BroadcastQueueImpl}.
+ */
+ final boolean mEnableModernQueue;
+
+ static final int BROADCAST_QUEUE_FG = 0;
+ static final int BROADCAST_QUEUE_BG = 1;
+ static final int BROADCAST_QUEUE_BG_OFFLOAD = 2;
+ static final int BROADCAST_QUEUE_FG_OFFLOAD = 3;
+
// Convenient for easy iteration over the queues. Foreground is first
// so that dispatch of foreground broadcasts gets precedence.
final BroadcastQueue[] mBroadcastQueues;
@@ -680,12 +704,16 @@ public class ActivityManagerService extends IActivityManager.Stub
}
BroadcastQueue broadcastQueueForFlags(int flags, Object cookie) {
+ if (mEnableModernQueue) {
+ return mBroadcastQueues[0];
+ }
+
if (isOnFgOffloadQueue(flags)) {
if (DEBUG_BROADCAST_BACKGROUND) {
Slog.i(TAG_BROADCAST,
"Broadcast intent " + cookie + " on foreground offload queue");
}
- return mFgOffloadBroadcastQueue;
+ return mBroadcastQueues[BROADCAST_QUEUE_FG_OFFLOAD];
}
if (isOnBgOffloadQueue(flags)) {
@@ -693,14 +721,15 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.i(TAG_BROADCAST,
"Broadcast intent " + cookie + " on background offload queue");
}
- return mBgOffloadBroadcastQueue;
+ return mBroadcastQueues[BROADCAST_QUEUE_BG_OFFLOAD];
}
final boolean isFg = (flags & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
if (DEBUG_BROADCAST_BACKGROUND) Slog.i(TAG_BROADCAST,
"Broadcast intent " + cookie + " on "
+ (isFg ? "foreground" : "background") + " queue");
- return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
+ return (isFg) ? mBroadcastQueues[BROADCAST_QUEUE_FG]
+ : mBroadcastQueues[BROADCAST_QUEUE_BG];
}
private volatile int mDeviceOwnerUid = INVALID_UID;
@@ -2340,6 +2369,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mUiHandler = injector.getUiHandler(null /* service */);
mUidObserverController = new UidObserverController(mUiHandler);
mUserController = new UserController(this);
+ mInjector.mUserController = mUserController;
mPendingIntentController =
new PendingIntentController(handlerThread.getLooper(), mUserController, mConstants);
mAppRestrictionController = new AppRestrictionController(mContext, this);
@@ -2353,9 +2383,8 @@ public class ActivityManagerService extends IActivityManager.Stub
mPendingStartActivityUids = new PendingStartActivityUids();
mUseFifoUiScheduling = false;
mEnableOffloadQueue = false;
+ mEnableModernQueue = false;
mBroadcastQueues = new BroadcastQueue[0];
- mFgBroadcastQueue = mBgBroadcastQueue = mBgOffloadBroadcastQueue =
- mFgOffloadBroadcastQueue = null;
mComponentAliasResolver = new ComponentAliasResolver(this);
}
@@ -2411,20 +2440,24 @@ public class ActivityManagerService extends IActivityManager.Stub
mEnableOffloadQueue = SystemProperties.getBoolean(
"persist.device_config.activity_manager_native_boot.offload_queue_enabled", true);
+ mEnableModernQueue = SystemProperties.getBoolean(
+ "persist.device_config.activity_manager_native_boot.modern_queue_enabled", false);
- mBroadcastQueues = new BroadcastQueue[4];
- mFgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
- "foreground", foreConstants, false, ProcessList.SCHED_GROUP_DEFAULT);
- mBgBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
- "background", backConstants, true, ProcessList.SCHED_GROUP_BACKGROUND);
- mBgOffloadBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
- "offload_bg", offloadConstants, true, ProcessList.SCHED_GROUP_BACKGROUND);
- mFgOffloadBroadcastQueue = new BroadcastQueueImpl(this, mHandler,
- "offload_fg", foreConstants, true, ProcessList.SCHED_GROUP_BACKGROUND);
- mBroadcastQueues[0] = mFgBroadcastQueue;
- mBroadcastQueues[1] = mBgBroadcastQueue;
- mBroadcastQueues[2] = mBgOffloadBroadcastQueue;
- mBroadcastQueues[3] = mFgOffloadBroadcastQueue;
+ if (mEnableModernQueue) {
+ mBroadcastQueues = new BroadcastQueue[1];
+ mBroadcastQueues[0] = new BroadcastQueueModernImpl(this, mHandler,
+ foreConstants, backConstants);
+ } else {
+ mBroadcastQueues = new BroadcastQueue[4];
+ mBroadcastQueues[BROADCAST_QUEUE_FG] = new BroadcastQueueImpl(this, mHandler,
+ "foreground", foreConstants, false, ProcessList.SCHED_GROUP_DEFAULT);
+ mBroadcastQueues[BROADCAST_QUEUE_BG] = new BroadcastQueueImpl(this, mHandler,
+ "background", backConstants, true, ProcessList.SCHED_GROUP_BACKGROUND);
+ mBroadcastQueues[BROADCAST_QUEUE_BG_OFFLOAD] = new BroadcastQueueImpl(this, mHandler,
+ "offload_bg", offloadConstants, true, ProcessList.SCHED_GROUP_BACKGROUND);
+ mBroadcastQueues[BROADCAST_QUEUE_FG_OFFLOAD] = new BroadcastQueueImpl(this, mHandler,
+ "offload_fg", foreConstants, true, ProcessList.SCHED_GROUP_BACKGROUND);
+ }
mServices = new ActiveServices(this);
mCpHelper = new ContentProviderHelper(this, true);
@@ -2451,6 +2484,7 @@ public class ActivityManagerService extends IActivityManager.Stub
mUgmInternal = LocalServices.getService(UriGrantsManagerInternal.class);
mUserController = new UserController(this);
+ mInjector.mUserController = mUserController;
mPendingIntentController = new PendingIntentController(
mHandlerThread.getLooper(), mUserController, mConstants);
@@ -3388,12 +3422,14 @@ public class ActivityManagerService extends IActivityManager.Stub
* @param lastPids of dalvik VM processes to dump stack traces for last
* @param nativePids optional list of native pids to dump stack crawls
* @param logExceptionCreatingFile optional writer to which we log errors creating the file
+ * @param latencyTracker the latency tracker instance of the current ANR.
*/
public static File dumpStackTraces(ArrayList<Integer> firstPids,
ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
- ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile) {
+ ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile,
+ AnrLatencyTracker latencyTracker) {
return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePids,
- logExceptionCreatingFile, null, null, null);
+ logExceptionCreatingFile, null, null, null, latencyTracker);
}
/**
@@ -3404,13 +3440,14 @@ public class ActivityManagerService extends IActivityManager.Stub
* @param logExceptionCreatingFile optional writer to which we log errors creating the file
* @param subject optional line related to the error
* @param criticalEventSection optional lines containing recent critical events.
+ * @param latencyTracker the latency tracker instance of the current ANR.
*/
public static File dumpStackTraces(ArrayList<Integer> firstPids,
ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile,
- String subject, String criticalEventSection) {
+ String subject, String criticalEventSection, AnrLatencyTracker latencyTracker) {
return dumpStackTraces(firstPids, processCpuTracker, lastPids, nativePids,
- logExceptionCreatingFile, null, subject, criticalEventSection);
+ logExceptionCreatingFile, null, subject, criticalEventSection, latencyTracker);
}
/**
@@ -3420,83 +3457,97 @@ public class ActivityManagerService extends IActivityManager.Stub
/* package */ static File dumpStackTraces(ArrayList<Integer> firstPids,
ProcessCpuTracker processCpuTracker, SparseArray<Boolean> lastPids,
ArrayList<Integer> nativePids, StringWriter logExceptionCreatingFile,
- long[] firstPidOffsets, String subject, String criticalEventSection) {
- ArrayList<Integer> extraPids = null;
+ long[] firstPidOffsets, String subject, String criticalEventSection,
+ AnrLatencyTracker latencyTracker) {
+ try {
+ if (latencyTracker != null) {
+ latencyTracker.dumpStackTracesStarted();
+ }
+ ArrayList<Integer> extraPids = null;
- Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);
+ Slog.i(TAG, "dumpStackTraces pids=" + lastPids + " nativepids=" + nativePids);
- // Measure CPU usage as soon as we're called in order to get a realistic sampling
- // of the top users at the time of the request.
- if (processCpuTracker != null) {
- processCpuTracker.init();
- try {
- Thread.sleep(200);
- } catch (InterruptedException ignored) {
- }
+ // Measure CPU usage as soon as we're called in order to get a realistic sampling
+ // of the top users at the time of the request.
+ if (processCpuTracker != null) {
+ if (latencyTracker != null) {
+ latencyTracker.processCpuTrackerMethodsCalled();
+ }
+ processCpuTracker.init();
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException ignored) {
+ }
- processCpuTracker.update();
+ processCpuTracker.update();
- // We'll take the stack crawls of just the top apps using CPU.
- final int N = processCpuTracker.countWorkingStats();
- extraPids = new ArrayList<>();
- for (int i = 0; i < N && extraPids.size() < 5; i++) {
- ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
- if (lastPids.indexOfKey(stats.pid) >= 0) {
- if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
+ // We'll take the stack crawls of just the top apps using CPU.
+ final int workingStatsNumber = processCpuTracker.countWorkingStats();
+ extraPids = new ArrayList<>();
+ for (int i = 0; i < workingStatsNumber && extraPids.size() < 5; i++) {
+ ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
+ if (lastPids.indexOfKey(stats.pid) >= 0) {
+ if (DEBUG_ANR) Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
- extraPids.add(stats.pid);
- } else {
- Slog.i(TAG, "Skipping next CPU consuming process, not a java proc: "
- + stats.pid);
+ extraPids.add(stats.pid);
+ } else {
+ Slog.i(TAG, "Skipping next CPU consuming process, not a java proc: "
+ + stats.pid);
+ }
+ }
+ if (latencyTracker != null) {
+ latencyTracker.processCpuTrackerMethodsReturned();
}
}
- }
- final File tracesDir = new File(ANR_TRACE_DIR);
- // Each set of ANR traces is written to a separate file and dumpstate will process
- // all such files and add them to a captured bug report if they're recent enough.
- maybePruneOldTraces(tracesDir);
+ final File tracesDir = new File(ANR_TRACE_DIR);
+ // Each set of ANR traces is written to a separate file and dumpstate will process
+ // all such files and add them to a captured bug report if they're recent enough.
+ maybePruneOldTraces(tracesDir);
- // NOTE: We should consider creating the file in native code atomically once we've
- // gotten rid of the old scheme of dumping and lot of the code that deals with paths
- // can be removed.
- File tracesFile;
- try {
- tracesFile = createAnrDumpFile(tracesDir);
- } catch (IOException e) {
- Slog.w(TAG, "Exception creating ANR dump file:", e);
- if (logExceptionCreatingFile != null) {
- logExceptionCreatingFile.append("----- Exception creating ANR dump file -----\n");
- e.printStackTrace(new PrintWriter(logExceptionCreatingFile));
+ // NOTE: We should consider creating the file in native code atomically once we've
+ // gotten rid of the old scheme of dumping and lot of the code that deals with paths
+ // can be removed.
+ File tracesFile;
+ try {
+ tracesFile = createAnrDumpFile(tracesDir);
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception creating ANR dump file:", e);
+ if (logExceptionCreatingFile != null) {
+ logExceptionCreatingFile.append(
+ "----- Exception creating ANR dump file -----\n");
+ e.printStackTrace(new PrintWriter(logExceptionCreatingFile));
+ }
+ if (latencyTracker != null) {
+ latencyTracker.anrSkippedDumpStackTraces();
+ }
+ return null;
}
- return null;
- }
- if (subject != null || criticalEventSection != null) {
- try (FileOutputStream fos = new FileOutputStream(tracesFile, true)) {
- if (subject != null) {
- String header = "Subject: " + subject + "\n\n";
- fos.write(header.getBytes(StandardCharsets.UTF_8));
- }
- if (criticalEventSection != null) {
- fos.write(criticalEventSection.getBytes(StandardCharsets.UTF_8));
+ if (subject != null || criticalEventSection != null) {
+ appendtoANRFile(tracesFile.getAbsolutePath(),
+ (subject != null ? "Subject: " + subject + "\n\n" : "")
+ + criticalEventSection != null ? criticalEventSection : "");
+ }
+
+ Pair<Long, Long> offsets = dumpStackTraces(
+ tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids, latencyTracker);
+ if (firstPidOffsets != null) {
+ if (offsets == null) {
+ firstPidOffsets[0] = firstPidOffsets[1] = -1;
+ } else {
+ firstPidOffsets[0] = offsets.first; // Start offset to the ANR trace file
+ firstPidOffsets[1] = offsets.second; // End offset to the ANR trace file
}
- } catch (IOException e) {
- Slog.w(TAG, "Exception writing to ANR dump file:", e);
}
- }
- Pair<Long, Long> offsets = dumpStackTraces(
- tracesFile.getAbsolutePath(), firstPids, nativePids, extraPids);
- if (firstPidOffsets != null) {
- if (offsets == null) {
- firstPidOffsets[0] = firstPidOffsets[1] = -1;
- } else {
- firstPidOffsets[0] = offsets.first; // Start offset to the ANR trace file
- firstPidOffsets[1] = offsets.second; // End offset to the ANR trace file
+ return tracesFile;
+ } finally {
+ if (latencyTracker != null) {
+ latencyTracker.dumpStackTracesEnded();
}
}
- return tracesFile;
+
}
@GuardedBy("ActivityManagerService.class")
@@ -3557,6 +3608,7 @@ public class ActivityManagerService extends IActivityManager.Stub
*/
private static long dumpJavaTracesTombstoned(int pid, String fileName, long timeoutMs) {
final long timeStart = SystemClock.elapsedRealtime();
+ int headerSize = writeUptimeStartHeaderForPid(pid, fileName);
boolean javaSuccess = Debug.dumpJavaBacktraceToFileTimeout(pid, fileName,
(int) (timeoutMs / 1000));
if (javaSuccess) {
@@ -3564,7 +3616,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// but better safe than sorry.
try {
long size = new File(fileName).length();
- if (size < JAVA_DUMP_MINIMUM_SIZE) {
+ if ((size - headerSize) < JAVA_DUMP_MINIMUM_SIZE) {
Slog.w(TAG, "Successfully created Java ANR file is empty!");
javaSuccess = false;
}
@@ -3584,11 +3636,32 @@ public class ActivityManagerService extends IActivityManager.Stub
return SystemClock.elapsedRealtime() - timeStart;
}
+ private static int appendtoANRFile(String fileName, String text) {
+ try (FileOutputStream fos = new FileOutputStream(fileName, true)) {
+ byte[] header = text.getBytes(StandardCharsets.UTF_8);
+ fos.write(header);
+ return header.length;
+ } catch (IOException e) {
+ Slog.w(TAG, "Exception writing to ANR dump file:", e);
+ return 0;
+ }
+ }
+
+ /*
+ * Writes a header containing the process id and the current system uptime.
+ */
+ private static int writeUptimeStartHeaderForPid(int pid, String fileName) {
+ return appendtoANRFile(fileName, "----- dumping pid: " + pid + " at "
+ + SystemClock.uptimeMillis() + "\n");
+ }
+
+
/**
* @return The start/end offset of the trace of the very first PID
*/
- public static Pair<Long, Long> dumpStackTraces(String tracesFile, ArrayList<Integer> firstPids,
- ArrayList<Integer> nativePids, ArrayList<Integer> extraPids) {
+ public static Pair<Long, Long> dumpStackTraces(String tracesFile,
+ ArrayList<Integer> firstPids, ArrayList<Integer> nativePids,
+ ArrayList<Integer> extraPids, AnrLatencyTracker latencyTracker) {
Slog.i(TAG, "Dumping to " + tracesFile);
@@ -3607,6 +3680,9 @@ public class ActivityManagerService extends IActivityManager.Stub
// First collect all of the stacks of the most important pids.
if (firstPids != null) {
+ if (latencyTracker != null) {
+ latencyTracker.dumpingFirstPidsStarted();
+ }
int num = firstPids.size();
for (int i = 0; i < num; i++) {
final int pid = firstPids.get(i);
@@ -3617,10 +3693,16 @@ public class ActivityManagerService extends IActivityManager.Stub
tf = new File(tracesFile);
firstPidStart = tf.exists() ? tf.length() : 0;
}
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidStarted(pid);
+ }
Slog.i(TAG, "Collecting stacks for pid " + pid);
final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile,
remainingTime);
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidEnded();
+ }
remainingTime -= timeTaken;
if (remainingTime <= 0) {
@@ -3631,24 +3713,40 @@ public class ActivityManagerService extends IActivityManager.Stub
if (firstPid) {
firstPidEnd = tf.length();
+ // Full latency dump
+ if (latencyTracker != null) {
+ appendtoANRFile(tracesFile,
+ latencyTracker.dumpAsCommaSeparatedArrayWithHeader());
+ }
}
if (DEBUG_ANR) {
Slog.d(TAG, "Done with pid " + firstPids.get(i) + " in " + timeTaken + "ms");
}
}
+ if (latencyTracker != null) {
+ latencyTracker.dumpingFirstPidsEnded();
+ }
}
// Next collect the stacks of the native pids
if (nativePids != null) {
+ if (latencyTracker != null) {
+ latencyTracker.dumpingNativePidsStarted();
+ }
for (int pid : nativePids) {
Slog.i(TAG, "Collecting stacks for native pid " + pid);
final long nativeDumpTimeoutMs = Math.min(NATIVE_DUMP_TIMEOUT_MS, remainingTime);
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidStarted(pid);
+ }
final long start = SystemClock.elapsedRealtime();
Debug.dumpNativeBacktraceToFileTimeout(
pid, tracesFile, (int) (nativeDumpTimeoutMs / 1000));
final long timeTaken = SystemClock.elapsedRealtime() - start;
-
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidEnded();
+ }
remainingTime -= timeTaken;
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current native pid=" + pid +
@@ -3660,15 +3758,25 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.d(TAG, "Done with native pid " + pid + " in " + timeTaken + "ms");
}
}
+ if (latencyTracker != null) {
+ latencyTracker.dumpingNativePidsEnded();
+ }
}
// Lastly, dump stacks for all extra PIDs from the CPU tracker.
if (extraPids != null) {
+ if (latencyTracker != null) {
+ latencyTracker.dumpingExtraPidsStarted();
+ }
for (int pid : extraPids) {
Slog.i(TAG, "Collecting stacks for extra pid " + pid);
-
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidStarted(pid);
+ }
final long timeTaken = dumpJavaTracesTombstoned(pid, tracesFile, remainingTime);
-
+ if (latencyTracker != null) {
+ latencyTracker.dumpingPidEnded();
+ }
remainingTime -= timeTaken;
if (remainingTime <= 0) {
Slog.e(TAG, "Aborting stack trace dump (current extra pid=" + pid +
@@ -3680,8 +3788,14 @@ public class ActivityManagerService extends IActivityManager.Stub
Slog.d(TAG, "Done with extra pid " + pid + " in " + timeTaken + "ms");
}
}
+ if (latencyTracker != null) {
+ latencyTracker.dumpingExtraPidsEnded();
+ }
}
+ // Append the dumping footer with the current uptime
+ appendtoANRFile(tracesFile, "----- dumping ended at " + SystemClock.uptimeMillis() + "\n");
Slog.i(TAG, "Done dumping");
+
return firstPidStart >= 0 ? new Pair<>(firstPidStart, firstPidEnd) : null;
}
@@ -4407,7 +4521,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// Clean-up disabled broadcast receivers.
for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
- packageName, disabledClasses, userId, true);
+ packageName, disabledClasses, userId);
}
}
@@ -4416,7 +4530,7 @@ public class ActivityManagerService extends IActivityManager.Stub
boolean didSomething = false;
for (int i = mBroadcastQueues.length - 1; i >= 0; i--) {
didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
- null, null, userId, true);
+ null, null, userId);
}
return didSomething;
}
@@ -4552,7 +4666,7 @@ public class ActivityManagerService extends IActivityManager.Stub
if (doit) {
for (i = mBroadcastQueues.length - 1; i >= 0; i--) {
didSomething |= mBroadcastQueues[i].cleanupDisabledPackageReceiversLocked(
- packageName, null, userId, doit);
+ packageName, null, userId);
}
}
@@ -4643,7 +4757,7 @@ public class ActivityManagerService extends IActivityManager.Stub
final String packageName = app.info.packageName;
mHandler.post(new Runnable() {
@Override
- public void run(){
+ public void run() {
try {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
@@ -5855,6 +5969,12 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ /**
+ * Allows if {@code pid} is {@link #MY_PID}, then denies if the {@code pid} has been denied
+ * provided non-{@code null} {@code permission} before. Otherwise calls into
+ * {@link ActivityManager#checkComponentPermission(String, int, int, boolean)}.
+ */
+ @PermissionMethod
public static int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
if (pid == MY_PID) {
@@ -5901,6 +6021,7 @@ public class ActivityManagerService extends IActivityManager.Stub
* This can be called with or without the global lock held.
*/
@Override
+ @PermissionMethod
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
@@ -5912,6 +6033,7 @@ public class ActivityManagerService extends IActivityManager.Stub
* Binder IPC calls go through the public entry point.
* This can be called with or without the global lock held.
*/
+ @PermissionMethod
int checkCallingPermission(String permission) {
return checkPermission(permission,
Binder.getCallingPid(),
@@ -5921,6 +6043,7 @@ public class ActivityManagerService extends IActivityManager.Stub
/**
* This can be called with or without the global lock held.
*/
+ @PermissionMethod
void enforceCallingPermission(String permission, String func) {
if (checkCallingPermission(permission)
== PackageManager.PERMISSION_GRANTED) {
@@ -5938,6 +6061,25 @@ public class ActivityManagerService extends IActivityManager.Stub
/**
* This can be called with or without the global lock held.
*/
+ @PermissionMethod
+ private void enforceCallingHasAtLeastOnePermission(String func, String... permissions) {
+ for (String permission : permissions) {
+ if (checkCallingPermission(permission) == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
+ }
+
+ String msg = "Permission Denial: " + func + " from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires one of " + Arrays.toString(permissions);
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
+ /**
+ * This can be called with or without the global lock held.
+ */
void enforcePermission(String permission, int pid, int uid, String func) {
if (checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED) {
return;
@@ -6437,7 +6579,9 @@ public class ActivityManagerService extends IActivityManager.Stub
TimeoutRecord timeoutRecord = TimeoutRecord.forApp("App requested: " + reason);
final int callingPid = Binder.getCallingPid();
+ timeoutRecord.mLatencyTracker.waitingOnPidLockStarted();
synchronized (mPidsSelfLocked) {
+ timeoutRecord.mLatencyTracker.waitingOnPidLockEnded();
final ProcessRecord app = mPidsSelfLocked.get(callingPid);
if (app == null) {
throw new SecurityException("Unknown process: " + callingPid);
@@ -6448,6 +6592,10 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ void appNotResponding(@NonNull ProcessRecord anrProcess, @NonNull TimeoutRecord timeoutRecord) {
+ mAnrHelper.appNotResponding(anrProcess, timeoutRecord);
+ }
+
void startPersistentApps(int matchFlags) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) return;
@@ -8085,7 +8233,7 @@ public class ActivityManagerService extends IActivityManager.Stub
synchronized(mPidsSelfLocked) {
for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
ProcessRecord proc = mPidsSelfLocked.valueAt(i);
- if (!isAllowedWhileBooting(proc.info)){
+ if (!isAllowedWhileBooting(proc.info)) {
if (procsToKill == null) {
procsToKill = new ArrayList<ProcessRecord>();
}
@@ -8818,6 +8966,7 @@ public class ActivityManagerService extends IActivityManager.Stub
* @param incrementalMetrics metrics for apps installed on Incremental.
* @param errorId a unique id to append to the dropbox headers.
*/
+ @SuppressWarnings("DoNotCall") // Ignore warning for synchronous to call to worker.run()
public void addErrorToDropBox(String eventType,
ProcessRecord process, String processName, String activityShortComponentName,
String parentShortComponentName, ProcessRecord parentProcess,
@@ -12379,6 +12528,58 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
+ * Filters out non-exported components in a given list of broadcast filters
+ * @param intent the original intent
+ * @param callingUid the calling UID
+ * @param query the list of broadcast filters
+ * @param platformCompat the instance of platform compat
+ */
+ private static void filterNonExportedComponents(Intent intent, int callingUid,
+ List query, PlatformCompat platformCompat, String callerPackage) {
+ if (query == null
+ || intent.getPackage() != null
+ || intent.getComponent() != null
+ || ActivityManager.canAccessUnexportedComponents(callingUid)) {
+ return;
+ }
+ for (int i = query.size() - 1; i >= 0; i--) {
+ String componentInfo;
+ ResolveInfo resolveInfo;
+ BroadcastFilter broadcastFilter;
+ if (query.get(i) instanceof ResolveInfo) {
+ resolveInfo = (ResolveInfo) query.get(i);
+ if (resolveInfo.getComponentInfo().exported) {
+ continue;
+ }
+ componentInfo = resolveInfo.getComponentInfo()
+ .getComponentName().flattenToShortString();
+ } else if (query.get(i) instanceof BroadcastFilter) {
+ broadcastFilter = (BroadcastFilter) query.get(i);
+ if (broadcastFilter.exported) {
+ continue;
+ }
+ componentInfo = broadcastFilter.packageName;
+ } else {
+ continue;
+ }
+ if (!platformCompat.isChangeEnabledByUid(
+ IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS, callingUid)) {
+ Slog.w(TAG, "Non-exported component not filtered out "
+ + "(will be filtered out once the app targets U+)- intent: "
+ + intent.getAction() + ", component: "
+ + componentInfo + ", sender: "
+ + callerPackage);
+ return;
+ }
+ Slog.w(TAG, "Non-exported component filtered out - intent: "
+ + intent.getAction() + ", component: "
+ + componentInfo + ", sender: "
+ + callerPackage);
+ query.remove(i);
+ }
+ }
+
+ /**
* Main code for cleaning up a process when it has gone away. This is
* called both as a result of the process dying, or directly when stopping
* a process when running in single process mode.
@@ -12420,7 +12621,7 @@ public class ActivityManagerService extends IActivityManager.Stub
+ backupTarget.appInfo + " died during backup");
mHandler.post(new Runnable() {
@Override
- public void run(){
+ public void run() {
try {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
@@ -14217,6 +14418,8 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
+ filterNonExportedComponents(intent, callingUid, registeredReceivers,
+ mPlatformCompat, callerPackage);
int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
if (!ordered && NR > 0) {
// If we are not serializing this broadcast, then send the
@@ -14319,6 +14522,8 @@ public class ActivityManagerService extends IActivityManager.Stub
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
+ filterNonExportedComponents(intent, callingUid, receivers,
+ mPlatformCompat, callerPackage);
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
requiredPermissions, excludedPermissions, excludedPackages, appOp, brOptions,
@@ -14953,59 +15158,66 @@ public class ActivityManagerService extends IActivityManager.Stub
@GuardedBy("this")
void finishInstrumentationLocked(ProcessRecord app, int resultCode, Bundle results) {
- final ActiveInstrumentation instr = app.getActiveInstrumentation();
- if (instr == null) {
- Slog.w(TAG, "finishInstrumentation called on non-instrumented: " + app);
- return;
- }
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "finishInstrumentationLocked()");
+ final ActiveInstrumentation instr = app.getActiveInstrumentation();
+ if (instr == null) {
+ Slog.w(TAG, "finishInstrumentation called on non-instrumented: " + app);
+ return;
+ }
- synchronized (mProcLock) {
- if (!instr.mFinished) {
- if (instr.mWatcher != null) {
- Bundle finalResults = instr.mCurResults;
- if (finalResults != null) {
- if (instr.mCurResults != null && results != null) {
- finalResults.putAll(results);
+ synchronized (mProcLock) {
+ if (!instr.mFinished) {
+ if (instr.mWatcher != null) {
+ Bundle finalResults = instr.mCurResults;
+ if (finalResults != null) {
+ if (instr.mCurResults != null && results != null) {
+ finalResults.putAll(results);
+ }
+ } else {
+ finalResults = results;
}
- } else {
- finalResults = results;
+ mInstrumentationReporter.reportFinished(instr.mWatcher,
+ instr.mClass, resultCode, finalResults);
}
- mInstrumentationReporter.reportFinished(instr.mWatcher,
- instr.mClass, resultCode, finalResults);
- }
- // Can't call out of the system process with a lock held, so post a message.
- if (instr.mUiAutomationConnection != null) {
- // Go back to the default mode of denying OP_NO_ISOLATED_STORAGE app op.
- mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid,
- app.info.packageName, AppOpsManager.MODE_ERRORED);
- mAppOpsService.setAppOpsServiceDelegate(null);
- getPermissionManagerInternal().stopShellPermissionIdentityDelegation();
- mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
- instr.mUiAutomationConnection).sendToTarget();
+ // Can't call out of the system process with a lock held, so post a message.
+ if (instr.mUiAutomationConnection != null) {
+ // Go back to the default mode of denying OP_NO_ISOLATED_STORAGE app op.
+ mAppOpsService.setMode(AppOpsManager.OP_NO_ISOLATED_STORAGE, app.uid,
+ app.info.packageName, AppOpsManager.MODE_ERRORED);
+ mAppOpsService.setAppOpsServiceDelegate(null);
+ getPermissionManagerInternal().stopShellPermissionIdentityDelegation();
+ mHandler.obtainMessage(SHUTDOWN_UI_AUTOMATION_CONNECTION_MSG,
+ instr.mUiAutomationConnection).sendToTarget();
+ }
+ instr.mFinished = true;
}
- instr.mFinished = true;
- }
- instr.removeProcess(app);
- app.setActiveInstrumentation(null);
- }
- app.mProfile.clearHostingComponentType(HOSTING_COMPONENT_TYPE_INSTRUMENTATION);
+ instr.removeProcess(app);
+ app.setActiveInstrumentation(null);
+ }
+ app.mProfile.clearHostingComponentType(HOSTING_COMPONENT_TYPE_INSTRUMENTATION);
- if (app.isSdkSandbox) {
- // For sharedUid apps this will kill all sdk sandbox processes, which is not ideal.
- // TODO(b/209061624): should we call ProcessList.removeProcessLocked instead?
- killUid(UserHandle.getAppId(app.uid), UserHandle.getUserId(app.uid), "finished instr");
- final SdkSandboxManagerLocal sandboxManagerLocal =
- LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
- if (sandboxManagerLocal != null) {
- sandboxManagerLocal.notifyInstrumentationFinished(
- app.sdkSandboxClientAppPackage, Process.getAppUidForSdkSandboxUid(app.uid));
+ if (app.isSdkSandbox) {
+ // For sharedUid apps this will kill all sdk sandbox processes, which is not ideal.
+ // TODO(b/209061624): should we call ProcessList.removeProcessLocked instead?
+ killUid(UserHandle.getAppId(app.uid), UserHandle.getUserId(app.uid),
+ "finished instr");
+ final SdkSandboxManagerLocal sandboxManagerLocal =
+ LocalManagerRegistry.getManager(SdkSandboxManagerLocal.class);
+ if (sandboxManagerLocal != null) {
+ sandboxManagerLocal.notifyInstrumentationFinished(
+ app.sdkSandboxClientAppPackage,
+ Process.getAppUidForSdkSandboxUid(app.uid));
+ }
+ } else if (!instr.mNoRestart) {
+ forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false,
+ app.userId,
+ "finished inst");
}
- } else if (!instr.mNoRestart) {
- forceStopPackageLocked(app.info.packageName, -1, false, false, true, true, false,
- app.userId,
- "finished inst");
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
@@ -16144,8 +16356,34 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public boolean startUserInBackgroundOnSecondaryDisplay(int userId, int displayId) {
+ int[] displayIds = getSecondaryDisplayIdsForStartingBackgroundUsers();
+ boolean validDisplay = false;
+ if (displayIds != null) {
+ for (int i = 0; i < displayIds.length; i++) {
+ if (displayId == displayIds[i]) {
+ validDisplay = true;
+ break;
+ }
+ }
+ }
+ if (!validDisplay) {
+ throw new IllegalArgumentException("Invalid display (" + displayId + ") to start user. "
+ + "Valid options are: " + Arrays.toString(displayIds));
+ }
+
+ if (DEBUG_MU) {
+ Slogf.d(TAG_MU, "Calling startUserOnSecondaryDisplay(%d, %d) using injector %s", userId,
+ displayId, mInjector);
+ }
// Permission check done inside UserController.
- return mUserController.startUserOnSecondaryDisplay(userId, displayId);
+ return mInjector.startUserOnSecondaryDisplay(userId, displayId);
+ }
+
+ @Override
+ public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
+ enforceCallingHasAtLeastOnePermission("getSecondaryDisplayIdsForStartingBackgroundUsers()",
+ MANAGE_USERS, INTERACT_ACROSS_USERS);
+ return mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
}
/**
@@ -16396,11 +16634,6 @@ public class ActivityManagerService extends IActivityManager.Stub
}
}
- @Override
- public void enableBinderTracing() {
- Binder.enableTracingForUid(Binder.getCallingUid());
- }
-
@VisibleForTesting
public final class LocalService extends ActivityManagerInternal
implements ActivityManagerLocal {
@@ -17182,6 +17415,8 @@ public class ActivityManagerService extends IActivityManager.Stub
bOptions.setTemporaryAppAllowlist(mInternal.getBootTimeTempAllowListDuration(),
TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
PowerExemptionManager.REASON_LOCALE_CHANGED, "");
+ bOptions.setRemoveMatchingFilter(
+ new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null,
null, null, OP_NONE, bOptions.toBundle(), false, false, MY_PID,
SYSTEM_UID, Binder.getCallingUid(), Binder.getCallingPid(),
@@ -17704,7 +17939,9 @@ public class ActivityManagerService extends IActivityManager.Stub
throw new SecurityException("Requires permission " + FILTER_EVENTS);
}
ProcessRecord proc;
+ timeoutRecord.mLatencyTracker.waitingOnPidLockStarted();
synchronized (mPidsSelfLocked) {
+ timeoutRecord.mLatencyTracker.waitingOnPidLockEnded();
proc = mPidsSelfLocked.get(pid);
}
final long timeoutMillis = proc != null ? proc.getInputDispatchingTimeoutMillis() :
@@ -17725,29 +17962,36 @@ public class ActivityManagerService extends IActivityManager.Stub
ApplicationInfo aInfo, String parentShortComponentName,
WindowProcessController parentProcess, boolean aboveSystem,
TimeoutRecord timeoutRecord) {
- if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires permission " + FILTER_EVENTS);
- }
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "inputDispatchingTimedOut()");
+ if (checkCallingPermission(FILTER_EVENTS) != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires permission " + FILTER_EVENTS);
+ }
- if (proc != null) {
- synchronized (this) {
- if (proc.isDebugging()) {
- return false;
- }
+ if (proc != null) {
+ timeoutRecord.mLatencyTracker.waitingOnAMSLockStarted();
+ synchronized (this) {
+ timeoutRecord.mLatencyTracker.waitingOnAMSLockEnded();
+ if (proc.isDebugging()) {
+ return false;
+ }
- if (proc.getActiveInstrumentation() != null) {
- Bundle info = new Bundle();
- info.putString("shortMsg", "keyDispatchingTimedOut");
- info.putString("longMsg", timeoutRecord.mReason);
- finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
- return true;
+ if (proc.getActiveInstrumentation() != null) {
+ Bundle info = new Bundle();
+ info.putString("shortMsg", "keyDispatchingTimedOut");
+ info.putString("longMsg", timeoutRecord.mReason);
+ finishInstrumentationLocked(proc, Activity.RESULT_CANCELED, info);
+ return true;
+ }
}
+ mAnrHelper.appNotResponding(proc, activityShortComponentName, aInfo,
+ parentShortComponentName, parentProcess, aboveSystem, timeoutRecord);
}
- mAnrHelper.appNotResponding(proc, activityShortComponentName, aInfo,
- parentShortComponentName, parentProcess, aboveSystem, timeoutRecord);
- }
- return true;
+ return true;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
}
/**
@@ -18148,8 +18392,10 @@ public class ActivityManagerService extends IActivityManager.Stub
@VisibleForTesting
public static class Injector {
+ private final Context mContext;
private NetworkManagementInternal mNmi;
- private Context mContext;
+
+ private UserController mUserController;
public Injector(Context context) {
mContext = context;
@@ -18175,6 +18421,103 @@ public class ActivityManagerService extends IActivityManager.Stub
}
/**
+ * Called by {@code AMS.getSecondaryDisplayIdsForStartingBackgroundUsers()}.
+ */
+ // NOTE: ideally Injector should have no complex logic, but if this logic was moved to AMS,
+ // it could not be tested with the existing ActivityManagerServiceTest (as DisplayManager,
+ // DisplayInfo, etc... are final and UserManager.isUsersOnSecondaryDisplaysEnabled is
+ // static).
+ // So, the logic was added here, and tested on ActivityManagerServiceInjectorTest (which
+ // was added on FrameworksMockingServicesTests and hence uses Extended Mockito to mock
+ // final and static stuff)
+ @Nullable
+ public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
+ if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) {
+ Slogf.w(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): not supported");
+ return null;
+ }
+
+ // NOTE: DisplayManagerInternal doesn't have a method to list all displays
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+
+ Display[] allDisplays = displayManager.getDisplays();
+
+ // allDisplays should contain at least Display.DEFAULT_DISPLAY, but it's better to
+ // double check, just in case...
+ if (allDisplays == null || allDisplays.length == 0) {
+ Slogf.wtf(TAG, "displayManager (%s) returned no displays", displayManager);
+ return null;
+ }
+ boolean hasDefaultDisplay = false;
+ for (Display display : allDisplays) {
+ if (display.getDisplayId() == Display.DEFAULT_DISPLAY) {
+ hasDefaultDisplay = true;
+ break;
+ }
+ }
+ if (!hasDefaultDisplay) {
+ Slogf.wtf(TAG, "displayManager (%s) has %d displays (%s), but none has id "
+ + "DEFAULT_DISPLAY (%d)", displayManager, allDisplays.length,
+ Arrays.toString(allDisplays), Display.DEFAULT_DISPLAY);
+ return null;
+ }
+
+ // Starts with all displays but DEFAULT_DISPLAY
+ int[] displayIds = new int[allDisplays.length - 1];
+
+ // TODO(b/247592632): check for other properties like isSecure or proper display type
+ int numberValidDisplays = 0;
+ for (Display display : allDisplays) {
+ int displayId = display.getDisplayId();
+ if (display.isValid() && displayId != Display.DEFAULT_DISPLAY) {
+ displayIds[numberValidDisplays++] = displayId;
+ }
+ }
+
+ if (numberValidDisplays == 0) {
+ // TODO(b/247580038): remove this workaround once a virtual display on Car's
+ // KitchenSink (or other app) can be used while running CTS tests on devices that
+ // don't have a real display.
+ // STOPSHIP: if not removed, it should at least be unit tested
+ String testingProp = "fw.secondary_display_for_starting_users_for_testing_purposes";
+ int displayId = SystemProperties.getInt(testingProp, Display.DEFAULT_DISPLAY);
+ if (displayId != Display.DEFAULT_DISPLAY && displayId > 0) {
+ Slogf.w(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): no valid "
+ + "display found, but returning %d as set by property %s", displayId,
+ testingProp);
+ return new int[] { displayId };
+ }
+ Slogf.e(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): no valid display"
+ + " on %s", Arrays.toString(allDisplays));
+ return null;
+ }
+
+ if (numberValidDisplays != displayIds.length) {
+ int[] validDisplayIds = new int[numberValidDisplays];
+ System.arraycopy(displayIds, 0, validDisplayIds, 0, numberValidDisplays);
+ if (DEBUG_MU) {
+ Slogf.d(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): returning "
+ + "only valid displays (%d instead of %d): %s", numberValidDisplays,
+ displayIds.length, Arrays.toString(validDisplayIds));
+ }
+ return validDisplayIds;
+ }
+
+ if (DEBUG_MU) {
+ Slogf.d(TAG, "getSecondaryDisplayIdsForStartingBackgroundUsers(): returning all "
+ + "(but DEFAULT_DISPLAY) displays : %s", Arrays.toString(displayIds));
+ }
+ return displayIds;
+ }
+
+ /**
+ * Called by {@code AMS.startUserOnSecondaryDisplay()}.
+ */
+ public boolean startUserOnSecondaryDisplay(int userId, int displayId) {
+ return mUserController.startUserOnSecondaryDisplay(userId, displayId);
+ }
+
+ /**
* Return the process list instance
*/
public ProcessList getProcessList(ActivityManagerService service) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index b4f6e35b3df3..10e2aae9a8d1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -367,6 +367,8 @@ final class ActivityManagerShellCommand extends ShellCommand {
return runGetCurrentForegroundProcess(pw, mInternal, mTaskInterface);
case "reset-dropbox-rate-limiter":
return runResetDropboxRateLimiter();
+ case "list-secondary-displays-for-starting-users":
+ return runListSecondaryDisplaysForStartingUsers(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -2068,6 +2070,10 @@ final class ActivityManagerShellCommand extends ShellCommand {
success = mInterface.startUserInBackgroundWithListener(userId, waiter);
displaySuffix = "";
} else {
+ if (!UserManager.isUsersOnSecondaryDisplaysEnabled()) {
+ pw.println("Not supported");
+ return -1;
+ }
success = mInterface.startUserInBackgroundOnSecondaryDisplay(userId, displayId);
displaySuffix = " on display " + displayId;
}
@@ -3591,6 +3597,14 @@ final class ActivityManagerShellCommand extends ShellCommand {
return 0;
}
+ int runListSecondaryDisplaysForStartingUsers(PrintWriter pw) throws RemoteException {
+ int[] displayIds = mInterface.getSecondaryDisplayIdsForStartingBackgroundUsers();
+ pw.println(displayIds == null || displayIds.length == 0
+ ? "none"
+ : Arrays.toString(displayIds));
+ return 0;
+ }
+
private Resources getResources(PrintWriter pw) throws RemoteException {
// system resources does not contain all the device configuration, construct it manually.
Configuration config = mInterface.getConfiguration();
@@ -3951,6 +3965,9 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println(" Set an app's background restriction level which in turn map to a app standby bucket.");
pw.println(" get-bg-restriction-level [--user <USER_ID>] <PACKAGE>");
pw.println(" Get an app's background restriction level.");
+ pw.println(" list-secondary-displays-for-starting-users");
+ pw.println(" Lists the id of displays that can be used to start users on "
+ + "background.");
pw.println();
Intent.printIntentArgsHelp(pw, "");
}
diff --git a/services/core/java/com/android/server/am/AnrHelper.java b/services/core/java/com/android/server/am/AnrHelper.java
index f1d8353392db..6de4118f8fbb 100644
--- a/services/core/java/com/android/server/am/AnrHelper.java
+++ b/services/core/java/com/android/server/am/AnrHelper.java
@@ -21,6 +21,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NA
import android.content.pm.ApplicationInfo;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -79,29 +80,41 @@ class AnrHelper {
ApplicationInfo aInfo, String parentShortComponentName,
WindowProcessController parentProcess, boolean aboveSystem,
TimeoutRecord timeoutRecord) {
- final int incomingPid = anrProcess.mPid;
- synchronized (mAnrRecords) {
- if (incomingPid == 0) {
- // Extreme corner case such as zygote is no response to return pid for the process.
- Slog.i(TAG, "Skip zero pid ANR, process=" + anrProcess.processName);
- return;
- }
- if (mProcessingPid == incomingPid) {
- Slog.i(TAG,
- "Skip duplicated ANR, pid=" + incomingPid + " " + timeoutRecord.mReason);
- return;
- }
- for (int i = mAnrRecords.size() - 1; i >= 0; i--) {
- if (mAnrRecords.get(i).mPid == incomingPid) {
+ try {
+ timeoutRecord.mLatencyTracker.appNotRespondingStarted();
+ final int incomingPid = anrProcess.mPid;
+ timeoutRecord.mLatencyTracker.waitingOnAnrRecordLockStarted();
+ synchronized (mAnrRecords) {
+ timeoutRecord.mLatencyTracker.waitingOnAnrRecordLockEnded();
+ if (incomingPid == 0) {
+ // Extreme corner case such as zygote is no response
+ // to return pid for the process.
+ Slog.i(TAG, "Skip zero pid ANR, process=" + anrProcess.processName);
+ return;
+ }
+ if (mProcessingPid == incomingPid) {
Slog.i(TAG,
- "Skip queued ANR, pid=" + incomingPid + " " + timeoutRecord.mReason);
+ "Skip duplicated ANR, pid=" + incomingPid + " "
+ + timeoutRecord.mReason);
return;
}
+ for (int i = mAnrRecords.size() - 1; i >= 0; i--) {
+ if (mAnrRecords.get(i).mPid == incomingPid) {
+ Slog.i(TAG,
+ "Skip queued ANR, pid=" + incomingPid + " "
+ + timeoutRecord.mReason);
+ return;
+ }
+ }
+ timeoutRecord.mLatencyTracker.anrRecordPlacingOnQueueWithSize(mAnrRecords.size());
+ mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
+ parentShortComponentName, parentProcess, aboveSystem, timeoutRecord));
}
- mAnrRecords.add(new AnrRecord(anrProcess, activityShortComponentName, aInfo,
- parentShortComponentName, parentProcess, aboveSystem, timeoutRecord));
+ startAnrConsumerIfNeeded();
+ } finally {
+ timeoutRecord.mLatencyTracker.appNotRespondingEnded();
}
- startAnrConsumerIfNeeded();
+
}
private void startAnrConsumerIfNeeded() {
@@ -126,6 +139,8 @@ class AnrHelper {
}
final AnrRecord record = mAnrRecords.remove(0);
mProcessingPid = record.mPid;
+ record.mTimeoutRecord.mLatencyTracker.anrRecordsQueueSizeWhenPopped(
+ mAnrRecords.size());
return record;
}
}
@@ -143,8 +158,8 @@ class AnrHelper {
continue;
}
final long startTime = SystemClock.uptimeMillis();
- // If there are many ANR at the same time, the latency may be larger. If the latency
- // is too large, the stack trace might not be meaningful.
+ // If there are many ANR at the same time, the latency may be larger.
+ // If the latency is too large, the stack trace might not be meaningful.
final long reportLatency = startTime - r.mTimestamp;
final boolean onlyDumpSelf = reportLatency > EXPIRED_REPORT_TIME_MS;
r.appNotResponding(onlyDumpSelf);
@@ -167,11 +182,17 @@ class AnrHelper {
}
private void scheduleBinderHeavyHitterAutoSamplerIfNecessary() {
- final long now = SystemClock.uptimeMillis();
- if (mLastAnrTimeMs + CONSECUTIVE_ANR_TIME_MS > now) {
- mService.scheduleBinderHeavyHitterAutoSampler();
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "scheduleBinderHeavyHitterAutoSamplerIfNecessary()");
+ final long now = SystemClock.uptimeMillis();
+ if (mLastAnrTimeMs + CONSECUTIVE_ANR_TIME_MS > now) {
+ mService.scheduleBinderHeavyHitterAutoSampler();
+ }
+ mLastAnrTimeMs = now;
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- mLastAnrTimeMs = now;
}
private static class AnrRecord {
@@ -184,7 +205,6 @@ class AnrHelper {
final WindowProcessController mParentProcess;
final boolean mAboveSystem;
final long mTimestamp = SystemClock.uptimeMillis();
-
AnrRecord(ProcessRecord anrProcess, String activityShortComponentName,
ApplicationInfo aInfo, String parentShortComponentName,
WindowProcessController parentProcess, boolean aboveSystem,
@@ -200,10 +220,14 @@ class AnrHelper {
}
void appNotResponding(boolean onlyDumpSelf) {
- mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
- mParentShortComponentName, mParentProcess, mAboveSystem,
- mTimeoutRecord,
- onlyDumpSelf);
+ try {
+ mTimeoutRecord.mLatencyTracker.anrProcessingStarted();
+ mApp.mErrorState.appNotResponding(mActivityShortComponentName, mAppInfo,
+ mParentShortComponentName, mParentProcess, mAboveSystem,
+ mTimeoutRecord, onlyDumpSelf);
+ } finally {
+ mTimeoutRecord.mLatencyTracker.anrProcessingEnded();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index 3299ee3f65d9..ceff67e168ab 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -44,6 +44,7 @@ import static com.android.server.am.ActivityManagerService.appendMemInfo;
import static com.android.server.am.ActivityManagerService.getKsmInfo;
import static com.android.server.am.ActivityManagerService.stringifyKBSize;
import static com.android.server.am.LowMemDetector.ADJ_MEM_FACTOR_NOTHING;
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_NONE;
import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
import static com.android.server.wm.ActivityTaskManagerService.DUMP_ACTIVITIES_CMD;
@@ -1047,17 +1048,7 @@ public class AppProfiler {
}
trimMemoryUiHiddenIfNecessaryLSP(app);
if (curProcState >= ActivityManager.PROCESS_STATE_HOME && !app.isKilledByAm()) {
- if (trimMemoryLevel < curLevel[0] && (thread = app.getThread()) != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
- Slog.v(TAG_OOM_ADJ,
- "Trimming memory of " + app.processName
- + " to " + curLevel[0]);
- }
- thread.scheduleTrimMemory(curLevel[0]);
- } catch (RemoteException e) {
- }
- }
+ scheduleTrimMemoryLSP(app, curLevel[0], "Trimming memory of ");
profile.setTrimMemoryLevel(curLevel[0]);
step[0]++;
if (step[0] >= actualFactor) {
@@ -1073,31 +1064,11 @@ public class AppProfiler {
}
} else if (curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
&& !app.isKilledByAm()) {
- if (trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
- && (thread = app.getThread()) != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
- Slog.v(TAG_OOM_ADJ,
- "Trimming memory of heavy-weight " + app.processName
- + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
- }
- thread.scheduleTrimMemory(
- ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
- } catch (RemoteException e) {
- }
- }
+ scheduleTrimMemoryLSP(app, ComponentCallbacks2.TRIM_MEMORY_BACKGROUND,
+ "Trimming memory of heavy-weight ");
profile.setTrimMemoryLevel(ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
} else {
- if (trimMemoryLevel < fgTrimLevel && (thread = app.getThread()) != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
- Slog.v(TAG_OOM_ADJ, "Trimming memory of fg " + app.processName
- + " to " + fgTrimLevel);
- }
- thread.scheduleTrimMemory(fgTrimLevel);
- } catch (RemoteException e) {
- }
- }
+ scheduleTrimMemoryLSP(app, fgTrimLevel, "Trimming memory of fg ");
profile.setTrimMemoryLevel(fgTrimLevel);
}
});
@@ -1128,19 +1099,25 @@ public class AppProfiler {
// If this application is now in the background and it
// had done UI, then give it the special trim level to
// have it free UI resources.
- final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
- IApplicationThread thread;
- if (app.mProfile.getTrimMemoryLevel() < level && (thread = app.getThread()) != null) {
- try {
- if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
- Slog.v(TAG_OOM_ADJ, "Trimming memory of bg-ui "
- + app.processName + " to " + level);
- }
- thread.scheduleTrimMemory(level);
- } catch (RemoteException e) {
+ scheduleTrimMemoryLSP(app, ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN,
+ "Trimming memory of bg-ui ");
+ app.mProfile.setPendingUiClean(false);
+ }
+ }
+
+ @GuardedBy({"mService", "mProcLock"})
+ private void scheduleTrimMemoryLSP(ProcessRecord app, int level, String msg) {
+ IApplicationThread thread;
+ if (app.mProfile.getTrimMemoryLevel() < level && (thread = app.getThread()) != null) {
+ try {
+ if (DEBUG_SWITCH || DEBUG_OOM_ADJ) {
+ Slog.v(TAG_OOM_ADJ, msg + app.processName + " to " + level);
}
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(app,
+ OOM_ADJ_REASON_NONE);
+ thread.scheduleTrimMemory(level);
+ } catch (RemoteException e) {
}
- app.mProfile.setPendingUiClean(false);
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 2ebe0b48296b..f9b0dd0d6f28 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -17,6 +17,7 @@
package com.android.server.am;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.Overridable;
@@ -24,6 +25,8 @@ import android.content.ContentResolver;
import android.database.ContentObserver;
import android.os.Build;
import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.provider.DeviceConfig;
import android.provider.Settings;
import android.util.KeyValueListParser;
import android.util.Slog;
@@ -39,6 +42,9 @@ import java.lang.annotation.RetentionPolicy;
public class BroadcastConstants {
private static final String TAG = "BroadcastConstants";
+ // TODO: migrate remaining constants to be loaded from DeviceConfig
+ // TODO: migrate fg/bg values into single constants instance
+
// Value element names within the Settings record
static final String KEY_TIMEOUT = "bcast_timeout";
static final String KEY_SLOW_TIME = "bcast_slow_time";
@@ -115,6 +121,43 @@ public class BroadcastConstants {
// started its process can start a background activity.
public long ALLOW_BG_ACTIVITY_START_TIMEOUT = DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT;
+ /**
+ * For {@link BroadcastQueueModernImpl}: Maximum number of process queues to
+ * dispatch broadcasts to simultaneously.
+ */
+ public int MAX_RUNNING_PROCESS_QUEUES = DEFAULT_MAX_RUNNING_PROCESS_QUEUES;
+ private static final int DEFAULT_MAX_RUNNING_PROCESS_QUEUES = 4;
+
+ /**
+ * For {@link BroadcastQueueModernImpl}: Maximum number of active broadcasts
+ * to dispatch to a "running" process queue before we retire them back to
+ * being "runnable" to give other processes a chance to run.
+ */
+ public int MAX_RUNNING_ACTIVE_BROADCASTS = DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS;
+ private static final int DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS = 16;
+
+ /**
+ * For {@link BroadcastQueueModernImpl}: Maximum number of pending
+ * broadcasts to hold for a process before we ignore any delays that policy
+ * might have applied to that process.
+ */
+ public int MAX_PENDING_BROADCASTS = DEFAULT_MAX_PENDING_BROADCASTS;
+ private static final int DEFAULT_MAX_PENDING_BROADCASTS = 256;
+
+ /**
+ * For {@link BroadcastQueueModernImpl}: Default delay to apply to normal
+ * broadcasts, giving a chance for debouncing of rapidly changing events.
+ */
+ public long DELAY_NORMAL_MILLIS = DEFAULT_DELAY_NORMAL_MILLIS;
+ private static final long DEFAULT_DELAY_NORMAL_MILLIS = 10_000 * Build.HW_TIMEOUT_MULTIPLIER;
+
+ /**
+ * For {@link BroadcastQueueModernImpl}: Default delay to apply to
+ * broadcasts targeting cached applications.
+ */
+ public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS;
+ private static final long DEFAULT_DELAY_CACHED_MILLIS = 30_000 * Build.HW_TIMEOUT_MULTIPLIER;
+
// Settings override tracking for this instance
private String mSettingsKey;
private SettingsObserver mSettingsObserver;
@@ -128,7 +171,7 @@ public class BroadcastConstants {
@Override
public void onChange(boolean selfChange) {
- updateConstants();
+ updateSettingsConstants();
}
}
@@ -148,11 +191,15 @@ public class BroadcastConstants {
mSettingsObserver = new SettingsObserver(handler);
mResolver.registerContentObserver(Settings.Global.getUriFor(mSettingsKey),
false, mSettingsObserver);
+ updateSettingsConstants();
- updateConstants();
+ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ new HandlerExecutor(handler), this::updateDeviceConfigConstants);
+ updateDeviceConfigConstants(
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER));
}
- private void updateConstants() {
+ private void updateSettingsConstants() {
synchronized (mParser) {
try {
mParser.setString(Settings.Global.getString(mResolver, mSettingsKey));
@@ -173,6 +220,19 @@ public class BroadcastConstants {
}
}
+ private void updateDeviceConfigConstants(@NonNull DeviceConfig.Properties properties) {
+ MAX_RUNNING_PROCESS_QUEUES = properties.getInt("bcast_max_running_process_queues",
+ DEFAULT_MAX_RUNNING_PROCESS_QUEUES);
+ MAX_RUNNING_ACTIVE_BROADCASTS = properties.getInt("bcast_max_running_active_broadcasts",
+ DEFAULT_MAX_RUNNING_ACTIVE_BROADCASTS);
+ MAX_PENDING_BROADCASTS = properties.getInt("bcast_max_pending_broadcasts",
+ DEFAULT_MAX_PENDING_BROADCASTS);
+ DELAY_NORMAL_MILLIS = properties.getLong("bcast_delay_normal_millis",
+ DEFAULT_DELAY_NORMAL_MILLIS);
+ DELAY_CACHED_MILLIS = properties.getLong("bcast_delay_cached_millis",
+ DEFAULT_DELAY_CACHED_MILLIS);
+ }
+
/**
* Standard dumpsys support; invoked from BroadcastQueue dump
*/
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
new file mode 100644
index 000000000000..342d1f2f3131
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.server.am.BroadcastQueue.checkState;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UptimeMillisLong;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
+
+import java.util.ArrayDeque;
+import java.util.Iterator;
+import java.util.Objects;
+
+/**
+ * Queue of pending {@link BroadcastRecord} entries intended for delivery to a
+ * specific process.
+ * <p>
+ * Each queue has a concept of being "runnable at" a particular time in the
+ * future, which supports arbitrarily pausing or delaying delivery on a
+ * per-process basis.
+ * <p>
+ * Internally each queue consists of a pending broadcasts which are waiting to
+ * be dispatched, and a single active broadcast which is currently being
+ * dispatched.
+ * <p>
+ * This entire class is marked as {@code NotThreadSafe} since it's the
+ * responsibility of the caller to always interact with a relevant lock held.
+ */
+// @NotThreadSafe
+class BroadcastProcessQueue {
+ final @NonNull BroadcastConstants constants;
+ final @NonNull String processName;
+ final int uid;
+
+ /**
+ * Linked list connection to another process under this {@link #uid} which
+ * has a different {@link #processName}.
+ */
+ @Nullable BroadcastProcessQueue processNameNext;
+
+ /**
+ * Linked list connections to runnable process with lower and higher
+ * {@link #getRunnableAt()} times.
+ */
+ @Nullable BroadcastProcessQueue runnableAtNext;
+ @Nullable BroadcastProcessQueue runnableAtPrev;
+
+ /**
+ * Currently known details about the target process; typically undefined
+ * when the process isn't actively running.
+ */
+ @Nullable ProcessRecord app;
+
+ /**
+ * Track name to use for {@link Trace} events.
+ */
+ @Nullable String traceTrackName;
+
+ /**
+ * Ordered collection of broadcasts that are waiting to be dispatched to
+ * this process, as a pair of {@link BroadcastRecord} and the index into
+ * {@link BroadcastRecord#receivers} that represents the receiver.
+ */
+ private final ArrayDeque<SomeArgs> mPending = new ArrayDeque<>();
+
+ /**
+ * Broadcast actively being dispatched to this process.
+ */
+ private @Nullable BroadcastRecord mActive;
+
+ /**
+ * Receiver actively being dispatched to in this process. This is an index
+ * into the {@link BroadcastRecord#receivers} list of {@link #mActive}.
+ */
+ private int mActiveIndex;
+
+ /**
+ * Count of {@link #mActive} broadcasts that have been dispatched since this
+ * queue was last idle.
+ */
+ private int mActiveCountSinceIdle;
+
+ /**
+ * Flag indicating that the currently active broadcast is being dispatched
+ * was scheduled via a cold start.
+ */
+ private boolean mActiveViaColdStart;
+
+ /**
+ * Count of {@link #mPending} broadcasts of these various flavors.
+ */
+ private int mCountForeground;
+ private int mCountOrdered;
+ private int mCountAlarm;
+
+ private @UptimeMillisLong long mRunnableAt = Long.MAX_VALUE;
+ private boolean mRunnableAtInvalidated;
+
+ private boolean mProcessCached;
+
+ private String mCachedToString;
+ private String mCachedToShortString;
+
+ public BroadcastProcessQueue(@NonNull BroadcastConstants constants,
+ @NonNull String processName, int uid) {
+ this.constants = Objects.requireNonNull(constants);
+ this.processName = Objects.requireNonNull(processName);
+ this.uid = uid;
+ }
+
+ /**
+ * Enqueue the given broadcast to be dispatched to this process at some
+ * future point in time. The target receiver is indicated by the given index
+ * into {@link BroadcastRecord#receivers}.
+ */
+ public void enqueueBroadcast(@NonNull BroadcastRecord record, int recordIndex) {
+ // Detect situations where the incoming broadcast should cause us to
+ // recalculate when we'll be runnable
+ if (mPending.isEmpty()) {
+ invalidateRunnableAt();
+ }
+ if (record.isForeground()) {
+ mCountForeground++;
+ invalidateRunnableAt();
+ }
+ if (record.ordered) {
+ mCountOrdered++;
+ invalidateRunnableAt();
+ }
+ if (record.alarm) {
+ mCountAlarm++;
+ invalidateRunnableAt();
+ }
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = record;
+ args.argi1 = recordIndex;
+ mPending.addLast(args);
+ }
+
+ /**
+ * Functional interface that tests a {@link BroadcastRecord} that has been
+ * previously enqueued in {@link BroadcastProcessQueue}.
+ */
+ @FunctionalInterface
+ public interface BroadcastPredicate {
+ public boolean test(@NonNull BroadcastRecord r, int index);
+ }
+
+ /**
+ * Functional interface that consumes a {@link BroadcastRecord} that has
+ * been previously enqueued in {@link BroadcastProcessQueue}.
+ */
+ @FunctionalInterface
+ public interface BroadcastConsumer {
+ public void accept(@NonNull BroadcastRecord r, int index);
+ }
+
+ /**
+ * Remove any broadcasts matching the given predicate.
+ * <p>
+ * Predicates that choose to remove a broadcast <em>must</em> finish
+ * delivery of the matched broadcast, to ensure that situations like ordered
+ * broadcasts are handled consistently.
+ */
+ public boolean removeMatchingBroadcasts(@NonNull BroadcastPredicate predicate,
+ @NonNull BroadcastConsumer consumer) {
+ boolean didSomething = false;
+ final Iterator<SomeArgs> it = mPending.iterator();
+ while (it.hasNext()) {
+ final SomeArgs args = it.next();
+ final BroadcastRecord record = (BroadcastRecord) args.arg1;
+ final int index = args.argi1;
+ if (predicate.test(record, index)) {
+ consumer.accept(record, index);
+ args.recycle();
+ it.remove();
+ didSomething = true;
+ }
+ }
+ // TODO: also check any active broadcast once we have a better "nonce"
+ // representing each scheduled broadcast to avoid races
+ return didSomething;
+ }
+
+ /**
+ * Update if this process is in the "cached" state, typically signaling that
+ * broadcast dispatch should be paused or delayed.
+ */
+ public void setProcessCached(boolean cached) {
+ if (mProcessCached != cached) {
+ mProcessCached = cached;
+ invalidateRunnableAt();
+ }
+ }
+
+ /**
+ * Return if we know of an actively running "warm" process for this queue.
+ */
+ public boolean isProcessWarm() {
+ return (app != null) && (app.getThread() != null) && !app.isKilled();
+ }
+
+ public int getPreferredSchedulingGroupLocked() {
+ if (mCountForeground > 0 || mCountOrdered > 0 || mCountAlarm > 0) {
+ // We have an important broadcast somewhere down the queue, so
+ // boost priority until we drain them all
+ return ProcessList.SCHED_GROUP_DEFAULT;
+ } else if ((mActive != null)
+ && (mActive.isForeground() || mActive.ordered || mActive.alarm)) {
+ // We have an important broadcast right now, so boost priority
+ return ProcessList.SCHED_GROUP_DEFAULT;
+ } else {
+ return ProcessList.SCHED_GROUP_BACKGROUND;
+ }
+ }
+
+ /**
+ * Count of {@link #mActive} broadcasts that have been dispatched since this
+ * queue was last idle.
+ */
+ public int getActiveCountSinceIdle() {
+ return mActiveCountSinceIdle;
+ }
+
+ public void setActiveViaColdStart(boolean activeViaColdStart) {
+ mActiveViaColdStart = activeViaColdStart;
+ }
+
+ public boolean getActiveViaColdStart() {
+ return mActiveViaColdStart;
+ }
+
+ /**
+ * Set the currently active broadcast to the next pending broadcast.
+ */
+ public void makeActiveNextPending() {
+ // TODO: what if the next broadcast isn't runnable yet?
+ checkState(isRunnable(), "isRunnable");
+ final SomeArgs next = mPending.removeFirst();
+ mActive = (BroadcastRecord) next.arg1;
+ mActiveIndex = next.argi1;
+ mActiveCountSinceIdle++;
+ mActiveViaColdStart = false;
+ next.recycle();
+ if (mActive.isForeground()) {
+ mCountForeground--;
+ }
+ if (mActive.ordered) {
+ mCountOrdered--;
+ }
+ if (mActive.alarm) {
+ mCountAlarm--;
+ }
+ invalidateRunnableAt();
+ }
+
+ /**
+ * Set the currently running broadcast to be idle.
+ */
+ public void makeActiveIdle() {
+ mActive = null;
+ mActiveIndex = 0;
+ mActiveCountSinceIdle = 0;
+ mActiveViaColdStart = false;
+ }
+
+ public void traceProcessStartingBegin() {
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ traceTrackName, toShortString() + " starting", hashCode());
+ }
+
+ public void traceProcessRunningBegin() {
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ traceTrackName, toShortString() + " running", hashCode());
+ }
+
+ public void traceProcessEnd() {
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ traceTrackName, hashCode());
+ }
+
+ public void traceActiveBegin() {
+ final int cookie = mActive.receivers.get(mActiveIndex).hashCode();
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ traceTrackName, mActive.toShortString() + " scheduled", cookie);
+ }
+
+ public void traceActiveEnd() {
+ final int cookie = mActive.receivers.get(mActiveIndex).hashCode();
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ traceTrackName, cookie);
+ }
+
+ /**
+ * Return the broadcast being actively dispatched in this process.
+ */
+ public @NonNull BroadcastRecord getActive() {
+ checkState(isActive(), "isActive");
+ return mActive;
+ }
+
+ /**
+ * Return the index into {@link BroadcastRecord#receivers} of the receiver
+ * being actively dispatched in this process.
+ */
+ public int getActiveIndex() {
+ checkState(isActive(), "isActive");
+ return mActiveIndex;
+ }
+
+ public boolean isEmpty() {
+ return mPending.isEmpty();
+ }
+
+ public boolean isActive() {
+ return mActive != null;
+ }
+
+ public boolean isRunnable() {
+ if (mRunnableAtInvalidated) updateRunnableAt();
+ return mRunnableAt != Long.MAX_VALUE;
+ }
+
+ /**
+ * Return time at which this process is considered runnable. This is
+ * typically the time at which the next pending broadcast was first
+ * enqueued, but it also reflects any pauses or delays that should be
+ * applied to the process.
+ * <p>
+ * Returns {@link Long#MAX_VALUE} when this queue isn't currently runnable,
+ * typically when the queue is empty or when paused.
+ */
+ public @UptimeMillisLong long getRunnableAt() {
+ if (mRunnableAtInvalidated) updateRunnableAt();
+ return mRunnableAt;
+ }
+
+ public void invalidateRunnableAt() {
+ mRunnableAtInvalidated = true;
+ }
+
+ /**
+ * Update {@link #getRunnableAt()} if it's currently invalidated.
+ */
+ private void updateRunnableAt() {
+ final SomeArgs next = mPending.peekFirst();
+ if (next != null) {
+ final BroadcastRecord r = (BroadcastRecord) next.arg1;
+ final int index = next.argi1;
+
+ // If our next broadcast is ordered, and we're not the next receiver
+ // in line, then we're not runnable at all
+ if (r.ordered && r.finishedCount != index) {
+ mRunnableAt = Long.MAX_VALUE;
+ return;
+ }
+
+ final long runnableAt = r.enqueueTime;
+ if (mCountForeground > 0) {
+ mRunnableAt = runnableAt;
+ } else if (mCountOrdered > 0) {
+ mRunnableAt = runnableAt;
+ } else if (mCountAlarm > 0) {
+ mRunnableAt = runnableAt;
+ } else if (mProcessCached) {
+ mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
+ } else {
+ mRunnableAt = runnableAt + constants.DELAY_NORMAL_MILLIS;
+ }
+
+ // If we have too many broadcasts pending, bypass any delays that
+ // might have been applied above to aid draining
+ if (mPending.size() >= constants.MAX_PENDING_BROADCASTS) {
+ mRunnableAt = runnableAt;
+ }
+ } else {
+ mRunnableAt = Long.MAX_VALUE;
+ }
+ }
+
+ /**
+ * Insert the given queue into a sorted linked list of "runnable" queues.
+ *
+ * @param head the current linked list head
+ * @param item the queue to insert
+ * @return a potentially updated linked list head
+ */
+ @VisibleForTesting
+ static @Nullable BroadcastProcessQueue insertIntoRunnableList(
+ @Nullable BroadcastProcessQueue head, @NonNull BroadcastProcessQueue item) {
+ if (head == null) {
+ return item;
+ }
+ final long itemRunnableAt = item.getRunnableAt();
+ BroadcastProcessQueue test = head;
+ BroadcastProcessQueue tail = null;
+ while (test != null) {
+ if (test.getRunnableAt() >= itemRunnableAt) {
+ item.runnableAtNext = test;
+ item.runnableAtPrev = test.runnableAtPrev;
+ if (item.runnableAtNext != null) {
+ item.runnableAtNext.runnableAtPrev = item;
+ }
+ if (item.runnableAtPrev != null) {
+ item.runnableAtPrev.runnableAtNext = item;
+ }
+ return (test == head) ? item : head;
+ }
+ tail = test;
+ test = test.runnableAtNext;
+ }
+ item.runnableAtPrev = tail;
+ item.runnableAtPrev.runnableAtNext = item;
+ return head;
+ }
+
+ /**
+ * Remove the given queue from a sorted linked list of "runnable" queues.
+ *
+ * @param head the current linked list head
+ * @param item the queue to remove
+ * @return a potentially updated linked list head
+ */
+ @VisibleForTesting
+ static @Nullable BroadcastProcessQueue removeFromRunnableList(
+ @Nullable BroadcastProcessQueue head, @NonNull BroadcastProcessQueue item) {
+ if (head == item) {
+ head = item.runnableAtNext;
+ }
+ if (item.runnableAtNext != null) {
+ item.runnableAtNext.runnableAtPrev = item.runnableAtPrev;
+ }
+ if (item.runnableAtPrev != null) {
+ item.runnableAtPrev.runnableAtNext = item.runnableAtNext;
+ }
+ item.runnableAtNext = null;
+ item.runnableAtPrev = null;
+ return head;
+ }
+
+ @Override
+ public String toString() {
+ if (mCachedToString == null) {
+ mCachedToString = "BroadcastProcessQueue{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " " + processName + "/" + UserHandle.formatUid(uid) + "}";
+ }
+ return mCachedToString;
+ }
+
+ public String toShortString() {
+ if (mCachedToShortString == null) {
+ mCachedToShortString = processName + "/" + UserHandle.formatUid(uid);
+ }
+ return mCachedToShortString;
+ }
+
+ public void dumpLocked(@NonNull IndentingPrintWriter pw) {
+ if ((mActive == null) && mPending.isEmpty()) return;
+
+ pw.println(toShortString());
+ pw.increaseIndent();
+ if (mActive != null) {
+ pw.print("🏃 ");
+ pw.print(mActive.toShortString());
+ pw.print(' ');
+ pw.println(mActive.receivers.get(mActiveIndex));
+ }
+ for (SomeArgs args : mPending) {
+ final BroadcastRecord r = (BroadcastRecord) args.arg1;
+ pw.print("\u3000 ");
+ pw.print(r.toShortString());
+ pw.print(' ');
+ pw.println(r.receivers.get(args.argi1));
+ }
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 6814509922b1..b46a2b2575fe 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -22,6 +22,7 @@ import android.content.ContentResolver;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
+import android.util.Slog;
import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
@@ -39,24 +40,28 @@ public abstract class BroadcastQueue {
final @NonNull ActivityManagerService mService;
final @NonNull Handler mHandler;
- final @NonNull BroadcastConstants mConstants;
final @NonNull BroadcastSkipPolicy mSkipPolicy;
final @NonNull BroadcastHistory mHistory;
final @NonNull String mQueueName;
BroadcastQueue(@NonNull ActivityManagerService service, @NonNull Handler handler,
- @NonNull String name, @NonNull BroadcastConstants constants,
- @NonNull BroadcastSkipPolicy skipPolicy, @NonNull BroadcastHistory history) {
+ @NonNull String name, @NonNull BroadcastSkipPolicy skipPolicy,
+ @NonNull BroadcastHistory history) {
mService = Objects.requireNonNull(service);
mHandler = Objects.requireNonNull(handler);
mQueueName = Objects.requireNonNull(name);
- mConstants = Objects.requireNonNull(constants);
mSkipPolicy = Objects.requireNonNull(skipPolicy);
mHistory = Objects.requireNonNull(history);
}
- void start(@NonNull ContentResolver resolver) {
- mConstants.startObserving(mHandler, resolver);
+ static void checkState(boolean state, String msg) {
+ if (!state) {
+ Slog.wtf(TAG, msg, new Throwable());
+ }
+ }
+
+ static void logv(String msg) {
+ Slog.v(TAG, msg);
}
@Override
@@ -64,6 +69,8 @@ public abstract class BroadcastQueue {
return mQueueName;
}
+ public abstract void start(@NonNull ContentResolver resolver);
+
public abstract boolean isDelayBehindServices();
/**
@@ -74,6 +81,7 @@ public abstract class BroadcastQueue {
* otherwise {@link ProcessList#SCHED_GROUP_UNDEFINED} if this queue
* has no opinion.
*/
+ @GuardedBy("mService")
public abstract int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app);
/**
@@ -106,6 +114,9 @@ public abstract class BroadcastQueue {
/**
* Signal from OS internals that the given process has just been actively
* attached, and is ready to begin receiving broadcasts.
+ *
+ * @return if the queue performed an action on the given process, such as
+ * dispatching a pending broadcast
*/
@GuardedBy("mService")
public abstract boolean onApplicationAttachedLocked(@NonNull ProcessRecord app);
@@ -115,7 +126,7 @@ public abstract class BroadcastQueue {
* an attempted start and attachment.
*/
@GuardedBy("mService")
- public abstract boolean onApplicationTimeoutLocked(@NonNull ProcessRecord app);
+ public abstract void onApplicationTimeoutLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process, which had already been
@@ -123,14 +134,14 @@ public abstract class BroadcastQueue {
* not responding.
*/
@GuardedBy("mService")
- public abstract boolean onApplicationProblemLocked(@NonNull ProcessRecord app);
+ public abstract void onApplicationProblemLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given process has been killed, and is
* no longer actively running.
*/
@GuardedBy("mService")
- public abstract boolean onApplicationCleanupLocked(@NonNull ProcessRecord app);
+ public abstract void onApplicationCleanupLocked(@NonNull ProcessRecord app);
/**
* Signal from OS internals that the given package (or some subset of that
@@ -139,7 +150,7 @@ public abstract class BroadcastQueue {
*/
@GuardedBy("mService")
public abstract boolean cleanupDisabledPackageReceiversLocked(@Nullable String packageName,
- @Nullable Set<String> filterByClasses, int userId, boolean doit);
+ @Nullable Set<String> filterByClasses, int userId);
/**
* Quickly determine if this queue has broadcasts that are still waiting to
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 56112cfbc3e9..16711853267e 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -46,7 +46,6 @@ import android.app.BroadcastOptions;
import android.app.IApplicationThread;
import android.app.RemoteServiceException.CannotDeliverBroadcastException;
import android.app.usage.UsageEvents.Event;
-import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.IIntentReceiver;
@@ -58,7 +57,6 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerExemptionManager.ReasonCode;
@@ -98,6 +96,8 @@ public class BroadcastQueueImpl extends BroadcastQueue {
private static final String TAG_MU = TAG + POSTFIX_MU;
private static final String TAG_BROADCAST = TAG + POSTFIX_BROADCAST;
+ final BroadcastConstants mConstants;
+
/**
* If true, we can delay broadcasts while waiting services to finish in the previous
* receiver's process.
@@ -194,14 +194,15 @@ public class BroadcastQueueImpl extends BroadcastQueue {
BroadcastQueueImpl(ActivityManagerService service, Handler handler,
String name, BroadcastConstants constants, BroadcastSkipPolicy skipPolicy,
BroadcastHistory history, boolean allowDelayBehindServices, int schedGroup) {
- super(service, handler, name, constants, skipPolicy, history);
+ super(service, handler, name, skipPolicy, history);
mHandler = new BroadcastHandler(handler.getLooper());
+ mConstants = constants;
mDelayBehindServices = allowDelayBehindServices;
mSchedGroup = schedGroup;
mDispatcher = new BroadcastDispatcher(this, mConstants, mHandler, mService);
}
- void start(ContentResolver resolver) {
+ public void start(ContentResolver resolver) {
mDispatcher.start();
mConstants.startObserving(mHandler, resolver);
}
@@ -425,16 +426,16 @@ public class BroadcastQueueImpl extends BroadcastQueue {
}
}
- public boolean onApplicationTimeoutLocked(ProcessRecord app) {
- return skipCurrentOrPendingReceiverLocked(app);
+ public void onApplicationTimeoutLocked(ProcessRecord app) {
+ skipCurrentOrPendingReceiverLocked(app);
}
- public boolean onApplicationProblemLocked(ProcessRecord app) {
- return skipCurrentOrPendingReceiverLocked(app);
+ public void onApplicationProblemLocked(ProcessRecord app) {
+ skipCurrentOrPendingReceiverLocked(app);
}
- public boolean onApplicationCleanupLocked(ProcessRecord app) {
- return skipCurrentOrPendingReceiverLocked(app);
+ public void onApplicationCleanupLocked(ProcessRecord app) {
+ skipCurrentOrPendingReceiverLocked(app);
}
public boolean sendPendingBroadcastsLocked(ProcessRecord app) {
@@ -731,9 +732,10 @@ public class BroadcastQueueImpl extends BroadcastQueue {
} catch (RemoteException ex) {
// Failed to call into the process. It's either dying or wedged. Kill it gently.
synchronized (mService) {
- Slog.w(TAG, "Can't deliver broadcast to " + app.processName
- + " (pid " + app.getPid() + "). Crashing it.");
- app.scheduleCrashLocked("can't deliver broadcast",
+ final String msg = "Failed to schedule " + intent + " to " + receiver
+ + " via " + app + ": " + ex;
+ Slog.w(TAG, msg);
+ app.scheduleCrashLocked(msg,
CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
}
throw ex;
@@ -813,7 +815,11 @@ public class BroadcastQueueImpl extends BroadcastQueue {
try {
if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
"Delivering to " + filter + " : " + r);
- if (filter.receiverList.app != null && filter.receiverList.app.isInFullBackup()) {
+ final boolean isInFullBackup = (filter.receiverList.app != null)
+ && filter.receiverList.app.isInFullBackup();
+ final boolean isKilled = (filter.receiverList.app != null)
+ && filter.receiverList.app.isKilled();
+ if (isInFullBackup || isKilled) {
// Skip delivery if full backup in progress
// If it's an ordered broadcast, we need to continue to the next receiver.
if (ordered) {
@@ -1378,8 +1384,11 @@ public class BroadcastQueueImpl extends BroadcastQueue {
processCurBroadcastLocked(r, app);
return;
} catch (RemoteException e) {
- Slog.w(TAG, "Exception when sending broadcast to "
- + r.curComponent, e);
+ final String msg = "Failed to schedule " + r.intent + " to " + info
+ + " via " + app + ": " + e;
+ Slog.w(TAG, msg);
+ app.scheduleCrashLocked(msg,
+ CannotDeliverBroadcastException.TYPE_ID, /* extras=*/ null);
} catch (RuntimeException e) {
Slog.wtf(TAG, "Failed sending broadcast to "
+ r.curComponent + " with " + r.intent, e);
@@ -1409,7 +1418,7 @@ public class BroadcastQueueImpl extends BroadcastQueue {
info.activityInfo.applicationInfo, true,
r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST, r.curComponent,
- r.intent.getAction(), getHostingRecordTriggerType(r)),
+ r.intent.getAction(), r.getHostingRecordTriggerType()),
isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE : ZYGOTE_POLICY_FLAG_EMPTY,
(r.intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false);
if (r.curApp == null) {
@@ -1432,17 +1441,6 @@ public class BroadcastQueueImpl extends BroadcastQueue {
mPendingBroadcastRecvIndex = recIdx;
}
- private String getHostingRecordTriggerType(BroadcastRecord r) {
- if (r.alarm) {
- return HostingRecord.TRIGGER_TYPE_ALARM;
- } else if (r.pushMessage) {
- return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE;
- } else if (r.pushMessageOverQuota) {
- return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA;
- }
- return HostingRecord.TRIGGER_TYPE_UNKNOWN;
- }
-
@Nullable
private String getTargetPackage(BroadcastRecord r) {
if (r.intent == null) {
@@ -1456,7 +1454,7 @@ public class BroadcastQueueImpl extends BroadcastQueue {
return null;
}
- private void logBootCompletedBroadcastCompletionLatencyIfPossible(BroadcastRecord r) {
+ static void logBootCompletedBroadcastCompletionLatencyIfPossible(BroadcastRecord r) {
// Only log after last receiver.
// In case of split BOOT_COMPLETED broadcast, make sure only call this method on the
// last BroadcastRecord of the split broadcast which has non-null resultTo.
@@ -1518,19 +1516,12 @@ public class BroadcastQueueImpl extends BroadcastQueue {
if (targetPackage == null) {
return;
}
- getUsageStatsManagerInternal().reportBroadcastDispatched(
+ mService.mUsageStatsService.reportBroadcastDispatched(
r.callingUid, targetPackage, UserHandle.of(r.userId),
r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(),
mService.getUidStateLocked(targetUid));
}
- @NonNull
- private UsageStatsManagerInternal getUsageStatsManagerInternal() {
- final UsageStatsManagerInternal usageStatsManagerInternal =
- LocalServices.getService(UsageStatsManagerInternal.class);
- return usageStatsManagerInternal;
- }
-
private void maybeAddAllowBackgroundActivityStartsToken(ProcessRecord proc, BroadcastRecord r) {
if (r == null || proc == null || !r.allowBackgroundActivityStarts) {
return;
@@ -1566,110 +1557,116 @@ public class BroadcastQueueImpl extends BroadcastQueue {
if (mDispatcher.isEmpty() || mDispatcher.getActiveBroadcastLocked() == null) {
return;
}
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastTimeoutLocked()");
+ try {
+ long now = SystemClock.uptimeMillis();
+ BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
+ if (fromMsg) {
+ if (!mService.mProcessesReady) {
+ // Only process broadcast timeouts if the system is ready; some early
+ // broadcasts do heavy work setting up system facilities
+ return;
+ }
- long now = SystemClock.uptimeMillis();
- BroadcastRecord r = mDispatcher.getActiveBroadcastLocked();
- if (fromMsg) {
- if (!mService.mProcessesReady) {
- // Only process broadcast timeouts if the system is ready; some early
- // broadcasts do heavy work setting up system facilities
- return;
- }
-
- // If the broadcast is generally exempt from timeout tracking, we're done
- if (r.timeoutExempt) {
- if (DEBUG_BROADCAST) {
- Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
- + r.intent.getAction());
+ // If the broadcast is generally exempt from timeout tracking, we're done
+ if (r.timeoutExempt) {
+ if (DEBUG_BROADCAST) {
+ Slog.i(TAG_BROADCAST, "Broadcast timeout but it's exempt: "
+ + r.intent.getAction());
+ }
+ return;
+ }
+ long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
+ if (timeoutTime > now) {
+ // We can observe premature timeouts because we do not cancel and reset the
+ // broadcast timeout message after each receiver finishes. Instead, we set up
+ // an initial timeout then kick it down the road a little further as needed
+ // when it expires.
+ if (DEBUG_BROADCAST) {
+ Slog.v(TAG_BROADCAST,
+ "Premature timeout ["
+ + mQueueName + "] @ " + now
+ + ": resetting BROADCAST_TIMEOUT_MSG for "
+ + timeoutTime);
+ }
+ setBroadcastTimeoutLocked(timeoutTime);
+ return;
}
- return;
}
- long timeoutTime = r.receiverTime + mConstants.TIMEOUT;
- if (timeoutTime > now) {
- // We can observe premature timeouts because we do not cancel and reset the
- // broadcast timeout message after each receiver finishes. Instead, we set up
- // an initial timeout then kick it down the road a little further as needed
- // when it expires.
- if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
- "Premature timeout ["
- + mQueueName + "] @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for "
- + timeoutTime);
- setBroadcastTimeoutLocked(timeoutTime);
+ if (r.state == BroadcastRecord.WAITING_SERVICES) {
+ // In this case the broadcast had already finished, but we had decided to wait
+ // for started services to finish as well before going on. So if we have actually
+ // waited long enough time timeout the broadcast, let's give up on the whole thing
+ // and just move on to the next.
+ Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
+ ? r.curComponent.flattenToShortString() : "(null)"));
+ r.curComponent = null;
+ r.state = BroadcastRecord.IDLE;
+ processNextBroadcastLocked(false, false);
return;
}
- }
-
- if (r.state == BroadcastRecord.WAITING_SERVICES) {
- // In this case the broadcast had already finished, but we had decided to wait
- // for started services to finish as well before going on. So if we have actually
- // waited long enough time timeout the broadcast, let's give up on the whole thing
- // and just move on to the next.
- Slog.i(TAG, "Waited long enough for: " + (r.curComponent != null
- ? r.curComponent.flattenToShortString() : "(null)"));
- r.curComponent = null;
- r.state = BroadcastRecord.IDLE;
- processNextBroadcastLocked(false, false);
- return;
- }
-
- // If the receiver app is being debugged we quietly ignore unresponsiveness, just
- // tidying up and moving on to the next broadcast without crashing or ANRing this
- // app just because it's stopped at a breakpoint.
- final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
-
- long timeoutDurationMs = now - r.receiverTime;
- Slog.w(TAG, "Timeout of broadcast " + r + " - curFilter=" + r.curFilter + " curReceiver="
- + r.curReceiver + ", started " + timeoutDurationMs + "ms ago");
- r.receiverTime = now;
- if (!debugging) {
- r.anrCount++;
- }
- ProcessRecord app = null;
- TimeoutRecord timeoutRecord = null;
+ // If the receiver app is being debugged we quietly ignore unresponsiveness, just
+ // tidying up and moving on to the next broadcast without crashing or ANRing this
+ // app just because it's stopped at a breakpoint.
+ final boolean debugging = (r.curApp != null && r.curApp.isDebugging());
+
+ long timeoutDurationMs = now - r.receiverTime;
+ Slog.w(TAG, "Timeout of broadcast " + r + " - curFilter=" + r.curFilter
+ + " curReceiver=" + r.curReceiver + ", started " + timeoutDurationMs
+ + "ms ago");
+ r.receiverTime = now;
+ if (!debugging) {
+ r.anrCount++;
+ }
- Object curReceiver;
- if (r.nextReceiver > 0) {
- curReceiver = r.receivers.get(r.nextReceiver-1);
- r.delivery[r.nextReceiver-1] = BroadcastRecord.DELIVERY_TIMEOUT;
- } else {
- curReceiver = r.curReceiver;
- }
- Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
- logBroadcastReceiverDiscardLocked(r);
- if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
- BroadcastFilter bf = (BroadcastFilter)curReceiver;
- if (bf.receiverList.pid != 0
- && bf.receiverList.pid != ActivityManagerService.MY_PID) {
- synchronized (mService.mPidsSelfLocked) {
- app = mService.mPidsSelfLocked.get(
- bf.receiverList.pid);
+ ProcessRecord app = null;
+ Object curReceiver;
+ if (r.nextReceiver > 0) {
+ curReceiver = r.receivers.get(r.nextReceiver - 1);
+ r.delivery[r.nextReceiver - 1] = BroadcastRecord.DELIVERY_TIMEOUT;
+ } else {
+ curReceiver = r.curReceiver;
+ }
+ Slog.w(TAG, "Receiver during timeout of " + r + " : " + curReceiver);
+ logBroadcastReceiverDiscardLocked(r);
+ String anrMessage =
+ "Broadcast of " + r.intent.toString() + ", waited " + timeoutDurationMs + "ms";
+ TimeoutRecord timeoutRecord = TimeoutRecord.forBroadcastReceiver(anrMessage);
+ if (curReceiver != null && curReceiver instanceof BroadcastFilter) {
+ BroadcastFilter bf = (BroadcastFilter) curReceiver;
+ if (bf.receiverList.pid != 0
+ && bf.receiverList.pid != ActivityManagerService.MY_PID) {
+ timeoutRecord.mLatencyTracker.waitingOnPidLockStarted();
+ synchronized (mService.mPidsSelfLocked) {
+ timeoutRecord.mLatencyTracker.waitingOnPidLockEnded();
+ app = mService.mPidsSelfLocked.get(
+ bf.receiverList.pid);
+ }
}
+ } else {
+ app = r.curApp;
}
- } else {
- app = r.curApp;
- }
- if (app != null) {
- String anrMessage =
- "Broadcast of " + r.intent.toString() + ", waited " + timeoutDurationMs
- + "ms";
- timeoutRecord = TimeoutRecord.forBroadcastReceiver(anrMessage);
- }
+ if (mPendingBroadcast == r) {
+ mPendingBroadcast = null;
+ }
- if (mPendingBroadcast == r) {
- mPendingBroadcast = null;
- }
+ // Move on to the next receiver.
+ finishReceiverLocked(r, r.resultCode, r.resultData,
+ r.resultExtras, r.resultAbort, false);
+ scheduleBroadcastsLocked();
- // Move on to the next receiver.
- finishReceiverLocked(r, r.resultCode, r.resultData,
- r.resultExtras, r.resultAbort, false);
- scheduleBroadcastsLocked();
+ // The ANR should only be triggered if we have a process record (app is non-null)
+ if (!debugging && app != null) {
+ mService.appNotResponding(app, timeoutRecord);
+ }
- if (!debugging && timeoutRecord != null) {
- mService.mAnrHelper.appNotResponding(app, timeoutRecord);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
+
}
private final void addBroadcastToHistoryLocked(BroadcastRecord original) {
@@ -1696,18 +1693,15 @@ public class BroadcastQueueImpl extends BroadcastQueue {
}
public boolean cleanupDisabledPackageReceiversLocked(
- String packageName, Set<String> filterByClasses, int userId, boolean doit) {
+ String packageName, Set<String> filterByClasses, int userId) {
boolean didSomething = false;
for (int i = mParallelBroadcasts.size() - 1; i >= 0; i--) {
didSomething |= mParallelBroadcasts.get(i).cleanupDisabledPackageReceiversLocked(
- packageName, filterByClasses, userId, doit);
- if (!doit && didSomething) {
- return true;
- }
+ packageName, filterByClasses, userId, true);
}
didSomething |= mDispatcher.cleanupDisabledPackageReceiversLocked(packageName,
- filterByClasses, userId, doit);
+ filterByClasses, userId, true);
return didSomething;
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
new file mode 100644
index 000000000000..7c236ffe7ba1
--- /dev/null
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -0,0 +1,1260 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
+import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
+
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
+import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME;
+import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
+import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
+import static com.android.server.am.BroadcastRecord.deliveryStateToString;
+import static com.android.server.am.BroadcastRecord.getReceiverPackageName;
+import static com.android.server.am.BroadcastRecord.getReceiverProcessName;
+import static com.android.server.am.BroadcastRecord.getReceiverUid;
+import static com.android.server.am.BroadcastRecord.isDeliveryStateTerminal;
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER;
+import static com.android.server.am.OomAdjuster.OOM_ADJ_REASON_START_RECEIVER;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.app.IApplicationThread;
+import android.app.RemoteServiceException.CannotDeliverBroadcastException;
+import android.app.UidObserver;
+import android.app.usage.UsageEvents.Event;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.util.IndentingPrintWriter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.TimeUtils;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.os.TimeoutRecord;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.am.BroadcastProcessQueue.BroadcastConsumer;
+import com.android.server.am.BroadcastProcessQueue.BroadcastPredicate;
+import com.android.server.am.BroadcastRecord.DeliveryState;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Predicate;
+
+/**
+ * Alternative {@link BroadcastQueue} implementation which pivots broadcasts to
+ * be dispatched on a per-process basis.
+ * <p>
+ * Each process now has its own broadcast queue represented by a
+ * {@link BroadcastProcessQueue} instance. Each queue has a concept of being
+ * "runnable at" a particular time in the future, which supports arbitrarily
+ * pausing or delaying delivery on a per-process basis.
+ * <p>
+ * To keep things easy to reason about, there is a <em>very strong</em>
+ * preference to have broadcast interactions flow through a consistent set of
+ * methods in this specific order:
+ * <ol>
+ * <li>{@link #updateRunnableList} promotes a per-process queue to be runnable
+ * when it has relevant pending broadcasts
+ * <li>{@link #updateRunningList} promotes a runnable queue to be running and
+ * schedules delivery of the first broadcast
+ * <li>{@link #scheduleReceiverColdLocked} requests any needed cold-starts, and
+ * results are reported back via {@link #onApplicationAttachedLocked}
+ * <li>{@link #scheduleReceiverWarmLocked} requests dispatch of the currently
+ * active broadcast to a running app, and results are reported back via
+ * {@link #finishReceiverLocked}
+ * </ol>
+ */
+class BroadcastQueueModernImpl extends BroadcastQueue {
+ BroadcastQueueModernImpl(ActivityManagerService service, Handler handler,
+ BroadcastConstants fgConstants, BroadcastConstants bgConstants) {
+ this(service, handler, fgConstants, bgConstants, new BroadcastSkipPolicy(service),
+ new BroadcastHistory());
+ }
+
+ BroadcastQueueModernImpl(ActivityManagerService service, Handler handler,
+ BroadcastConstants fgConstants, BroadcastConstants bgConstants,
+ BroadcastSkipPolicy skipPolicy, BroadcastHistory history) {
+ super(service, handler, "modern", skipPolicy, history);
+
+ // For the moment, read agnostic constants from foreground
+ mConstants = Objects.requireNonNull(fgConstants);
+ mFgConstants = Objects.requireNonNull(fgConstants);
+ mBgConstants = Objects.requireNonNull(bgConstants);
+
+ mLocalHandler = new Handler(handler.getLooper(), mLocalCallback);
+
+ // We configure runnable size only once at boot; it'd be too complex to
+ // try resizing dynamically at runtime
+ mRunning = new BroadcastProcessQueue[mConstants.MAX_RUNNING_PROCESS_QUEUES];
+ }
+
+ // TODO: add support for replacing pending broadcasts
+ // TODO: add support for merging pending broadcasts
+
+ // TODO: consider reordering foreground broadcasts within queue
+
+ // TODO: pause queues when background services are running
+ // TODO: pause queues when processes are frozen
+
+ /**
+ * Map from UID to per-process broadcast queues. If a UID hosts more than
+ * one process, each additional process is stored as a linked list using
+ * {@link BroadcastProcessQueue#next}.
+ *
+ * @see #getProcessQueue
+ * @see #getOrCreateProcessQueue
+ */
+ @GuardedBy("mService")
+ private final SparseArray<BroadcastProcessQueue> mProcessQueues = new SparseArray<>();
+
+ /**
+ * Head of linked list containing queues which are "runnable". They're
+ * sorted by {@link BroadcastProcessQueue#getRunnableAt()} so that we prefer
+ * dispatching of longer-waiting broadcasts first.
+ *
+ * @see BroadcastProcessQueue#insertIntoRunnableList
+ * @see BroadcastProcessQueue#removeFromRunnableList
+ */
+ private BroadcastProcessQueue mRunnableHead = null;
+
+ /**
+ * Array of queues which are currently "running", which may have gaps that
+ * are {@code null}.
+ *
+ * @see #getRunningSize
+ * @see #getRunningIndexOf
+ */
+ @GuardedBy("mService")
+ private final BroadcastProcessQueue[] mRunning;
+
+ /**
+ * Single queue which is "running" but is awaiting a cold start to be
+ * completed via {@link #onApplicationAttachedLocked}. To optimize for
+ * system health we only request one cold start at a time.
+ */
+ @GuardedBy("mService")
+ private @Nullable BroadcastProcessQueue mRunningColdStart;
+
+ /**
+ * Collection of latches waiting for queue to go idle.
+ */
+ @GuardedBy("mService")
+ private final ArrayList<CountDownLatch> mWaitingForIdle = new ArrayList<>();
+
+ private final BroadcastConstants mConstants;
+ private final BroadcastConstants mFgConstants;
+ private final BroadcastConstants mBgConstants;
+
+ private static final int MSG_UPDATE_RUNNING_LIST = 1;
+ private static final int MSG_DELIVERY_TIMEOUT = 2;
+ private static final int MSG_BG_ACTIVITY_START_TIMEOUT = 3;
+
+ private void enqueueUpdateRunningList() {
+ mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
+ mLocalHandler.sendEmptyMessage(MSG_UPDATE_RUNNING_LIST);
+ }
+
+ private final Handler mLocalHandler;
+
+ private final Handler.Callback mLocalCallback = (msg) -> {
+ switch (msg.what) {
+ case MSG_UPDATE_RUNNING_LIST: {
+ synchronized (mService) {
+ updateRunningList();
+ }
+ return true;
+ }
+ case MSG_DELIVERY_TIMEOUT: {
+ synchronized (mService) {
+ finishReceiverLocked((BroadcastProcessQueue) msg.obj,
+ BroadcastRecord.DELIVERY_TIMEOUT);
+ }
+ return true;
+ }
+ case MSG_BG_ACTIVITY_START_TIMEOUT: {
+ synchronized (mService) {
+ final SomeArgs args = (SomeArgs) msg.obj;
+ final ProcessRecord app = (ProcessRecord) args.arg1;
+ final BroadcastRecord r = (BroadcastRecord) args.arg2;
+ args.recycle();
+ app.removeAllowBackgroundActivityStartsToken(r);
+ }
+ return true;
+ }
+ }
+ return false;
+ };
+
+ /**
+ * Return the total number of active queues contained inside
+ * {@link #mRunning}.
+ */
+ private int getRunningSize() {
+ int size = 0;
+ for (int i = 0; i < mRunning.length; i++) {
+ if (mRunning[i] != null) size++;
+ }
+ return size;
+ }
+
+ /**
+ * Return the first index of the given value contained inside
+ * {@link #mRunning}, otherwise {@code -1}.
+ */
+ private int getRunningIndexOf(@Nullable BroadcastProcessQueue test) {
+ for (int i = 0; i < mRunning.length; i++) {
+ if (mRunning[i] == test) return i;
+ }
+ return -1;
+ }
+
+ /**
+ * Consider updating the list of "runnable" queues, specifically with
+ * relation to the given queue.
+ * <p>
+ * Typically called when {@link BroadcastProcessQueue#getRunnableAt()} might
+ * have changed, since that influences the order in which we'll promote a
+ * "runnable" queue to be "running."
+ */
+ @GuardedBy("mService")
+ private void updateRunnableList(@NonNull BroadcastProcessQueue queue) {
+ if (getRunningIndexOf(queue) >= 0) {
+ // Already running; they'll be reinserted into the runnable list
+ // once they finish running, so no need to update them now
+ return;
+ }
+
+ final boolean wantQueue = queue.isRunnable();
+ final boolean inQueue = (queue == mRunnableHead) || (queue.runnableAtPrev != null)
+ || (queue.runnableAtNext != null);
+ if (wantQueue) {
+ if (inQueue) {
+ // We're in a good state, but our position within the linked
+ // list might need to move based on a runnableAt change
+ final boolean prevLower = (queue.runnableAtPrev != null)
+ ? queue.runnableAtPrev.getRunnableAt() <= queue.getRunnableAt() : true;
+ final boolean nextHigher = (queue.runnableAtNext != null)
+ ? queue.runnableAtNext.getRunnableAt() >= queue.getRunnableAt() : true;
+ if (!prevLower || !nextHigher) {
+ mRunnableHead = removeFromRunnableList(mRunnableHead, queue);
+ mRunnableHead = insertIntoRunnableList(mRunnableHead, queue);
+ }
+ } else {
+ mRunnableHead = insertIntoRunnableList(mRunnableHead, queue);
+ }
+ } else if (inQueue) {
+ mRunnableHead = removeFromRunnableList(mRunnableHead, queue);
+ }
+
+ // If app isn't running, and there's nothing in the queue, clean up
+ if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) {
+ removeProcessQueue(queue.processName, queue.uid);
+ }
+ }
+
+ /**
+ * Consider updating the list of "running" queues.
+ * <p>
+ * This method can promote "runnable" queues to become "running", subject to
+ * a maximum of {@link BroadcastConstants#MAX_RUNNING_PROCESS_QUEUES} warm
+ * processes and only one pending cold-start.
+ */
+ @GuardedBy("mService")
+ private void updateRunningList() {
+ int avail = mRunning.length - getRunningSize();
+ if (avail == 0) return;
+
+ final int cookie = traceBegin(TAG, "updateRunningList");
+ final long now = SystemClock.uptimeMillis();
+
+ // If someone is waiting to go idle, everything is runnable now
+ final boolean waitingForIdle = !mWaitingForIdle.isEmpty();
+
+ // We're doing an update now, so remove any future update requests;
+ // we'll repost below if needed
+ mLocalHandler.removeMessages(MSG_UPDATE_RUNNING_LIST);
+
+ boolean updateOomAdj = false;
+ BroadcastProcessQueue queue = mRunnableHead;
+ while (queue != null && avail > 0) {
+ BroadcastProcessQueue nextQueue = queue.runnableAtNext;
+ final long runnableAt = queue.getRunnableAt();
+
+ // If queues beyond this point aren't ready to run yet, schedule
+ // another pass when they'll be runnable
+ if (runnableAt > now && !waitingForIdle) {
+ mLocalHandler.sendEmptyMessageAtTime(MSG_UPDATE_RUNNING_LIST, runnableAt);
+ break;
+ }
+
+ // We might not have heard about a newly running process yet, so
+ // consider refreshing if we think we're cold
+ updateWarmProcess(queue);
+
+ final boolean processWarm = queue.isProcessWarm();
+ if (!processWarm) {
+ // We only offer to run one cold-start at a time to preserve
+ // system resources; below we either claim that single slot or
+ // skip to look for another warm process
+ if (mRunningColdStart == null) {
+ mRunningColdStart = queue;
+ } else {
+ // Move to considering next runnable queue
+ queue = nextQueue;
+ continue;
+ }
+ }
+
+ if (DEBUG_BROADCAST) logv("Promoting " + queue
+ + " from runnable to running; process is " + queue.app);
+
+ // Allocate this available permit and start running!
+ final int queueIndex = getRunningIndexOf(null);
+ mRunning[queueIndex] = queue;
+ avail--;
+
+ // Remove ourselves from linked list of runnable things
+ mRunnableHead = removeFromRunnableList(mRunnableHead, queue);
+
+ // Emit all trace events for this process into a consistent track
+ queue.traceTrackName = TAG + ".mRunning[" + queueIndex + "]";
+
+ // If we're already warm, boost OOM adjust now; if cold we'll boost
+ // it after the app has been started
+ if (processWarm) {
+ notifyStartedRunning(queue);
+ }
+
+ // If we're already warm, schedule next pending broadcast now;
+ // otherwise we'll wait for the cold start to circle back around
+ queue.makeActiveNextPending();
+ if (processWarm) {
+ queue.traceProcessRunningBegin();
+ scheduleReceiverWarmLocked(queue);
+ } else {
+ queue.traceProcessStartingBegin();
+ scheduleReceiverColdLocked(queue);
+ }
+
+ // We've moved at least one process into running state above, so we
+ // need to kick off an OOM adjustment pass
+ updateOomAdj = true;
+
+ // Move to considering next runnable queue
+ queue = nextQueue;
+ }
+
+ if (updateOomAdj) {
+ mService.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_START_RECEIVER);
+ }
+
+ if (waitingForIdle && isIdleLocked()) {
+ mWaitingForIdle.forEach((latch) -> latch.countDown());
+ mWaitingForIdle.clear();
+ }
+
+ traceEnd(TAG, cookie);
+ }
+
+ @Override
+ public boolean onApplicationAttachedLocked(@NonNull ProcessRecord app) {
+ boolean didSomething = false;
+ if ((mRunningColdStart != null) && (mRunningColdStart.app == app)) {
+ // We've been waiting for this app to cold start, and it's ready
+ // now; dispatch its next broadcast and clear the slot
+ final BroadcastProcessQueue queue = mRunningColdStart;
+ mRunningColdStart = null;
+
+ queue.traceProcessEnd();
+ queue.traceProcessRunningBegin();
+ scheduleReceiverWarmLocked(queue);
+
+ // We might be willing to kick off another cold start
+ enqueueUpdateRunningList();
+ didSomething = true;
+ }
+ return didSomething;
+ }
+
+ @Override
+ public void onApplicationTimeoutLocked(@NonNull ProcessRecord app) {
+ onApplicationCleanupLocked(app);
+ }
+
+ @Override
+ public void onApplicationProblemLocked(@NonNull ProcessRecord app) {
+ onApplicationCleanupLocked(app);
+ }
+
+ @Override
+ public void onApplicationCleanupLocked(@NonNull ProcessRecord app) {
+ if ((mRunningColdStart != null) && (mRunningColdStart.app == app)) {
+ // We've been waiting for this app to cold start, and it had
+ // trouble; clear the slot and fail delivery below
+ mRunningColdStart = null;
+
+ // We might be willing to kick off another cold start
+ enqueueUpdateRunningList();
+ }
+
+ final BroadcastProcessQueue queue = getProcessQueue(app);
+ if (queue != null) {
+ queue.app = null;
+
+ // If queue was running a broadcast, fail it
+ if (queue.isActive()) {
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
+ }
+
+ // Skip any pending registered receivers, since the old process
+ // would never be around to receive them
+ queue.removeMatchingBroadcasts((r, i) -> {
+ return (r.receivers.get(i) instanceof BroadcastFilter);
+ }, mBroadcastConsumerSkip);
+
+ // If queue has nothing else pending, consider cleaning it
+ if (queue.isEmpty()) {
+ updateRunnableList(queue);
+ }
+ }
+ }
+
+ @Override
+ public int getPreferredSchedulingGroupLocked(@NonNull ProcessRecord app) {
+ final BroadcastProcessQueue queue = getProcessQueue(app);
+ if ((queue != null) && getRunningIndexOf(queue) >= 0) {
+ return queue.getPreferredSchedulingGroupLocked();
+ }
+ return ProcessList.SCHED_GROUP_UNDEFINED;
+ }
+
+ @Override
+ public void enqueueBroadcastLocked(@NonNull BroadcastRecord r) {
+ // TODO: handle empty receivers to deliver result immediately
+ if (r.receivers == null) return;
+
+ final IntentFilter removeMatchingFilter = (r.options != null)
+ ? r.options.getRemoveMatchingFilter() : null;
+ if (removeMatchingFilter != null) {
+ final Predicate<Intent> removeMatching = removeMatchingFilter.asPredicate();
+ skipMatchingBroadcasts(QUEUE_PREDICATE_ANY, (testRecord, testReceiver) -> {
+ // We only allow caller to clear broadcasts they enqueued
+ return (testRecord.callingUid == r.callingUid)
+ && removeMatching.test(testRecord.intent);
+ });
+ }
+
+ r.enqueueTime = SystemClock.uptimeMillis();
+ r.enqueueRealTime = SystemClock.elapsedRealtime();
+ r.enqueueClockTime = System.currentTimeMillis();
+
+ for (int i = 0; i < r.receivers.size(); i++) {
+ final Object receiver = r.receivers.get(i);
+ final BroadcastProcessQueue queue = getOrCreateProcessQueue(
+ getReceiverProcessName(receiver), getReceiverUid(receiver));
+ queue.enqueueBroadcast(r, i);
+ updateRunnableList(queue);
+ enqueueUpdateRunningList();
+ }
+ }
+
+ /**
+ * Schedule the currently active broadcast on the given queue when we know
+ * the process is cold. This kicks off a cold start and will eventually call
+ * through to {@link #scheduleReceiverWarmLocked} once it's ready.
+ */
+ private void scheduleReceiverColdLocked(@NonNull BroadcastProcessQueue queue) {
+ checkState(queue.isActive(), "isActive");
+
+ // Remember that active broadcast was scheduled via a cold start
+ queue.setActiveViaColdStart(true);
+
+ final BroadcastRecord r = queue.getActive();
+ final int index = queue.getActiveIndex();
+ final Object receiver = r.receivers.get(index);
+
+ // Ignore registered receivers from a previous PID
+ if (receiver instanceof BroadcastFilter) {
+ mRunningColdStart = null;
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
+ return;
+ }
+
+ final ApplicationInfo info = ((ResolveInfo) receiver).activityInfo.applicationInfo;
+ final ComponentName component = ((ResolveInfo) receiver).activityInfo.getComponentName();
+
+ final int intentFlags = r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND;
+ final HostingRecord hostingRecord = new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST,
+ component, r.intent.getAction(), r.getHostingRecordTriggerType());
+ final boolean isActivityCapable = (r.options != null
+ && r.options.getTemporaryAppAllowlistDuration() > 0);
+ final int zygotePolicyFlags = isActivityCapable ? ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE
+ : ZYGOTE_POLICY_FLAG_EMPTY;
+ final boolean allowWhileBooting = (r.intent.getFlags()
+ & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0;
+
+ if (DEBUG_BROADCAST) logv("Scheduling " + r + " to cold " + queue);
+ queue.app = mService.startProcessLocked(queue.processName, info, true, intentFlags,
+ hostingRecord, zygotePolicyFlags, allowWhileBooting, false);
+ if (queue.app != null) {
+ notifyStartedRunning(queue);
+ } else {
+ mRunningColdStart = null;
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
+ return;
+ }
+ }
+
+ /**
+ * Schedule the currently active broadcast on the given queue when we know
+ * the process is warm.
+ * <p>
+ * There is a <em>very strong</em> preference to consistently handle all
+ * results by calling through to {@link #finishReceiverLocked}, both in the
+ * case where a broadcast is handled by a remote app, and the case where the
+ * broadcast was finished locally without the remote app being involved.
+ */
+ private void scheduleReceiverWarmLocked(@NonNull BroadcastProcessQueue queue) {
+ checkState(queue.isActive(), "isActive");
+
+ final ProcessRecord app = queue.app;
+ final BroadcastRecord r = queue.getActive();
+ final int index = queue.getActiveIndex();
+ final Object receiver = r.receivers.get(index);
+
+ // If someone already finished this broadcast, finish immediately
+ final int oldDeliveryState = getDeliveryState(r, index);
+ if (isDeliveryStateTerminal(oldDeliveryState)) {
+ finishReceiverLocked(queue, oldDeliveryState);
+ return;
+ }
+
+ // Consider additional cases where we'd want to finish immediately
+ if (app.isInFullBackup()) {
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
+ return;
+ }
+ if (mSkipPolicy.shouldSkip(r, receiver)) {
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
+ return;
+ }
+ final Intent receiverIntent = r.getReceiverIntent(receiver);
+ if (receiverIntent == null) {
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
+ return;
+ }
+
+ // Ignore registered receivers from a previous PID
+ if ((receiver instanceof BroadcastFilter)
+ && ((BroadcastFilter) receiver).receiverList.pid != app.getPid()) {
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_SKIPPED);
+ return;
+ }
+
+ if (mService.mProcessesReady && !r.timeoutExempt) {
+ final long timeout = r.isForeground() ? mFgConstants.TIMEOUT : mBgConstants.TIMEOUT;
+ mLocalHandler.sendMessageDelayed(
+ Message.obtain(mLocalHandler, MSG_DELIVERY_TIMEOUT, queue), timeout);
+ }
+
+ if (r.allowBackgroundActivityStarts) {
+ app.addOrUpdateAllowBackgroundActivityStartsToken(r, r.mBackgroundActivityStartsToken);
+
+ final long timeout = r.isForeground() ? mFgConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT
+ : mBgConstants.ALLOW_BG_ACTIVITY_START_TIMEOUT;
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = app;
+ args.arg2 = r;
+ mLocalHandler.sendMessageDelayed(
+ Message.obtain(mLocalHandler, MSG_BG_ACTIVITY_START_TIMEOUT, args), timeout);
+ }
+
+ if (r.options != null && r.options.getTemporaryAppAllowlistDuration() > 0) {
+ mService.tempAllowlistUidLocked(queue.uid,
+ r.options.getTemporaryAppAllowlistDuration(),
+ r.options.getTemporaryAppAllowlistReasonCode(), r.toShortString(),
+ r.options.getTemporaryAppAllowlistType(), r.callingUid);
+ }
+
+ if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);
+ setDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED);
+
+ final IApplicationThread thread = app.getThread();
+ if (thread != null) {
+ try {
+ if (receiver instanceof BroadcastFilter) {
+ notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);
+ thread.scheduleRegisteredReceiver(
+ ((BroadcastFilter) receiver).receiverList.receiver, receiverIntent,
+ r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky,
+ r.userId, app.mState.getReportedProcState());
+
+ // TODO: consider making registered receivers of unordered
+ // broadcasts report results to detect ANRs
+ if (!r.ordered) {
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_DELIVERED);
+ }
+ } else {
+ notifyScheduleReceiver(app, r, (ResolveInfo) receiver);
+ thread.scheduleReceiver(receiverIntent, ((ResolveInfo) receiver).activityInfo,
+ null, r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
+ app.mState.getReportedProcState());
+ }
+ } catch (RemoteException e) {
+ final String msg = "Failed to schedule " + r + " to " + receiver
+ + " via " + app + ": " + e;
+ Slog.w(TAG, msg);
+ app.scheduleCrashLocked(msg, CannotDeliverBroadcastException.TYPE_ID, null);
+ app.setKilled(true);
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
+ }
+ } else {
+ finishReceiverLocked(queue, BroadcastRecord.DELIVERY_FAILURE);
+ }
+ }
+
+ /**
+ * Schedule the final {@link BroadcastRecord#resultTo} delivery for an
+ * ordered broadcast; assumes the sender is still a warm process.
+ */
+ private void scheduleResultTo(@NonNull BroadcastRecord r) {
+ if ((r.callerApp == null) || (r.resultTo == null)) return;
+ final ProcessRecord app = r.callerApp;
+ final IApplicationThread thread = app.getThread();
+ if (thread != null) {
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
+ app, OOM_ADJ_REASON_FINISH_RECEIVER);
+ try {
+ thread.scheduleRegisteredReceiver(r.resultTo, r.intent,
+ r.resultCode, r.resultData, r.resultExtras, false, r.initialSticky,
+ r.userId, app.mState.getReportedProcState());
+ } catch (RemoteException e) {
+ final String msg = "Failed to schedule result of " + r + " via " + app + ": " + e;
+ Slog.w(TAG, msg);
+ app.scheduleCrashLocked(msg, CannotDeliverBroadcastException.TYPE_ID, null);
+ }
+ }
+ }
+
+ @Override
+ public boolean finishReceiverLocked(@NonNull ProcessRecord app, int resultCode,
+ @Nullable String resultData, @Nullable Bundle resultExtras, boolean resultAbort,
+ boolean waitForServices) {
+ final BroadcastProcessQueue queue = getProcessQueue(app);
+ final BroadcastRecord r = queue.getActive();
+ r.resultCode = resultCode;
+ r.resultData = resultData;
+ r.resultExtras = resultExtras;
+ if (!r.isNoAbort()) {
+ r.resultAbort = resultAbort;
+ }
+
+ // When the caller aborted an ordered broadcast, we mark all remaining
+ // receivers as skipped
+ if (r.ordered && r.resultAbort) {
+ for (int i = r.finishedCount + 1; i < r.receivers.size(); i++) {
+ setDeliveryState(null, null, r, i, r.receivers.get(i),
+ BroadcastRecord.DELIVERY_SKIPPED);
+ }
+ }
+
+ return finishReceiverLocked(queue, BroadcastRecord.DELIVERY_DELIVERED);
+ }
+
+ private boolean finishReceiverLocked(@NonNull BroadcastProcessQueue queue,
+ @DeliveryState int deliveryState) {
+ checkState(queue.isActive(), "isActive");
+
+ final ProcessRecord app = queue.app;
+ final BroadcastRecord r = queue.getActive();
+ final int index = queue.getActiveIndex();
+ final Object receiver = r.receivers.get(index);
+
+ setDeliveryState(queue, app, r, index, receiver, deliveryState);
+
+ if (deliveryState == BroadcastRecord.DELIVERY_TIMEOUT) {
+ r.anrCount++;
+ if (app != null && !app.isDebugging()) {
+ mService.appNotResponding(queue.app, TimeoutRecord
+ .forBroadcastReceiver("Broadcast of " + r.toShortString()));
+ }
+ } else {
+ mLocalHandler.removeMessages(MSG_DELIVERY_TIMEOUT, queue);
+ }
+
+ // Even if we have more broadcasts, if we've made reasonable progress
+ // and someone else is waiting, retire ourselves to avoid starvation
+ final boolean shouldRetire = (mRunnableHead != null)
+ && (queue.getActiveCountSinceIdle() >= mConstants.MAX_RUNNING_ACTIVE_BROADCASTS);
+
+ if (queue.isRunnable() && queue.isProcessWarm() && !shouldRetire) {
+ // We're on a roll; move onto the next broadcast for this process
+ queue.makeActiveNextPending();
+ scheduleReceiverWarmLocked(queue);
+ return true;
+ } else {
+ // We've drained running broadcasts; maybe move back to runnable
+ queue.makeActiveIdle();
+ queue.traceProcessEnd();
+
+ final int queueIndex = getRunningIndexOf(queue);
+ mRunning[queueIndex] = null;
+ updateRunnableList(queue);
+ enqueueUpdateRunningList();
+
+ // Tell other OS components that app is not actively running, giving
+ // a chance to update OOM adjustment
+ notifyStoppedRunning(queue);
+ return false;
+ }
+ }
+
+ /**
+ * Set the delivery state on the given broadcast, then apply any additional
+ * bookkeeping related to ordered broadcasts.
+ */
+ private void setDeliveryState(@Nullable BroadcastProcessQueue queue,
+ @Nullable ProcessRecord app, @NonNull BroadcastRecord r, int index,
+ @NonNull Object receiver, @DeliveryState int newDeliveryState) {
+ final int oldDeliveryState = getDeliveryState(r, index);
+
+ if (newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) {
+ Slog.w(TAG, "Delivery state of " + r + " to " + receiver
+ + " via " + app + " changed from "
+ + deliveryStateToString(oldDeliveryState) + " to "
+ + deliveryStateToString(newDeliveryState));
+ }
+
+ // Only apply state when we haven't already reached a terminal state;
+ // this is how we ignore racing timeout messages
+ if (!isDeliveryStateTerminal(oldDeliveryState)) {
+ r.setDeliveryState(index, newDeliveryState);
+ }
+
+ // Emit any relevant tracing results when we're changing the delivery
+ // state as part of running from a queue
+ if (queue != null) {
+ if (newDeliveryState == BroadcastRecord.DELIVERY_SCHEDULED) {
+ queue.traceActiveBegin();
+ } else if ((oldDeliveryState == BroadcastRecord.DELIVERY_SCHEDULED)
+ && isDeliveryStateTerminal(newDeliveryState)) {
+ queue.traceActiveEnd();
+ }
+ }
+
+ // If we're moving into a terminal state, we might have internal
+ // bookkeeping to update for ordered broadcasts
+ if (!isDeliveryStateTerminal(oldDeliveryState)
+ && isDeliveryStateTerminal(newDeliveryState)) {
+ r.finishedCount++;
+ notifyFinishReceiver(queue, r, index, receiver);
+
+ if (r.ordered) {
+ if (r.finishedCount < r.receivers.size()) {
+ // We just finished an ordered receiver, which means the
+ // next receiver might now be runnable
+ final Object nextReceiver = r.receivers.get(r.finishedCount);
+ final BroadcastProcessQueue nextQueue = getProcessQueue(
+ getReceiverProcessName(nextReceiver), getReceiverUid(nextReceiver));
+ nextQueue.invalidateRunnableAt();
+ updateRunnableList(nextQueue);
+ } else {
+ // Everything finished, so deliver final result
+ scheduleResultTo(r);
+ }
+ }
+ }
+ }
+
+ private @DeliveryState int getDeliveryState(@NonNull BroadcastRecord r, int index) {
+ return r.getDeliveryState(index);
+ }
+
+ @Override
+ public boolean cleanupDisabledPackageReceiversLocked(@Nullable String packageName,
+ @Nullable Set<String> filterByClasses, int userId) {
+ final Predicate<BroadcastProcessQueue> queuePredicate;
+ final BroadcastPredicate broadcastPredicate;
+ if (packageName != null) {
+ // Caller provided a package and user ID, so we're focused on queues
+ // belonging to a specific UID
+ final int uid = mService.mPackageManagerInt.getPackageUid(
+ packageName, PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+ queuePredicate = (q) -> {
+ return q.uid == uid;
+ };
+
+ // If caller provided a set of classes, filter to skip only those;
+ // otherwise we skip all broadcasts
+ if (filterByClasses != null) {
+ broadcastPredicate = (r, i) -> {
+ final Object receiver = r.receivers.get(i);
+ if (receiver instanceof ResolveInfo) {
+ final ActivityInfo info = ((ResolveInfo) receiver).activityInfo;
+ return packageName.equals(info.packageName)
+ && filterByClasses.contains(info.name);
+ } else {
+ return false;
+ }
+ };
+ } else {
+ broadcastPredicate = (r, i) -> {
+ final Object receiver = r.receivers.get(i);
+ return packageName.equals(getReceiverPackageName(receiver));
+ };
+ }
+ } else {
+ // Caller is cleaning up an entire user ID; skip all broadcasts
+ queuePredicate = (q) -> {
+ return UserHandle.getUserId(q.uid) == userId;
+ };
+ broadcastPredicate = BROADCAST_PREDICATE_ANY;
+ }
+ return skipMatchingBroadcasts(queuePredicate, broadcastPredicate);
+ }
+
+ private static final Predicate<BroadcastProcessQueue> QUEUE_PREDICATE_ANY =
+ (q) -> true;
+ private static final BroadcastPredicate BROADCAST_PREDICATE_ANY =
+ (r, i) -> true;
+
+ /**
+ * Typical consumer that will skip the given broadcast, usually as a result
+ * of it matching a predicate.
+ */
+ private final BroadcastConsumer mBroadcastConsumerSkip = (r, i) -> {
+ setDeliveryState(null, null, r, i, r.receivers.get(i), BroadcastRecord.DELIVERY_SKIPPED);
+ };
+
+ private boolean skipMatchingBroadcasts(
+ @NonNull Predicate<BroadcastProcessQueue> queuePredicate,
+ @NonNull BroadcastPredicate broadcastPredicate) {
+ // Note that we carefully preserve any "skipped" broadcasts in their
+ // queues so that we follow our normal flow for "finishing" a broadcast,
+ // which is where we handle things like ordered broadcasts.
+ boolean didSomething = false;
+ for (int i = 0; i < mProcessQueues.size(); i++) {
+ BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
+ while (leaf != null) {
+ if (queuePredicate.test(leaf)) {
+ didSomething |= leaf.removeMatchingBroadcasts(broadcastPredicate,
+ mBroadcastConsumerSkip);
+ }
+ leaf = leaf.processNameNext;
+ }
+ }
+ return didSomething;
+ }
+
+ @Override
+ public void start(@NonNull ContentResolver resolver) {
+ mFgConstants.startObserving(mHandler, resolver);
+ mBgConstants.startObserving(mHandler, resolver);
+
+ mService.registerUidObserver(new UidObserver() {
+ @Override
+ public void onUidCachedChanged(int uid, boolean cached) {
+ synchronized (mService) {
+ BroadcastProcessQueue leaf = mProcessQueues.get(uid);
+ while (leaf != null) {
+ leaf.setProcessCached(cached);
+ updateRunnableList(leaf);
+ leaf = leaf.processNameNext;
+ }
+ enqueueUpdateRunningList();
+ }
+ }
+ }, ActivityManager.UID_OBSERVER_CACHED, 0, "android");
+ }
+
+ @Override
+ public boolean isIdleLocked() {
+ return (mRunnableHead == null) && (getRunningSize() == 0);
+ }
+
+ @Override
+ public void waitForIdle(@Nullable PrintWriter pw) {
+ final CountDownLatch latch = new CountDownLatch(1);
+ synchronized (mService) {
+ mWaitingForIdle.add(latch);
+ }
+ enqueueUpdateRunningList();
+ try {
+ latch.await();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void waitForBarrier(@Nullable PrintWriter pw) {
+ // TODO: implement
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String describeStateLocked() {
+ return getRunningSize() + " running";
+ }
+
+ @Override
+ public boolean isDelayBehindServices() {
+ // TODO: implement
+ return false;
+ }
+
+ @Override
+ public void backgroundServicesFinishedLocked(int userId) {
+ // TODO: implement
+ }
+
+ private int traceBegin(String trackName, String methodName) {
+ final int cookie = methodName.hashCode();
+ Trace.asyncTraceForTrackBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ trackName, methodName, cookie);
+ return cookie;
+ }
+
+ private void traceEnd(String trackName, int cookie) {
+ Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ trackName, cookie);
+ }
+
+ private void updateWarmProcess(@NonNull BroadcastProcessQueue queue) {
+ if (!queue.isProcessWarm()) {
+ queue.app = mService.getProcessRecordLocked(queue.processName, queue.uid);
+ }
+ }
+
+ /**
+ * Inform other parts of OS that the given broadcast queue has started
+ * running, typically for internal bookkeeping.
+ */
+ private void notifyStartedRunning(@NonNull BroadcastProcessQueue queue) {
+ if (queue.app != null) {
+ queue.app.mReceivers.incrementCurReceivers();
+
+ queue.app.mState.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
+
+ // Don't bump its LRU position if it's in the background restricted.
+ if (mService.mInternal.getRestrictionLevel(
+ queue.uid) < ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET) {
+ mService.updateLruProcessLocked(queue.app, false, null);
+ }
+
+ mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(queue.app,
+ OOM_ADJ_REASON_START_RECEIVER);
+
+ mService.enqueueOomAdjTargetLocked(queue.app);
+ }
+ }
+
+ /**
+ * Inform other parts of OS that the given broadcast queue has stopped
+ * running, typically for internal bookkeeping.
+ */
+ private void notifyStoppedRunning(@NonNull BroadcastProcessQueue queue) {
+ if (queue.app != null) {
+ // Update during our next pass; no need for an immediate update
+ mService.enqueueOomAdjTargetLocked(queue.app);
+
+ queue.app.mReceivers.decrementCurReceivers();
+ }
+ }
+
+ /**
+ * Inform other parts of OS that the given broadcast was just scheduled for
+ * a registered receiver, typically for internal bookkeeping.
+ */
+ private void notifyScheduleRegisteredReceiver(@NonNull ProcessRecord app,
+ @NonNull BroadcastRecord r, @NonNull BroadcastFilter receiver) {
+ reportUsageStatsBroadcastDispatched(app, r);
+ }
+
+ /**
+ * Inform other parts of OS that the given broadcast was just scheduled for
+ * a manifest receiver, typically for internal bookkeeping.
+ */
+ private void notifyScheduleReceiver(@NonNull ProcessRecord app,
+ @NonNull BroadcastRecord r, @NonNull ResolveInfo receiver) {
+ reportUsageStatsBroadcastDispatched(app, r);
+
+ final String receiverPackageName = receiver.activityInfo.packageName;
+ app.addPackage(receiverPackageName,
+ receiver.activityInfo.applicationInfo.longVersionCode, mService.mProcessStats);
+
+ final boolean targetedBroadcast = r.intent.getComponent() != null;
+ final boolean targetedSelf = Objects.equals(r.callerPackage, receiverPackageName);
+ if (targetedBroadcast && !targetedSelf) {
+ mService.mUsageStatsService.reportEvent(receiverPackageName,
+ r.userId, Event.APP_COMPONENT_USED);
+ }
+
+ mService.notifyPackageUse(receiverPackageName,
+ PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER);
+
+ mService.mPackageManagerInt.setPackageStoppedState(
+ receiverPackageName, false, r.userId);
+ }
+
+ private void reportUsageStatsBroadcastDispatched(@NonNull ProcessRecord app,
+ @NonNull BroadcastRecord r) {
+ final long idForResponseEvent = (r.options != null)
+ ? r.options.getIdForResponseEvent() : 0L;
+ if (idForResponseEvent <= 0) return;
+
+ final String targetPackage;
+ if (r.intent.getPackage() != null) {
+ targetPackage = r.intent.getPackage();
+ } else if (r.intent.getComponent() != null) {
+ targetPackage = r.intent.getComponent().getPackageName();
+ } else {
+ targetPackage = null;
+ }
+ if (targetPackage == null) return;
+
+ mService.mUsageStatsService.reportBroadcastDispatched(r.callingUid, targetPackage,
+ UserHandle.of(r.userId), idForResponseEvent, SystemClock.elapsedRealtime(),
+ mService.getUidStateLocked(app.uid));
+ }
+
+ /**
+ * Inform other parts of OS that the given broadcast was just finished,
+ * typically for internal bookkeeping.
+ */
+ private void notifyFinishReceiver(@Nullable BroadcastProcessQueue queue,
+ @NonNull BroadcastRecord r, int index, @NonNull Object receiver) {
+ // Report statistics for each individual receiver
+ final int uid = getReceiverUid(receiver);
+ final int senderUid = (r.callingUid == -1) ? Process.SYSTEM_UID : r.callingUid;
+ final String actionName = ActivityManagerService.getShortAction(r.intent.getAction());
+ final int receiverType = (receiver instanceof BroadcastFilter)
+ ? BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__RUNTIME
+ : BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
+ final int type;
+ if (queue == null) {
+ type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_UNKNOWN;
+ } else if (queue.getActiveViaColdStart()) {
+ type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
+ } else {
+ type = BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_WARM;
+ }
+ // With the new per-process queues, there's no delay between being
+ // "dispatched" and "scheduled", so we report no "receive delay"
+ final long dispatchDelay = r.scheduledTime[index] - r.enqueueTime;
+ final long receiveDelay = 0;
+ final long finishDelay = r.duration[index];
+ FrameworkStatsLog.write(BROADCAST_DELIVERY_EVENT_REPORTED, uid, senderUid, actionName,
+ receiverType, type, dispatchDelay, receiveDelay, finishDelay);
+
+ final boolean recordFinished = (r.finishedCount == r.receivers.size());
+ if (recordFinished) {
+ mHistory.addBroadcastToHistoryLocked(r);
+
+ r.nextReceiver = r.receivers.size();
+ BroadcastQueueImpl.logBootCompletedBroadcastCompletionLatencyIfPossible(r);
+
+ if (r.intent.getComponent() == null && r.intent.getPackage() == null
+ && (r.intent.getFlags() & Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
+ int manifestCount = 0;
+ int manifestSkipCount = 0;
+ for (int i = 0; i < r.receivers.size(); i++) {
+ if (r.receivers.get(i) instanceof ResolveInfo) {
+ manifestCount++;
+ if (r.delivery[i] == BroadcastRecord.DELIVERY_SKIPPED) {
+ manifestSkipCount++;
+ }
+ }
+ }
+
+ final long dispatchTime = SystemClock.uptimeMillis() - r.enqueueTime;
+ mService.addBroadcastStatLocked(r.intent.getAction(), r.callerPackage,
+ manifestCount, manifestSkipCount, dispatchTime);
+ }
+ }
+ }
+
+ @VisibleForTesting
+ @NonNull BroadcastProcessQueue getOrCreateProcessQueue(@NonNull ProcessRecord app) {
+ return getOrCreateProcessQueue(app.processName, app.info.uid);
+ }
+
+ @VisibleForTesting
+ @NonNull BroadcastProcessQueue getOrCreateProcessQueue(@NonNull String processName,
+ int uid) {
+ BroadcastProcessQueue leaf = mProcessQueues.get(uid);
+ while (leaf != null) {
+ if (Objects.equals(leaf.processName, processName)) {
+ return leaf;
+ } else if (leaf.processNameNext == null) {
+ break;
+ }
+ leaf = leaf.processNameNext;
+ }
+
+ BroadcastProcessQueue created = new BroadcastProcessQueue(mConstants, processName, uid);
+ created.app = mService.getProcessRecordLocked(processName, uid);
+
+ if (leaf == null) {
+ mProcessQueues.put(uid, created);
+ } else {
+ leaf.processNameNext = created;
+ }
+ return created;
+ }
+
+ @VisibleForTesting
+ @Nullable BroadcastProcessQueue getProcessQueue(@NonNull ProcessRecord app) {
+ return getProcessQueue(app.processName, app.info.uid);
+ }
+
+ @VisibleForTesting
+ @Nullable BroadcastProcessQueue getProcessQueue(@NonNull String processName, int uid) {
+ BroadcastProcessQueue leaf = mProcessQueues.get(uid);
+ while (leaf != null) {
+ if (Objects.equals(leaf.processName, processName)) {
+ return leaf;
+ }
+ leaf = leaf.processNameNext;
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ @Nullable BroadcastProcessQueue removeProcessQueue(@NonNull ProcessRecord app) {
+ return removeProcessQueue(app.processName, app.info.uid);
+ }
+
+ @VisibleForTesting
+ @Nullable BroadcastProcessQueue removeProcessQueue(@NonNull String processName,
+ int uid) {
+ BroadcastProcessQueue prev = null;
+ BroadcastProcessQueue leaf = mProcessQueues.get(uid);
+ while (leaf != null) {
+ if (Objects.equals(leaf.processName, processName)) {
+ if (prev != null) {
+ prev.processNameNext = leaf.processNameNext;
+ } else {
+ if (leaf.processNameNext != null) {
+ mProcessQueues.put(uid, leaf.processNameNext);
+ } else {
+ mProcessQueues.remove(uid);
+ }
+ }
+ return leaf;
+ }
+ prev = leaf;
+ leaf = leaf.processNameNext;
+ }
+ return null;
+ }
+
+ @Override
+ public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
+ long token = proto.start(fieldId);
+ proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
+ mHistory.dumpDebug(proto);
+ proto.end(token);
+ }
+
+ @Override
+ public boolean dumpLocked(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
+ @NonNull String[] args, int opti, boolean dumpAll, @Nullable String dumpPackage,
+ boolean needSep) {
+ final long now = SystemClock.uptimeMillis();
+ final IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ ipw.increaseIndent();
+
+ ipw.println();
+ ipw.println("📋 Per-process queues:");
+ ipw.increaseIndent();
+ for (int i = 0; i < mProcessQueues.size(); i++) {
+ BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
+ while (leaf != null) {
+ leaf.dumpLocked(ipw);
+ leaf = leaf.processNameNext;
+ }
+ }
+ ipw.decreaseIndent();
+
+ ipw.println();
+ ipw.println("🧍 Runnable:");
+ ipw.increaseIndent();
+ if (mRunnableHead == null) {
+ ipw.println("(none)");
+ } else {
+ BroadcastProcessQueue queue = mRunnableHead;
+ while (queue != null) {
+ TimeUtils.formatDuration(queue.getRunnableAt(), now, ipw);
+ ipw.print(' ');
+ ipw.println(queue.toShortString());
+ queue = queue.runnableAtNext;
+ }
+ }
+ ipw.decreaseIndent();
+
+ ipw.println();
+ ipw.println("🏃 Running:");
+ ipw.increaseIndent();
+ for (BroadcastProcessQueue queue : mRunning) {
+ if ((queue != null) && (queue == mRunningColdStart)) {
+ ipw.print("🥶 ");
+ } else {
+ ipw.print("\u3000 ");
+ }
+ if (queue != null) {
+ ipw.println(queue.toShortString());
+ } else {
+ ipw.println("(none)");
+ }
+ }
+ ipw.decreaseIndent();
+
+ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ needSep = mHistory.dumpLocked(ipw, dumpPackage, mQueueName, sdf, dumpAll, needSep);
+ return needSep;
+ }
+}
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 817831cb003b..33f74f3bd779 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -23,7 +23,10 @@ import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROA
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_CHANGE_ID;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_NONE;
import static com.android.server.am.BroadcastConstants.DEFER_BOOT_COMPLETED_BROADCAST_TARGET_T_ONLY;
+import static com.android.server.am.BroadcastQueue.checkState;
+import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManagerInternal;
@@ -48,6 +51,8 @@ import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.VisibleForTesting;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
@@ -83,7 +88,8 @@ final class BroadcastRecord extends Binder {
final int appOp; // an app op that is associated with this broadcast
final BroadcastOptions options; // BroadcastOptions supplied by caller
final List receivers; // contains BroadcastFilter and ResolveInfo
- final int[] delivery; // delivery state of each receiver
+ final @DeliveryState int[] delivery; // delivery state of each receiver
+ final long[] scheduledTime; // uptimeMillis when each receiver was scheduled
final long[] duration; // duration a receiver took to process broadcast
IIntentReceiver resultTo; // who receives final result if non-null
boolean deferred;
@@ -107,6 +113,7 @@ final class BroadcastRecord extends Binder {
int anrCount; // has this broadcast record hit any ANRs?
int manifestCount; // number of manifest receivers dispatched.
int manifestSkipCount; // number of manifest receivers skipped.
+ int finishedCount; // number of receivers finished.
BroadcastQueue queue; // the outbound queue handling this broadcast
// if set to true, app's process will be temporarily allowed to start activities from background
@@ -121,16 +128,66 @@ final class BroadcastRecord extends Binder {
@Nullable
final BiFunction<Integer, Bundle, Bundle> filterExtrasForReceiver;
+ String cachedToString;
+ String cachedToShortString;
+
static final int IDLE = 0;
static final int APP_RECEIVE = 1;
static final int CALL_IN_RECEIVE = 2;
static final int CALL_DONE_RECEIVE = 3;
static final int WAITING_SERVICES = 4;
+ /** Initial state: waiting to run in future */
static final int DELIVERY_PENDING = 0;
+ /** Terminal state: finished successfully */
static final int DELIVERY_DELIVERED = 1;
+ /** Terminal state: skipped due to internal policy */
static final int DELIVERY_SKIPPED = 2;
+ /** Terminal state: timed out during attempted delivery */
static final int DELIVERY_TIMEOUT = 3;
+ /** Intermediate state: currently executing */
+ static final int DELIVERY_SCHEDULED = 4;
+ /** Terminal state: failure to dispatch */
+ static final int DELIVERY_FAILURE = 5;
+
+ @IntDef(flag = false, prefix = { "DELIVERY_" }, value = {
+ DELIVERY_PENDING,
+ DELIVERY_DELIVERED,
+ DELIVERY_SKIPPED,
+ DELIVERY_TIMEOUT,
+ DELIVERY_SCHEDULED,
+ DELIVERY_FAILURE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DeliveryState {}
+
+ static @NonNull String deliveryStateToString(@DeliveryState int deliveryState) {
+ switch (deliveryState) {
+ case DELIVERY_PENDING: return "Pending";
+ case DELIVERY_DELIVERED: return "Delivered";
+ case DELIVERY_SKIPPED: return "Skipped";
+ case DELIVERY_TIMEOUT: return "Timeout";
+ case DELIVERY_SCHEDULED: return "Scheduled";
+ case DELIVERY_FAILURE: return "Failure";
+ default: return Integer.toString(deliveryState);
+ }
+ }
+
+ /**
+ * Return if the given delivery state is "terminal", where no additional
+ * delivery state changes will be made.
+ */
+ static boolean isDeliveryStateTerminal(@DeliveryState int deliveryState) {
+ switch (deliveryState) {
+ case DELIVERY_DELIVERED:
+ case DELIVERY_SKIPPED:
+ case DELIVERY_TIMEOUT:
+ case DELIVERY_FAILURE:
+ return true;
+ default:
+ return false;
+ }
+ }
ProcessRecord curApp; // hosting application of current receiver.
ComponentName curComponent; // the receiver class that is currently running.
@@ -248,13 +305,7 @@ final class BroadcastRecord extends Binder {
for (int i = 0; i < N; i++) {
Object o = receivers.get(i);
pw.print(prefix);
- switch (delivery[i]) {
- case DELIVERY_PENDING: pw.print("Pending"); break;
- case DELIVERY_DELIVERED: pw.print("Deliver"); break;
- case DELIVERY_SKIPPED: pw.print("Skipped"); break;
- case DELIVERY_TIMEOUT: pw.print("Timeout"); break;
- default: pw.print("???????"); break;
- }
+ pw.print(deliveryStateToString(delivery[i]));
pw.print(" "); TimeUtils.formatDuration(duration[i], pw);
pw.print(" #"); pw.print(i); pw.print(": ");
if (o instanceof BroadcastFilter) {
@@ -300,6 +351,7 @@ final class BroadcastRecord extends Binder {
options = _options;
receivers = _receivers;
delivery = new int[_receivers != null ? _receivers.size() : 0];
+ scheduledTime = new long[delivery.length];
duration = new long[delivery.length];
resultTo = _resultTo;
resultCode = _resultCode;
@@ -346,6 +398,7 @@ final class BroadcastRecord extends Binder {
options = from.options;
receivers = from.receivers;
delivery = from.delivery;
+ scheduledTime = from.scheduledTime;
duration = from.duration;
resultTo = from.resultTo;
enqueueTime = from.enqueueTime;
@@ -497,7 +550,85 @@ final class BroadcastRecord extends Binder {
return ret;
}
- int getReceiverUid(Object receiver) {
+ /**
+ * Update the delivery state of the given {@link #receivers} index.
+ * Automatically updates any time measurements related to state changes.
+ */
+ void setDeliveryState(int index, @DeliveryState int deliveryState) {
+ delivery[index] = deliveryState;
+
+ switch (deliveryState) {
+ case DELIVERY_DELIVERED:
+ duration[index] = SystemClock.uptimeMillis() - scheduledTime[index];
+ break;
+ case DELIVERY_SCHEDULED:
+ scheduledTime[index] = SystemClock.uptimeMillis();
+ break;
+ }
+ }
+
+ @DeliveryState int getDeliveryState(int index) {
+ return delivery[index];
+ }
+
+ boolean isForeground() {
+ return (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
+ }
+
+ boolean isReplacePending() {
+ return (intent.getFlags() & Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
+ }
+
+ boolean isNoAbort() {
+ return (intent.getFlags() & Intent.FLAG_RECEIVER_NO_ABORT) != 0;
+ }
+
+ @NonNull String getHostingRecordTriggerType() {
+ if (alarm) {
+ return HostingRecord.TRIGGER_TYPE_ALARM;
+ } else if (pushMessage) {
+ return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE;
+ } else if (pushMessageOverQuota) {
+ return HostingRecord.TRIGGER_TYPE_PUSH_MESSAGE_OVER_QUOTA;
+ }
+ return HostingRecord.TRIGGER_TYPE_UNKNOWN;
+ }
+
+ /**
+ * Return an instance of {@link #intent} specialized for the given receiver.
+ * For example, this returns a new specialized instance if the extras need
+ * to be filtered, or a {@link ResolveInfo} needs to be configured.
+ *
+ * @return a specialized intent, otherwise {@code null} to indicate that the
+ * broadcast should not be delivered to this receiver, typically due
+ * to it being filtered away by {@link #filterExtrasForReceiver}.
+ */
+ @Nullable Intent getReceiverIntent(@NonNull Object receiver) {
+ Intent newIntent = null;
+ if (filterExtrasForReceiver != null) {
+ final Bundle extras = intent.getExtras();
+ if (extras != null) {
+ final int receiverUid = getReceiverUid(receiver);
+ final Bundle filteredExtras = filterExtrasForReceiver.apply(receiverUid, extras);
+ if (filteredExtras == null) {
+ // Completely filtered; skip the broadcast!
+ return null;
+ } else {
+ newIntent = new Intent(intent);
+ newIntent.replaceExtras(filteredExtras);
+ }
+ }
+ }
+ if (receiver instanceof ResolveInfo) {
+ if (newIntent == null) {
+ newIntent = new Intent(intent);
+ }
+ newIntent.setComponent(((ResolveInfo) receiver).activityInfo.getComponentName());
+ }
+ return (newIntent != null) ? newIntent : intent;
+ }
+
+ static int getReceiverUid(@NonNull Object receiver) {
if (receiver instanceof BroadcastFilter) {
return ((BroadcastFilter) receiver).owningUid;
} else /* if (receiver instanceof ResolveInfo) */ {
@@ -505,6 +636,22 @@ final class BroadcastRecord extends Binder {
}
}
+ static @NonNull String getReceiverProcessName(@NonNull Object receiver) {
+ if (receiver instanceof BroadcastFilter) {
+ return ((BroadcastFilter) receiver).receiverList.app.processName;
+ } else /* if (receiver instanceof ResolveInfo) */ {
+ return ((ResolveInfo) receiver).activityInfo.processName;
+ }
+ }
+
+ static @NonNull String getReceiverPackageName(@NonNull Object receiver) {
+ if (receiver instanceof BroadcastFilter) {
+ return ((BroadcastFilter) receiver).receiverList.app.info.packageName;
+ } else /* if (receiver instanceof ResolveInfo) */ {
+ return ((ResolveInfo) receiver).activityInfo.packageName;
+ }
+ }
+
public BroadcastRecord maybeStripForHistory() {
if (!intent.canStripForHistory()) {
return this;
@@ -556,9 +703,27 @@ final class BroadcastRecord extends Binder {
@Override
public String toString() {
- return "BroadcastRecord{"
- + Integer.toHexString(System.identityHashCode(this))
- + " u" + userId + " " + intent.getAction() + "}";
+ if (cachedToString == null) {
+ String label = intent.getAction();
+ if (label == null) {
+ label = intent.toString();
+ }
+ cachedToString = "BroadcastRecord{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " u" + userId + " " + label + "}";
+ }
+ return cachedToString;
+ }
+
+ public String toShortString() {
+ if (cachedToShortString == null) {
+ String label = intent.getAction();
+ if (label == null) {
+ label = intent.toString();
+ }
+ cachedToShortString = label + "/u" + userId;
+ }
+ return cachedToShortString;
}
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
diff --git a/services/core/java/com/android/server/am/BroadcastSkipPolicy.java b/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
index 9569848cbe37..e9b503007b5e 100644
--- a/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
+++ b/services/core/java/com/android/server/am/BroadcastSkipPolicy.java
@@ -16,7 +16,6 @@
package com.android.server.am;
-import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
import static com.android.server.am.ActivityManagerService.checkComponentPermission;
import static com.android.server.am.BroadcastQueue.TAG;
@@ -35,7 +34,6 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
-import android.os.Bundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -59,6 +57,18 @@ public class BroadcastSkipPolicy {
/**
* Determine if the given {@link BroadcastRecord} is eligible to be sent to
+ * the given {@link BroadcastFilter} or {@link ResolveInfo}.
+ */
+ public boolean shouldSkip(@NonNull BroadcastRecord r, @NonNull Object target) {
+ if (target instanceof BroadcastFilter) {
+ return shouldSkip(r, (BroadcastFilter) target);
+ } else {
+ return shouldSkip(r, (ResolveInfo) target);
+ }
+ }
+
+ /**
+ * Determine if the given {@link BroadcastRecord} is eligible to be sent to
* the given {@link ResolveInfo}.
*/
public boolean shouldSkip(@NonNull BroadcastRecord r, @NonNull ResolveInfo info) {
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 86c97707362a..a97173d2695f 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -29,6 +29,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
import static com.android.server.am.ActivityManagerService.TAG_MU;
import static com.android.server.am.ProcessProfileRecord.HOSTING_COMPONENT_TYPE_PROVIDER;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -48,7 +49,6 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManagerInternal;
import android.content.pm.PathPermission;
import android.content.pm.ProviderInfo;
import android.content.pm.UserInfo;
@@ -960,20 +960,22 @@ public class ContentProviderHelper {
String getProviderMimeType(Uri uri, int userId) {
mService.enforceNotIsolatedCaller("getProviderMimeType");
final String name = uri.getAuthority();
- int callingUid = Binder.getCallingUid();
- int callingPid = Binder.getCallingPid();
- long ident = 0;
- boolean clearedIdentity = false;
- userId = mService.mUserController.unsafeConvertIncomingUser(userId);
- if (canClearIdentity(callingPid, callingUid, userId)) {
- clearedIdentity = true;
- ident = Binder.clearCallingIdentity();
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
+ final long ident = canClearIdentity(callingPid, callingUid, safeUserId)
+ ? Binder.clearCallingIdentity() : 0;
+ final ContentProviderHolder holder;
+ try {
+ holder = getContentProviderExternalUnchecked(name, null /* token */, callingUid,
+ "*getmimetype*", safeUserId);
+ } finally {
+ if (ident != 0) {
+ Binder.restoreCallingIdentity(ident);
+ }
}
- ContentProviderHolder holder = null;
try {
- holder = getContentProviderExternalUnchecked(name, null, callingUid,
- "*getmimetype*", userId);
- if (holder != null) {
+ if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) {
final IBinder providerConnection = holder.connection;
final ComponentName providerName = holder.info.getComponentName();
// Note: creating a new Runnable instead of using a lambda here since lambdas in
@@ -992,6 +994,13 @@ public class ContentProviderHelper {
return holder.provider.getType(uri);
} finally {
mService.mHandler.removeCallbacks(providerNotResponding);
+ // We need to clear the identity to call removeContentProviderExternalUnchecked
+ final long token = Binder.clearCallingIdentity();
+ try {
+ removeContentProviderExternalUnchecked(name, null /* token */, safeUserId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
} catch (RemoteException e) {
@@ -1000,18 +1009,6 @@ public class ContentProviderHelper {
} catch (Exception e) {
Log.w(TAG, "Exception while determining type of " + uri, e);
return null;
- } finally {
- // We need to clear the identity to call removeContentProviderExternalUnchecked
- if (!clearedIdentity) {
- ident = Binder.clearCallingIdentity();
- }
- try {
- if (holder != null) {
- removeContentProviderExternalUnchecked(name, null, userId);
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
}
return null;
@@ -1029,12 +1026,20 @@ public class ContentProviderHelper {
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int safeUserId = mService.mUserController.unsafeConvertIncomingUser(userId);
- final long ident = canClearIdentity(callingPid, callingUid, userId)
+ final long ident = canClearIdentity(callingPid, callingUid, safeUserId)
? Binder.clearCallingIdentity() : 0;
+ final ContentProviderHolder holder;
try {
- final ContentProviderHolder holder = getContentProviderExternalUnchecked(name, null,
- callingUid, "*getmimetype*", safeUserId);
- if (holder != null) {
+ holder = getContentProviderExternalUnchecked(name, null /* token */, callingUid,
+ "*getmimetype*", safeUserId);
+ } finally {
+ if (ident != 0) {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ try {
+ if (isHolderVisibleToCaller(holder, callingUid, safeUserId)) {
holder.provider.getTypeAsync(uri, new RemoteCallback(result -> {
final long identity = Binder.clearCallingIdentity();
try {
@@ -1050,8 +1055,6 @@ public class ContentProviderHelper {
} catch (RemoteException e) {
Log.w(TAG, "Content provider dead retrieving " + uri, e);
resultCallback.sendResult(Bundle.EMPTY);
- } finally {
- Binder.restoreCallingIdentity(ident);
}
}
@@ -1067,6 +1070,35 @@ public class ContentProviderHelper {
callingUid, -1, true) == PackageManager.PERMISSION_GRANTED;
}
+ private boolean isHolderVisibleToCaller(@Nullable ContentProviderHolder holder, int callingUid,
+ @UserIdInt int userId) {
+ if (holder == null || holder.info == null) {
+ return false;
+ }
+
+ if (isAuthorityRedirectedForCloneProfile(holder.info.authority)
+ && resolveParentUserIdForCloneProfile(userId) != userId) {
+ // Since clone profile shares certain providers with its parent and the access is
+ // re-directed as well, the holder may not actually be installed on the clone profile.
+ return !mService.getPackageManagerInternal().filterAppAccess(holder.info.packageName,
+ callingUid, userId, false /* filterUninstalled */);
+ }
+
+ return !mService.getPackageManagerInternal().filterAppAccess(holder.info.packageName,
+ callingUid, userId);
+ }
+
+ private static @UserIdInt int resolveParentUserIdForCloneProfile(@UserIdInt int userId) {
+ final UserManagerInternal umInternal = LocalServices.getService(UserManagerInternal.class);
+ final UserInfo userInfo = umInternal.getUserInfo(userId);
+
+ if (userInfo == null || !userInfo.isCloneProfile()) {
+ return userId;
+ }
+
+ return umInternal.getProfileParentId(userId);
+ }
+
/**
* Check if the calling UID has a possible chance at accessing the provider
* at the given authority and user.
@@ -1135,9 +1167,7 @@ public class ContentProviderHelper {
"*checkContentProviderUriPermission*", userId);
if (holder != null) {
- final PackageManagerInternal packageManagerInt = LocalServices.getService(
- PackageManagerInternal.class);
- final AndroidPackage androidPackage = packageManagerInt
+ final AndroidPackage androidPackage = mService.getPackageManagerInternal()
.getPackage(Binder.getCallingUid());
if (androidPackage == null) {
return PackageManager.PERMISSION_DENIED;
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index ceea01b3ccc7..71d39964ed72 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -51,6 +51,7 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TimeoutRecord;
+import com.android.internal.os.anr.AnrLatencyTracker;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ResourcePressureUtil;
import com.android.server.criticalevents.CriticalEventLog;
@@ -258,11 +259,15 @@ class ProcessErrorStateRecord {
boolean aboveSystem, TimeoutRecord timeoutRecord,
boolean onlyDumpSelf) {
String annotation = timeoutRecord.mReason;
+ AnrLatencyTracker latencyTracker = timeoutRecord.mLatencyTracker;
+
ArrayList<Integer> firstPids = new ArrayList<>(5);
SparseArray<Boolean> lastPids = new SparseArray<>(20);
mApp.getWindowProcessController().appEarlyNotResponding(annotation, () -> {
+ latencyTracker.waitingOnAMSLockStarted();
synchronized (mService) {
+ latencyTracker.waitingOnAMSLockEnded();
// Store annotation here as instance below races with this killLocked.
setAnrAnnotation(annotation);
mApp.killLocked("anr", ApplicationExitInfo.REASON_ANR, true);
@@ -271,37 +276,48 @@ class ProcessErrorStateRecord {
long anrTime = SystemClock.uptimeMillis();
if (isMonitorCpuUsage()) {
+ latencyTracker.updateCpuStatsNowCalled();
mService.updateCpuStatsNow();
+ latencyTracker.updateCpuStatsNowReturned();
}
final boolean isSilentAnr;
final int pid = mApp.getPid();
final UUID errorId;
+ latencyTracker.waitingOnAMSLockStarted();
synchronized (mService) {
+ latencyTracker.waitingOnAMSLockEnded();
// Store annotation here as instance above will not be hit on all paths.
setAnrAnnotation(annotation);
// PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
if (mService.mAtmInternal.isShuttingDown()) {
Slog.i(TAG, "During shutdown skipping ANR: " + this + " " + annotation);
+ latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
return;
} else if (isNotResponding()) {
Slog.i(TAG, "Skipping duplicate ANR: " + this + " " + annotation);
+ latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
return;
} else if (isCrashing()) {
Slog.i(TAG, "Crashing app skipping ANR: " + this + " " + annotation);
+ latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
return;
} else if (mApp.isKilledByAm()) {
Slog.i(TAG, "App already killed by AM skipping ANR: " + this + " " + annotation);
+ latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
return;
} else if (mApp.isKilled()) {
Slog.i(TAG, "Skipping died app ANR: " + this + " " + annotation);
+ latencyTracker.anrSkippedProcessErrorStateRecordAppNotResponding();
return;
}
// In case we come through here for the same app before completing
// this one, mark as anring now so we will bail out.
+ latencyTracker.waitingOnProcLockStarted();
synchronized (mProcLock) {
+ latencyTracker.waitingOnProcLockEnded();
setNotResponding(true);
}
@@ -361,9 +377,11 @@ class ProcessErrorStateRecord {
}
// Get critical event log before logging the ANR so that it doesn't occur in the log.
+ latencyTracker.criticalEventLogStarted();
final String criticalEventLog =
CriticalEventLog.getInstance().logLinesForTraceFile(
mApp.getProcessClassEnum(), mApp.processName, mApp.uid);
+ latencyTracker.criticalEventLogEnded();
CriticalEventLog.getInstance().logAnr(annotation, mApp.getProcessClassEnum(),
mApp.processName, mApp.uid, mApp.mPid);
@@ -405,9 +423,14 @@ class ProcessErrorStateRecord {
}
StringBuilder report = new StringBuilder();
- report.append(ResourcePressureUtil.currentPsiState());
+
+ latencyTracker.currentPsiStateCalled();
+ String currentPsiState = ResourcePressureUtil.currentPsiState();
+ latencyTracker.currentPsiStateReturned();
+ report.append(currentPsiState);
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
+ latencyTracker.nativePidCollectionStarted();
// don't dump native PIDs for background ANRs unless it is the process of interest
String[] nativeProcs = null;
if (isSilentAnr || onlyDumpSelf) {
@@ -430,7 +453,7 @@ class ProcessErrorStateRecord {
nativePids.add(i);
}
}
-
+ latencyTracker.nativePidCollectionEnded();
// For background ANRs, don't pass the ProcessCpuTracker to
// avoid spending 1/2 second collecting stats to rank lastPids.
StringWriter tracesFileException = new StringWriter();
@@ -438,7 +461,8 @@ class ProcessErrorStateRecord {
final long[] offsets = new long[2];
File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
isSilentAnr ? null : processCpuTracker, isSilentAnr ? null : lastPids,
- nativePids, tracesFileException, offsets, annotation, criticalEventLog);
+ nativePids, tracesFileException, offsets, annotation, criticalEventLog,
+ latencyTracker);
if (isMonitorCpuUsage()) {
mService.updateCpuStatsNow();
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 992d41637f3f..d7b38482a4ed 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -27,6 +27,7 @@ import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
+import static android.os.Process.getAdvertisedMem;
import static android.os.Process.getFreeMemory;
import static android.os.Process.getTotalMemory;
import static android.os.Process.killProcessQuiet;
@@ -1532,6 +1533,7 @@ public final class ProcessList {
void getMemoryInfo(ActivityManager.MemoryInfo outInfo) {
final long homeAppMem = getMemLevel(HOME_APP_ADJ);
final long cachedAppMem = getMemLevel(CACHED_APP_MIN_ADJ);
+ outInfo.advertisedMem = getAdvertisedMem();
outInfo.availMem = getFreeMemory();
outInfo.totalMem = getTotalMemory();
outInfo.threshold = homeAppMem;
diff --git a/services/core/java/com/android/server/am/ProcessProfileRecord.java b/services/core/java/com/android/server/am/ProcessProfileRecord.java
index 7369e8fadb09..4c15308a574e 100644
--- a/services/core/java/com/android/server/am/ProcessProfileRecord.java
+++ b/services/core/java/com/android/server/am/ProcessProfileRecord.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
-import static android.app.ActivityManager.processStateAmToProto;
import android.annotation.IntDef;
import android.app.IApplicationThread;
@@ -318,12 +317,6 @@ final class ProcessProfileRecord {
origBase.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
pkgList.getPackageListLocked());
- pkgList.forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- mApp.uid, mApp.processName, pkgName,
- processStateAmToProto(ProcessStats.STATE_NOTHING),
- holder.appVersion)
- );
}
origBase.makeInactive();
}
@@ -362,12 +355,6 @@ final class ProcessProfileRecord {
origBase.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), SystemClock.uptimeMillis(),
pkgList.getPackageListLocked());
- pkgList.forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- mApp.uid, mApp.processName, pkgName,
- processStateAmToProto(ProcessStats.STATE_NOTHING),
- holder.appVersion)
- );
}
origBase.makeInactive();
setBaseProcessTracker(null);
diff --git a/services/core/java/com/android/server/am/ProcessReceiverRecord.java b/services/core/java/com/android/server/am/ProcessReceiverRecord.java
index 8d3e9669ea00..34a2b03f1d7d 100644
--- a/services/core/java/com/android/server/am/ProcessReceiverRecord.java
+++ b/services/core/java/com/android/server/am/ProcessReceiverRecord.java
@@ -34,29 +34,61 @@ final class ProcessReceiverRecord {
*/
private final ArraySet<BroadcastRecord> mCurReceivers = new ArraySet<BroadcastRecord>();
+ private int mCurReceiversSize;
+
/**
* All IIntentReceivers that are registered from this process.
*/
private final ArraySet<ReceiverList> mReceivers = new ArraySet<>();
int numberOfCurReceivers() {
- return mCurReceivers.size();
+ return mCurReceiversSize;
+ }
+
+ void incrementCurReceivers() {
+ mCurReceiversSize++;
}
+ void decrementCurReceivers() {
+ mCurReceiversSize--;
+ }
+
+ /**
+ * @deprecated we're moving towards tracking only a reference count to
+ * improve performance.
+ */
+ @Deprecated
BroadcastRecord getCurReceiverAt(int index) {
return mCurReceivers.valueAt(index);
}
+ /**
+ * @deprecated we're moving towards tracking only a reference count to
+ * improve performance.
+ */
+ @Deprecated
boolean hasCurReceiver(BroadcastRecord receiver) {
return mCurReceivers.contains(receiver);
}
+ /**
+ * @deprecated we're moving towards tracking only a reference count to
+ * improve performance.
+ */
+ @Deprecated
void addCurReceiver(BroadcastRecord receiver) {
mCurReceivers.add(receiver);
+ mCurReceiversSize = mCurReceivers.size();
}
+ /**
+ * @deprecated we're moving towards tracking only a reference count to
+ * improve performance.
+ */
+ @Deprecated
void removeCurReceiver(BroadcastRecord receiver) {
mCurReceivers.remove(receiver);
+ mCurReceiversSize = mCurReceivers.size();
}
int numberOfReceivers() {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 07b6fcdcb0ca..482e6a792a90 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -54,7 +54,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.Zygote;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.wm.WindowProcessController;
import com.android.server.wm.WindowProcessListener;
@@ -1212,12 +1211,6 @@ class ProcessRecord implements WindowProcessListener {
long now = SystemClock.uptimeMillis();
baseProcessTracker.setState(ProcessStats.STATE_NOTHING,
tracker.getMemFactorLocked(), now, mPkgList.getPackageListLocked());
- mPkgList.forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- uid, processName, pkgName,
- ActivityManager.processStateAmToProto(ProcessStats.STATE_NOTHING),
- holder.appVersion)
- );
if (numOfPkgs != 1) {
mPkgList.forEachPackageProcessStats(holder -> {
if (holder.state != null && holder.state != baseProcessTracker) {
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index ef137787905f..d2ef479ed524 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -35,7 +35,6 @@ import android.util.TimeUtils;
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.FrameworkStatsLog;
import com.android.server.am.PlatformCompatCache.CachedCompatChangeId;
import java.io.PrintWriter;
@@ -599,12 +598,6 @@ final class ProcessStateRecord {
@GuardedBy({"mService", "mProcLock"})
void setReportedProcState(int repProcState) {
mRepProcState = repProcState;
- mApp.getPkgList().forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- mApp.uid, mApp.processName, pkgName,
- ActivityManager.processStateAmToProto(mRepProcState),
- holder.appVersion)
- );
mApp.getWindowProcessController().setReportedProcState(repProcState);
}
@@ -620,12 +613,6 @@ final class ProcessStateRecord {
mRepProcState = newState;
setCurProcState(newState);
setCurRawProcState(newState);
- mApp.getPkgList().forEachPackage((pkgName, holder) ->
- FrameworkStatsLog.write(FrameworkStatsLog.PROCESS_STATE_CHANGED,
- mApp.uid, mApp.processName, pkgName,
- ActivityManager.processStateAmToProto(mRepProcState),
- holder.appVersion)
- );
}
}
}
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index f8558e80588c..9ccf74115ba0 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -60,6 +60,14 @@
{ "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
{ "include-filter": "com.android.server.power.stats.BatteryStatsTests" }
]
+ },
+ {
+ "file_patterns": ["Broadcast"],
+ "name": "FrameworksMockingServicesTests",
+ "options": [
+ { "include-filter": "com.android.server.am.BroadcastQueueTest" },
+ { "include-filter": "com.android.server.am.BroadcastQueueModernImplTest" }
+ ]
}
],
"presubmit-large": [
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 6775c9952ddd..82fb1e816888 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1387,7 +1387,7 @@ class UserController implements Handler.Callback {
int i = 0;
for (; i < profilesToStartSize && i < (getMaxRunningUsers() - 1); ++i) {
// NOTE: this method is setting the profiles of the current user - which is always
- // assigned to the default display - so there's no need to pass PARENT_DISPLAY
+ // assigned to the default display
startUser(profilesToStart.get(i).id, /* foreground= */ false);
}
if (i < profilesToStartSize) {
@@ -1430,10 +1430,7 @@ class UserController implements Handler.Callback {
return false;
}
- int displayId = mInjector.isUsersOnSecondaryDisplaysEnabled()
- ? UserManagerInternal.PARENT_DISPLAY
- : Display.DEFAULT_DISPLAY;
- return startUserNoChecks(userId, displayId, /* foreground= */ false,
+ return startUserNoChecks(userId, Display.DEFAULT_DISPLAY, /* foreground= */ false,
/* unlockListener= */ null);
}
@@ -1641,7 +1638,7 @@ class UserController implements Handler.Callback {
}
mInjector.updateUserConfiguration();
updateCurrentProfileIds();
- mInjector.getWindowManager().setCurrentUser(userId, getCurrentProfileIds());
+ mInjector.getWindowManager().setCurrentUser(userId);
mInjector.reportCurWakefulnessUsageEvent();
// Once the internal notion of the active user has switched, we lock the device
// with the option to show the user switcher on the keyguard.
@@ -1655,7 +1652,6 @@ class UserController implements Handler.Callback {
} else {
final Integer currentUserIdInt = mCurrentUserId;
updateCurrentProfileIds();
- mInjector.getWindowManager().setCurrentProfileIds(getCurrentProfileIds());
synchronized (mLock) {
mUserLru.remove(currentUserIdInt);
mUserLru.add(currentUserIdInt);
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 0f5aa3a08950..4aaf1abf4147 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -52,6 +52,7 @@ import android.util.Slog;
import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost.SurfacePackage;
import android.view.WindowManager;
+import android.window.ScreenCapture;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -843,8 +844,8 @@ final class GameServiceProviderInstanceImpl implements GameServiceProviderInstan
final SurfaceControl overlaySurfaceControl =
overlaySurfacePackage != null ? overlaySurfacePackage.getSurfaceControl() : null;
mBackgroundExecutor.execute(() -> {
- final SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder =
- new SurfaceControl.LayerCaptureArgs.Builder(/* layer */ null);
+ final ScreenCapture.LayerCaptureArgs.Builder layerCaptureArgsBuilder =
+ new ScreenCapture.LayerCaptureArgs.Builder(/* layer */ null);
if (overlaySurfaceControl != null) {
SurfaceControl[] excludeLayers = new SurfaceControl[1];
excludeLayers[0] = overlaySurfaceControl;
diff --git a/services/core/java/com/android/server/appop/AppOpsRestrictions.java b/services/core/java/com/android/server/appop/AppOpsRestrictions.java
new file mode 100644
index 000000000000..f7ccd3429f11
--- /dev/null
+++ b/services/core/java/com/android/server/appop/AppOpsRestrictions.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import android.os.PackageTagsList;
+
+import java.io.PrintWriter;
+
+/**
+ * Legacy implementation for AppOpsService's app-op restrictions (global and user)
+ * storage and access.
+ */
+public interface AppOpsRestrictions {
+ /**
+ * Set or clear a global app-op restriction for the given {@code clientToken}.
+ *
+ * @param clientToken A token identifying the client this restriction applies to.
+ * @param code The app-op opCode to set (or clear) a restriction for.
+ * @param restricted {@code true} to restrict this app-op code, or {@code false} to clear an
+ * existing restriction.
+ * @return {@code true} if any restriction state was modified as a result of this operation
+ */
+ boolean setGlobalRestriction(Object clientToken, int code, boolean restricted);
+
+ /**
+ * Get the state of a global app-op restriction for the given {@code clientToken}.
+ *
+ * @param clientToken A token identifying the client to get the restriction state of.
+ * @param code The app-op code to get the restriction state of.
+ * @return the restriction state
+ */
+ boolean getGlobalRestriction(Object clientToken, int code);
+
+ /**
+ * Returns {@code true} if *any* global app-op restrictions are currently set for the given
+ * {@code clientToken}.
+ *
+ * @param clientToken A token identifying the client to check restrictions for.
+ * @return {@code true} if any restrictions are set
+ */
+ boolean hasGlobalRestrictions(Object clientToken);
+
+ /**
+ * Clear *all* global app-op restrictions for the given {@code clientToken}.
+ *
+ * @param clientToken A token identifying the client to clear restrictions from.
+ * @return {@code true} if any restriction state was modified as a result of this operation
+ */
+ boolean clearGlobalRestrictions(Object clientToken);
+
+ /**
+ * Set or clear a user app-op restriction for the given {@code clientToken} and {@code userId}.
+ *
+ * @param clientToken A token identifying the client this restriction applies to.
+ * @param code The app-op code to set (or clear) a restriction for.
+ * @param restricted {@code true} to restrict this app-op code, or {@code false} to
+ * remove any existing restriction.
+ * @param excludedPackageTags A list of packages and associated attribution tags to exclude
+ * from this restriction. Or, if {@code null}, removes any
+ * exclusions from this restriction.
+ * @return {@code true} if any restriction state was modified as a result of this operation
+ */
+ boolean setUserRestriction(Object clientToken, int userId, int code, boolean restricted,
+ PackageTagsList excludedPackageTags);
+
+ /**
+ * Get the state of a user app-op restriction for the given {@code clientToken} and {@code
+ * userId}. Or, if the combination of ({{@code clientToken}, {@code userId}, @code
+ * packageName}, {@code attributionTag}) has been excluded via
+ * {@link AppOpsRestrictions#setUserRestriction}, always returns {@code false}.
+ *
+ * @param clientToken A token identifying the client this restriction applies to.
+ * @param userId Which userId this restriction applies to.
+ * @param code The app-op code to get the restriction state of.
+ * @param packageName A package name used to check for exclusions.
+ * @param attributionTag An attribution tag used to check for exclusions.
+ * @param isCheckOp a flag that, when {@code true}, denotes that exclusions should be
+ * checked by (packageName) rather than (packageName, attributionTag)
+ * @return the restriction state
+ */
+ boolean getUserRestriction(Object clientToken, int userId, int code, String packageName,
+ String attributionTag, boolean isCheckOp);
+
+ /**
+ * Returns {@code true} if *any* user app-op restrictions are currently set for the given
+ * {@code clientToken}.
+ *
+ * @param clientToken A token identifying the client to check restrictions for.
+ * @return {@code true} if any restrictions are set
+ */
+ boolean hasUserRestrictions(Object clientToken);
+
+ /**
+ * Clear *all* user app-op restrictions for the given {@code clientToken}.
+ *
+ * @param clientToken A token identifying the client to clear restrictions for.
+ * @return {@code true} if any restriction state was modified as a result of this operation
+ */
+ boolean clearUserRestrictions(Object clientToken);
+
+ /**
+ * Clear *all* user app-op restrictions for the given {@code clientToken} and {@code userId}.
+ *
+ * @param clientToken A token identifying the client to clear restrictions for.
+ * @param userId Which userId to clear restrictions for.
+ * @return {@code true} if any restriction state was modified as a result of this operation
+ */
+ boolean clearUserRestrictions(Object clientToken, Integer userId);
+
+ /**
+ * Returns the set of exclusions previously set by
+ * {@link AppOpsRestrictions#setUserRestriction} for the given {@code clientToken}
+ * and {@code userId}.
+ *
+ * @param clientToken A token identifying the client to get restriction exclusions for.
+ * @param userId Which userId to get restriction exclusions for
+ * @return a set of user restriction exclusions
+ */
+ PackageTagsList getUserRestrictionExclusions(Object clientToken, int userId);
+
+ /**
+ * Dump the state of appop restrictions.
+ *
+ * @param printWriter writer to dump to.
+ * @param dumpOp if -1 then op mode listeners for all app-ops are dumped. If it's
+ * set to an app-op, only the watchers for that app-op are dumped.
+ * @param dumpPackage if not null and if dumpOp is -1, dumps watchers for the package
+ * name.
+ * @param showUserRestrictions include user restriction state in the output
+ */
+ void dumpRestrictions(PrintWriter printWriter, int dumpOp, String dumpPackage,
+ boolean showUserRestrictions);
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java b/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
new file mode 100644
index 000000000000..adfd2afffd78
--- /dev/null
+++ b/services/core/java/com/android/server/appop/AppOpsRestrictionsImpl.java
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import android.annotation.RequiresPermission;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.os.Handler;
+import android.os.PackageTagsList;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Implementation for AppOpsService's app-op restrictions (global and user) storage and retrieval.
+ */
+public class AppOpsRestrictionsImpl implements AppOpsRestrictions {
+
+ private static final int UID_ANY = -2;
+
+ private Context mContext;
+ private Handler mHandler;
+ private AppOpsServiceInterface mAppOpsServiceInterface;
+
+ // Map from (Object token) to (int code) to (boolean restricted)
+ private final ArrayMap<Object, SparseBooleanArray> mGlobalRestrictions = new ArrayMap<>();
+
+ // Map from (Object token) to (int userId) to (int code) to (boolean restricted)
+ private final ArrayMap<Object, SparseArray<SparseBooleanArray>> mUserRestrictions =
+ new ArrayMap<>();
+
+ // Map from (Object token) to (int userId) to (PackageTagsList packageTagsList)
+ private final ArrayMap<Object, SparseArray<PackageTagsList>>
+ mUserRestrictionExcludedPackageTags = new ArrayMap<>();
+
+ public AppOpsRestrictionsImpl(Context context, Handler handler,
+ AppOpsServiceInterface appOpsServiceInterface) {
+ mContext = context;
+ mHandler = handler;
+ mAppOpsServiceInterface = appOpsServiceInterface;
+ }
+
+ @Override
+ public boolean setGlobalRestriction(Object clientToken, int code, boolean restricted) {
+ if (restricted) {
+ if (!mGlobalRestrictions.containsKey(clientToken)) {
+ mGlobalRestrictions.put(clientToken, new SparseBooleanArray());
+ }
+ SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken);
+ Objects.requireNonNull(restrictedCodes);
+ boolean changed = !restrictedCodes.get(code);
+ restrictedCodes.put(code, true);
+ return changed;
+ } else {
+ SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken);
+ if (restrictedCodes == null) {
+ return false;
+ }
+ boolean changed = restrictedCodes.get(code);
+ restrictedCodes.delete(code);
+ if (restrictedCodes.size() == 0) {
+ mGlobalRestrictions.remove(clientToken);
+ }
+ return changed;
+ }
+ }
+
+ @Override
+ public boolean getGlobalRestriction(Object clientToken, int code) {
+ SparseBooleanArray restrictedCodes = mGlobalRestrictions.get(clientToken);
+ if (restrictedCodes == null) {
+ return false;
+ }
+ return restrictedCodes.get(code);
+ }
+
+ @Override
+ public boolean hasGlobalRestrictions(Object clientToken) {
+ return mGlobalRestrictions.containsKey(clientToken);
+ }
+
+ @Override
+ public boolean clearGlobalRestrictions(Object clientToken) {
+ return mGlobalRestrictions.remove(clientToken) != null;
+ }
+
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS
+ })
+ @Override
+ public boolean setUserRestriction(Object clientToken, int userId, int code,
+ boolean restricted,
+ PackageTagsList excludedPackageTags) {
+ int[] userIds = resolveUserId(userId);
+ boolean changed = false;
+ for (int i = 0; i < userIds.length; i++) {
+ changed |= putUserRestriction(clientToken, userIds[i], code, restricted);
+ changed |= putUserRestrictionExclusions(clientToken, userIds[i],
+ excludedPackageTags);
+ }
+ return changed;
+ }
+
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.CREATE_USERS
+ })
+ private int[] resolveUserId(int userId) {
+ int[] userIds;
+ if (userId == UserHandle.USER_ALL) {
+ // TODO(b/162888972): this call is returning all users, not just live ones - we
+ // need to either fix the method called, or rename the variable
+ List<UserInfo> liveUsers = UserManager.get(mContext).getUsers();
+
+ userIds = new int[liveUsers.size()];
+ for (int i = 0; i < liveUsers.size(); i++) {
+ userIds[i] = liveUsers.get(i).id;
+ }
+ } else {
+ userIds = new int[]{userId};
+ }
+ return userIds;
+ }
+
+ @Override
+ public boolean hasUserRestrictions(Object clientToken) {
+ return mUserRestrictions.containsKey(clientToken);
+ }
+
+ private boolean getUserRestriction(Object clientToken, int userId, int code) {
+ SparseArray<SparseBooleanArray> userIdRestrictedCodes =
+ mUserRestrictions.get(clientToken);
+ if (userIdRestrictedCodes == null) {
+ return false;
+ }
+ SparseBooleanArray restrictedCodes = userIdRestrictedCodes.get(userId);
+ if (restrictedCodes == null) {
+ return false;
+ }
+ return restrictedCodes.get(code);
+ }
+
+ @Override
+ public boolean getUserRestriction(Object clientToken, int userId, int code, String packageName,
+ String attributionTag, boolean isCheckOp) {
+ boolean restricted = getUserRestriction(clientToken, userId, code);
+ if (!restricted) {
+ return false;
+ }
+
+ PackageTagsList perUserExclusions = getUserRestrictionExclusions(clientToken, userId);
+ if (perUserExclusions == null) {
+ return true;
+ }
+
+ // TODO (b/240617242) add overload for checkOp to support attribution tags
+ if (isCheckOp) {
+ return !perUserExclusions.includes(packageName);
+ }
+ return !perUserExclusions.contains(packageName, attributionTag);
+ }
+
+ @Override
+ public boolean clearUserRestrictions(Object clientToken) {
+ boolean changed = false;
+ SparseBooleanArray allUserRestrictedCodes = collectAllUserRestrictedCodes(clientToken);
+ changed |= mUserRestrictions.remove(clientToken) != null;
+ changed |= mUserRestrictionExcludedPackageTags.remove(clientToken) != null;
+ notifyAllUserRestrictions(allUserRestrictedCodes);
+ return changed;
+ }
+
+ private SparseBooleanArray collectAllUserRestrictedCodes(Object clientToken) {
+ SparseBooleanArray allRestrictedCodes = new SparseBooleanArray();
+ SparseArray<SparseBooleanArray> userIdRestrictedCodes = mUserRestrictions.get(clientToken);
+ if (userIdRestrictedCodes == null) {
+ return allRestrictedCodes;
+ }
+ int userIdRestrictedCodesSize = userIdRestrictedCodes.size();
+ for (int i = 0; i < userIdRestrictedCodesSize; i++) {
+ SparseBooleanArray restrictedCodes = userIdRestrictedCodes.valueAt(i);
+ int restrictedCodesSize = restrictedCodes.size();
+ for (int j = 0; j < restrictedCodesSize; j++) {
+ int code = restrictedCodes.keyAt(j);
+ allRestrictedCodes.put(code, true);
+ }
+ }
+ return allRestrictedCodes;
+ }
+
+ // TODO: For clearUserRestrictions, we are calling notifyOpChanged from within the
+ // LegacyAppOpsServiceInterfaceImpl class. But, for all other changes to restrictions, we're
+ // calling it from within AppOpsService. This is awkward, and we should probably do it one
+ // way or the other.
+ private void notifyAllUserRestrictions(SparseBooleanArray allUserRestrictedCodes) {
+ int restrictedCodesSize = allUserRestrictedCodes.size();
+ for (int j = 0; j < restrictedCodesSize; j++) {
+ int code = allUserRestrictedCodes.keyAt(j);
+ mHandler.post(() -> mAppOpsServiceInterface.notifyWatchersOfChange(code, UID_ANY));
+ }
+ }
+
+ @Override
+ public boolean clearUserRestrictions(Object clientToken, Integer userId) {
+ boolean changed = false;
+
+ SparseArray<SparseBooleanArray> userIdRestrictedCodes =
+ mUserRestrictions.get(clientToken);
+ if (userIdRestrictedCodes != null) {
+ changed |= userIdRestrictedCodes.contains(userId);
+ userIdRestrictedCodes.remove(userId);
+ if (userIdRestrictedCodes.size() == 0) {
+ mUserRestrictions.remove(clientToken);
+ }
+ }
+
+ SparseArray<PackageTagsList> userIdPackageTags =
+ mUserRestrictionExcludedPackageTags.get(clientToken);
+ if (userIdPackageTags != null) {
+ changed |= userIdPackageTags.contains(userId);
+ userIdPackageTags.remove(userId);
+ if (userIdPackageTags.size() == 0) {
+ mUserRestrictionExcludedPackageTags.remove(clientToken);
+ }
+ }
+
+ return changed;
+ }
+
+ private boolean putUserRestriction(Object token, int userId, int code, boolean restricted) {
+ boolean changed = false;
+ if (restricted) {
+ if (!mUserRestrictions.containsKey(token)) {
+ mUserRestrictions.put(token, new SparseArray<>());
+ }
+ SparseArray<SparseBooleanArray> userIdRestrictedCodes = mUserRestrictions.get(token);
+ Objects.requireNonNull(userIdRestrictedCodes);
+
+ if (!userIdRestrictedCodes.contains(userId)) {
+ userIdRestrictedCodes.put(userId, new SparseBooleanArray());
+ }
+ SparseBooleanArray restrictedCodes = userIdRestrictedCodes.get(userId);
+
+ changed = !restrictedCodes.get(code);
+ restrictedCodes.put(code, restricted);
+ } else {
+ SparseArray<SparseBooleanArray> userIdRestrictedCodes = mUserRestrictions.get(token);
+ if (userIdRestrictedCodes == null) {
+ return false;
+ }
+ SparseBooleanArray restrictedCodes = userIdRestrictedCodes.get(userId);
+ if (restrictedCodes == null) {
+ return false;
+ }
+ changed = restrictedCodes.get(code);
+ restrictedCodes.delete(code);
+ if (restrictedCodes.size() == 0) {
+ userIdRestrictedCodes.remove(userId);
+ }
+ if (userIdRestrictedCodes.size() == 0) {
+ mUserRestrictions.remove(token);
+ }
+ }
+ return changed;
+ }
+
+ @Override
+ public PackageTagsList getUserRestrictionExclusions(Object clientToken, int userId) {
+ SparseArray<PackageTagsList> userIdPackageTags =
+ mUserRestrictionExcludedPackageTags.get(clientToken);
+ if (userIdPackageTags == null) {
+ return null;
+ }
+ return userIdPackageTags.get(userId);
+ }
+
+ private boolean putUserRestrictionExclusions(Object token, int userId,
+ PackageTagsList excludedPackageTags) {
+ boolean addingExclusions = excludedPackageTags != null && !excludedPackageTags.isEmpty();
+ if (addingExclusions) {
+ if (!mUserRestrictionExcludedPackageTags.containsKey(token)) {
+ mUserRestrictionExcludedPackageTags.put(token, new SparseArray<>());
+ }
+ SparseArray<PackageTagsList> userIdExcludedPackageTags =
+ mUserRestrictionExcludedPackageTags.get(token);
+ Objects.requireNonNull(userIdExcludedPackageTags);
+
+ userIdExcludedPackageTags.put(userId, excludedPackageTags);
+ return true;
+ } else {
+ SparseArray<PackageTagsList> userIdExclusions =
+ mUserRestrictionExcludedPackageTags.get(token);
+ if (userIdExclusions == null) {
+ return false;
+ }
+ boolean changed = userIdExclusions.get(userId) != null;
+ userIdExclusions.remove(userId);
+ if (userIdExclusions.size() == 0) {
+ mUserRestrictionExcludedPackageTags.remove(token);
+ }
+ return changed;
+ }
+ }
+
+ @Override
+ public void dumpRestrictions(PrintWriter pw, int code, String dumpPackage,
+ boolean showUserRestrictions) {
+ final int globalRestrictionCount = mGlobalRestrictions.size();
+ for (int i = 0; i < globalRestrictionCount; i++) {
+ Object token = mGlobalRestrictions.keyAt(i);
+ SparseBooleanArray restrictedOps = mGlobalRestrictions.valueAt(i);
+
+ pw.println(" Global restrictions for token " + token + ":");
+ StringBuilder restrictedOpsValue = new StringBuilder();
+ restrictedOpsValue.append("[");
+ final int restrictedOpCount = restrictedOps.size();
+ for (int j = 0; j < restrictedOpCount; j++) {
+ if (restrictedOpsValue.length() > 1) {
+ restrictedOpsValue.append(", ");
+ }
+ restrictedOpsValue.append(AppOpsManager.opToName(restrictedOps.keyAt(j)));
+ }
+ restrictedOpsValue.append("]");
+ pw.println(" Restricted ops: " + restrictedOpsValue);
+ }
+
+ if (!showUserRestrictions) {
+ return;
+ }
+
+ final int userRestrictionCount = mUserRestrictions.size();
+ for (int i = 0; i < userRestrictionCount; i++) {
+ Object token = mUserRestrictions.keyAt(i);
+ SparseArray<SparseBooleanArray> perUserRestrictions = mUserRestrictions.get(token);
+ SparseArray<PackageTagsList> perUserExcludedPackageTags =
+ mUserRestrictionExcludedPackageTags.get(token);
+
+ boolean printedTokenHeader = false;
+
+ final int restrictionCount = perUserRestrictions != null
+ ? perUserRestrictions.size() : 0;
+ if (restrictionCount > 0 && dumpPackage == null) {
+ boolean printedOpsHeader = false;
+ for (int j = 0; j < restrictionCount; j++) {
+ int userId = perUserRestrictions.keyAt(j);
+ SparseBooleanArray restrictedOps = perUserRestrictions.valueAt(j);
+ if (restrictedOps == null) {
+ continue;
+ }
+ if (code >= 0 && !restrictedOps.get(code)) {
+ continue;
+ }
+ if (!printedTokenHeader) {
+ pw.println(" User restrictions for token " + token + ":");
+ printedTokenHeader = true;
+ }
+ if (!printedOpsHeader) {
+ pw.println(" Restricted ops:");
+ printedOpsHeader = true;
+ }
+ StringBuilder restrictedOpsValue = new StringBuilder();
+ restrictedOpsValue.append("[");
+ final int restrictedOpCount = restrictedOps.size();
+ for (int k = 0; k < restrictedOpCount; k++) {
+ int restrictedOp = restrictedOps.keyAt(k);
+ if (restrictedOpsValue.length() > 1) {
+ restrictedOpsValue.append(", ");
+ }
+ restrictedOpsValue.append(AppOpsManager.opToName(restrictedOp));
+ }
+ restrictedOpsValue.append("]");
+ pw.print(" ");
+ pw.print("user: ");
+ pw.print(userId);
+ pw.print(" restricted ops: ");
+ pw.println(restrictedOpsValue);
+ }
+ }
+
+ final int excludedPackageCount = perUserExcludedPackageTags != null
+ ? perUserExcludedPackageTags.size() : 0;
+ if (excludedPackageCount > 0 && code < 0) {
+ IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
+ ipw.increaseIndent();
+ boolean printedPackagesHeader = false;
+ for (int j = 0; j < excludedPackageCount; j++) {
+ int userId = perUserExcludedPackageTags.keyAt(j);
+ PackageTagsList packageNames =
+ perUserExcludedPackageTags.valueAt(j);
+ if (packageNames == null) {
+ continue;
+ }
+ boolean hasPackage;
+ if (dumpPackage != null) {
+ hasPackage = packageNames.includes(dumpPackage);
+ } else {
+ hasPackage = true;
+ }
+ if (!hasPackage) {
+ continue;
+ }
+ if (!printedTokenHeader) {
+ ipw.println("User restrictions for token " + token + ":");
+ printedTokenHeader = true;
+ }
+
+ ipw.increaseIndent();
+ if (!printedPackagesHeader) {
+ ipw.println("Excluded packages:");
+ printedPackagesHeader = true;
+ }
+
+ ipw.increaseIndent();
+ ipw.print("user: ");
+ ipw.print(userId);
+ ipw.println(" packages: ");
+
+ ipw.increaseIndent();
+ packageNames.dump(ipw);
+
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+ ipw.decreaseIndent();
+ }
+ ipw.decreaseIndent();
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 62ae9b8a9789..bc650ad2bd76 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -16,9 +16,6 @@
package com.android.server.appop;
-import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
-import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
-import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED;
import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
@@ -36,7 +33,6 @@ import static android.app.AppOpsManager.MODE_DEFAULT;
import static android.app.AppOpsManager.MODE_ERRORED;
import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.MODE_IGNORED;
-import static android.app.AppOpsManager.NoteOpEvent;
import static android.app.AppOpsManager.OP_CAMERA;
import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_FLAG_SELF;
@@ -46,8 +42,8 @@ import static android.app.AppOpsManager.OP_PLAY_AUDIO;
import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO;
import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD;
+import static android.app.AppOpsManager.OP_VIBRATE;
import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED;
-import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_RESUMED;
import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_STARTED;
import static android.app.AppOpsManager.OpEventProxyInfo;
import static android.app.AppOpsManager.RestrictionBypass;
@@ -56,23 +52,14 @@ import static android.app.AppOpsManager.SAMPLING_STRATEGY_RARELY_USED;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM;
import static android.app.AppOpsManager.SAMPLING_STRATEGY_UNIFORM_OPS;
import static android.app.AppOpsManager.SECURITY_EXCEPTION_ON_INVALID_ATTRIBUTION_TAG_CHANGE;
-import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
-import static android.app.AppOpsManager.UID_STATE_CACHED;
-import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
-import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
-import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
-import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
-import static android.app.AppOpsManager.UID_STATE_TOP;
import static android.app.AppOpsManager._NUM_OP;
import static android.app.AppOpsManager.extractFlagsFromKey;
import static android.app.AppOpsManager.extractUidStateFromKey;
-import static android.app.AppOpsManager.makeKey;
import static android.app.AppOpsManager.modeToName;
import static android.app.AppOpsManager.opAllowSystemBypassRestriction;
import static android.app.AppOpsManager.opRestrictsRead;
import static android.app.AppOpsManager.opToName;
import static android.app.AppOpsManager.opToPublicName;
-import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
@@ -80,10 +67,7 @@ import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
-import static java.lang.Long.max;
-
import android.Manifest;
-import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -113,7 +97,6 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PermissionInfo;
-import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
import android.net.Uri;
@@ -122,6 +105,7 @@ import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.PackageTagsList;
import android.os.Process;
@@ -134,19 +118,14 @@ import android.os.ShellCallback;
import android.os.ShellCommand;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.os.UserManager;
import android.os.storage.StorageManagerInternal;
import android.permission.PermissionManager;
import android.provider.Settings;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
-import android.util.IndentingPrintWriter;
import android.util.KeyValueListParser;
-import android.util.LongSparseArray;
import android.util.Pair;
-import android.util.Pools;
-import android.util.Pools.SimplePool;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -167,6 +146,7 @@ import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsStartedCallback;
import com.android.internal.app.MessageSamplingConfig;
import com.android.internal.compat.IPlatformCompat;
+import com.android.internal.os.Clock;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.Preconditions;
@@ -209,7 +189,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Scanner;
import java.util.Set;
@@ -236,39 +215,11 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
// Constant meaning that any UID should be matched when dispatching callbacks
private static final int UID_ANY = -2;
- // Map from process states to the uid states we track.
- private static final int[] PROCESS_STATE_TO_UID_STATE = new int[] {
- UID_STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT
- UID_STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI
- UID_STATE_TOP, // ActivityManager.PROCESS_STATE_TOP
- UID_STATE_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_TOP
- UID_STATE_FOREGROUND_SERVICE, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
- UID_STATE_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
- UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
- UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
- UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
- UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_BACKUP
- UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_SERVICE
- UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_RECEIVER
- UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
- UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
- UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_HOME
- UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
- UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
- UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
- UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_RECENT
- UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_EMPTY
- UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_NONEXISTENT
- };
-
private static final int[] OPS_RESTRICTED_ON_SUSPEND = {
OP_PLAY_AUDIO,
OP_RECORD_AUDIO,
OP_CAMERA,
- };
-
- private static final int[] WATCHABLE_NON_PERMISSION_OPS = {
- OP_RECEIVE_AMBIENT_TRIGGER_AUDIO,
+ OP_VIBRATE,
};
private static final int MAX_UNFORWARDED_OPS = 10;
@@ -280,14 +231,22 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
private final @Nullable File mNoteOpCallerStacktracesFile;
final Handler mHandler;
- /** Pool for {@link OpEventProxyInfoPool} to avoid to constantly reallocate new objects */
+ /**
+ * Pool for {@link AttributedOp.OpEventProxyInfoPool} to avoid to constantly reallocate new
+ * objects
+ */
@GuardedBy("this")
- private final OpEventProxyInfoPool mOpEventProxyInfoPool = new OpEventProxyInfoPool();
+ final AttributedOp.OpEventProxyInfoPool mOpEventProxyInfoPool =
+ new AttributedOp.OpEventProxyInfoPool(MAX_UNUSED_POOLED_OBJECTS);
- /** Pool for {@link InProgressStartOpEventPool} to avoid to constantly reallocate new objects */
+ /**
+ * Pool for {@link AttributedOp.InProgressStartOpEventPool} to avoid to constantly reallocate
+ * new objects
+ */
@GuardedBy("this")
- private final InProgressStartOpEventPool mInProgressStartOpEventPool =
- new InProgressStartOpEventPool();
+ final AttributedOp.InProgressStartOpEventPool mInProgressStartOpEventPool =
+ new AttributedOp.InProgressStartOpEventPool(mOpEventProxyInfoPool,
+ MAX_UNUSED_POOLED_OBJECTS);
private final AppOpsManagerInternalImpl mAppOpsManagerInternal
= new AppOpsManagerInternalImpl();
@@ -346,8 +305,6 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
volatile @NonNull HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this);
- long mLastRealtime;
-
/*
* These are app op restrictions imposed per user from various parties.
*/
@@ -408,58 +365,29 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
/** Interface for app-op modes.*/
@VisibleForTesting AppOpsServiceInterface mAppOpsServiceInterface;
- /**
- * An unsynchronized pool of {@link OpEventProxyInfo} objects.
- */
- private class OpEventProxyInfoPool extends SimplePool<OpEventProxyInfo> {
- OpEventProxyInfoPool() {
- super(MAX_UNUSED_POOLED_OBJECTS);
- }
-
- OpEventProxyInfo acquire(@IntRange(from = 0) int uid, @Nullable String packageName,
- @Nullable String attributionTag) {
- OpEventProxyInfo recycled = acquire();
- if (recycled != null) {
- recycled.reinit(uid, packageName, attributionTag);
- return recycled;
- }
-
- return new OpEventProxyInfo(uid, packageName, attributionTag);
- }
- }
-
- /**
- * An unsynchronized pool of {@link InProgressStartOpEvent} objects.
- */
- private class InProgressStartOpEventPool extends SimplePool<InProgressStartOpEvent> {
- InProgressStartOpEventPool() {
- super(MAX_UNUSED_POOLED_OBJECTS);
- }
+ /** Interface for app-op restrictions.*/
+ @VisibleForTesting AppOpsRestrictions mAppOpsRestrictions;
- InProgressStartOpEvent acquire(long startTime, long elapsedTime, @NonNull IBinder clientId,
- @Nullable String attributionTag, @NonNull Runnable onDeath, int proxyUid,
- @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
- @AppOpsManager.UidState int uidState, @OpFlags int flags, @AttributionFlags
- int attributionFlags, int attributionChainId) throws RemoteException {
+ private AppOpsUidStateTracker mUidStateTracker;
- InProgressStartOpEvent recycled = acquire();
-
- OpEventProxyInfo proxyInfo = null;
- if (proxyUid != Process.INVALID_UID) {
- proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
- proxyAttributionTag);
- }
-
- if (recycled != null) {
- recycled.reinit(startTime, elapsedTime, clientId, attributionTag, onDeath,
- uidState, flags, proxyInfo, attributionFlags, attributionChainId,
- mOpEventProxyInfoPool);
- return recycled;
- }
+ /** Hands the definition of foreground and uid states */
+ @GuardedBy("this")
+ public AppOpsUidStateTracker getUidStateTracker() {
+ if (mUidStateTracker == null) {
+ mUidStateTracker = new AppOpsUidStateTrackerImpl(
+ LocalServices.getService(ActivityManagerInternal.class),
+ mHandler,
+ r -> {
+ synchronized (AppOpsService.this) {
+ r.run();
+ }
+ },
+ Clock.SYSTEM_CLOCK, mConstants);
- return new InProgressStartOpEvent(startTime, elapsedTime, clientId, attributionTag,
- onDeath, uidState, proxyInfo, flags, attributionFlags, attributionChainId);
+ mUidStateTracker.addUidStateChangedCallback(new HandlerExecutor(mHandler),
+ this::onUidStateChanged);
}
+ return mUidStateTracker;
}
/**
@@ -467,7 +395,6 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
* global Settings. Any access to this class or its fields should be done while
* holding the AppOpsService lock.
*/
- @VisibleForTesting
final class Constants extends ContentObserver {
/**
@@ -555,14 +482,6 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
final class UidState {
public final int uid;
- public int state = UID_STATE_CACHED;
- public int pendingState = UID_STATE_CACHED;
- public long pendingStateCommitTime;
- public int capability;
- public int pendingCapability;
- public boolean appWidgetVisible;
- public boolean pendingAppWidgetVisible;
-
public ArrayMap<String, Ops> pkgOps;
// true indicates there is an interested observer, false there isn't but it has such an op
@@ -596,10 +515,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
return (pkgOps == null || pkgOps.isEmpty())
- && (state == UID_STATE_CACHED
- && (pendingState == UID_STATE_CACHED))
- && (mAppOpsServiceInterface.areUidModesDefault(uid)
- && areAllPackageModesDefault);
+ && mAppOpsServiceInterface.areUidModesDefault(uid)
+ && areAllPackageModesDefault;
}
// Functions for uid mode access and manipulation.
@@ -615,52 +532,9 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
return mAppOpsServiceInterface.setUidMode(uid, op, mode);
}
+ @SuppressWarnings("GuardedBy")
int evalMode(int op, int mode) {
- if (mode == MODE_FOREGROUND) {
- if (appWidgetVisible) {
- return MODE_ALLOWED;
- } else if (mActivityManagerInternal != null
- && mActivityManagerInternal.isPendingTopUid(uid)) {
- return MODE_ALLOWED;
- } else if (mActivityManagerInternal != null
- && mActivityManagerInternal.isTempAllowlistedForFgsWhileInUse(uid)) {
- return MODE_ALLOWED;
- } else if (state <= UID_STATE_TOP) {
- // process is in TOP.
- return MODE_ALLOWED;
- } else if (state <= AppOpsManager.resolveFirstUnrestrictedUidState(op)) {
- // process is in foreground, check its capability.
- switch (op) {
- case AppOpsManager.OP_FINE_LOCATION:
- case AppOpsManager.OP_COARSE_LOCATION:
- case AppOpsManager.OP_MONITOR_LOCATION:
- case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
- if ((capability & PROCESS_CAPABILITY_FOREGROUND_LOCATION) != 0) {
- return MODE_ALLOWED;
- } else {
- return MODE_IGNORED;
- }
- case OP_CAMERA:
- if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) {
- return MODE_ALLOWED;
- } else {
- return MODE_IGNORED;
- }
- case OP_RECORD_AUDIO:
- if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) {
- return MODE_ALLOWED;
- } else {
- return MODE_IGNORED;
- }
- default:
- return MODE_ALLOWED;
- }
- } else {
- // process is not in foreground.
- return MODE_IGNORED;
- }
- }
- return mode;
+ return getUidStateTracker().evalMode(uid, op, mode);
}
public void evalForegroundOps() {
@@ -683,6 +557,16 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
}
+
+ @SuppressWarnings("GuardedBy")
+ public int getState() {
+ return getUidStateTracker().getUidState(uid);
+ }
+
+ @SuppressWarnings("GuardedBy")
+ public void dump(PrintWriter pw, long nowElapsed) {
+ getUidStateTracker().dumpUidState(pw, uid, nowElapsed);
+ }
}
final static class Ops extends SparseArray<Op> {
@@ -710,7 +594,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
- /** Returned from {@link #verifyAndGetBypass(int, String, String, String, boolean)}. */
+ /** Returned from {@link #verifyAndGetBypass(int, String, String, String)}. */
private static final class PackageVerificationResult {
final RestrictionBypass bypass;
@@ -722,740 +606,6 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
- /** A in progress startOp->finishOp event */
- private static final class InProgressStartOpEvent implements IBinder.DeathRecipient {
- /** Wall clock time of startOp event (not monotonic) */
- private long mStartTime;
-
- /** Elapsed time since boot of startOp event */
- private long mStartElapsedTime;
-
- /** Id of the client that started the event */
- private @NonNull IBinder mClientId;
-
- /** The attribution tag for this operation */
- private @Nullable String mAttributionTag;
-
- /** To call when client dies */
- private @NonNull Runnable mOnDeath;
-
- /** uidstate used when calling startOp */
- private @AppOpsManager.UidState int mUidState;
-
- /** Proxy information of the startOp event */
- private @Nullable OpEventProxyInfo mProxy;
-
- /** Proxy flag information */
- private @OpFlags int mFlags;
-
- /** How many times the op was started but not finished yet */
- int numUnfinishedStarts;
-
- /** The attribution flags related to this event */
- private @AttributionFlags int mAttributionFlags;
-
- /** The id of the attribution chain this even is a part of */
- private int mAttributionChainId;
-
- /**
- * Create a new {@link InProgressStartOpEvent}.
- *
- * @param startTime The time {@link #startOperation} was called
- * @param startElapsedTime The elapsed time when {@link #startOperation} was called
- * @param clientId The client id of the caller of {@link #startOperation}
- * @param attributionTag The attribution tag for the operation.
- * @param onDeath The code to execute on client death
- * @param uidState The uidstate of the app {@link #startOperation} was called for
- * @param attributionFlags the attribution flags for this operation.
- * @param attributionChainId the unique id of the attribution chain this op is a part of.
- * @param proxy The proxy information, if {@link #startProxyOperation} was called
- * @param flags The trusted/nontrusted/self flags.
- *
- * @throws RemoteException If the client is dying
- */
- private InProgressStartOpEvent(long startTime, long startElapsedTime,
- @NonNull IBinder clientId, @Nullable String attributionTag,
- @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState,
- @Nullable OpEventProxyInfo proxy, @OpFlags int flags,
- @AttributionFlags int attributionFlags, int attributionChainId)
- throws RemoteException {
- mStartTime = startTime;
- mStartElapsedTime = startElapsedTime;
- mClientId = clientId;
- mAttributionTag = attributionTag;
- mOnDeath = onDeath;
- mUidState = uidState;
- mProxy = proxy;
- mFlags = flags;
- mAttributionFlags = attributionFlags;
- mAttributionChainId = attributionChainId;
-
- clientId.linkToDeath(this, 0);
- }
-
- /** Clean up event */
- public void finish() {
- try {
- mClientId.unlinkToDeath(this, 0);
- } catch (NoSuchElementException e) {
- // Either not linked, or already unlinked. Either way, nothing to do.
- }
- }
-
- @Override
- public void binderDied() {
- mOnDeath.run();
- }
-
- /**
- * Reinit existing object with new state.
- *
- * @param startTime The time {@link #startOperation} was called
- * @param startElapsedTime The elapsed time when {@link #startOperation} was called
- * @param clientId The client id of the caller of {@link #startOperation}
- * @param attributionTag The attribution tag for this operation.
- * @param onDeath The code to execute on client death
- * @param uidState The uidstate of the app {@link #startOperation} was called for
- * @param flags The flags relating to the proxy
- * @param proxy The proxy information, if {@link #startProxyOperation} was called
- * @param attributionFlags the attribution flags for this operation.
- * @param attributionChainId the unique id of the attribution chain this op is a part of.
- * @param proxyPool The pool to release previous {@link OpEventProxyInfo} to
- *
- * @throws RemoteException If the client is dying
- */
- public void reinit(long startTime, long startElapsedTime, @NonNull IBinder clientId,
- @Nullable String attributionTag, @NonNull Runnable onDeath,
- @AppOpsManager.UidState int uidState, @OpFlags int flags,
- @Nullable OpEventProxyInfo proxy, @AttributionFlags int attributionFlags,
- int attributionChainId, @NonNull Pools.Pool<OpEventProxyInfo> proxyPool
- ) throws RemoteException {
- mStartTime = startTime;
- mStartElapsedTime = startElapsedTime;
- mClientId = clientId;
- mAttributionTag = attributionTag;
- mOnDeath = onDeath;
- mUidState = uidState;
- mFlags = flags;
-
- if (mProxy != null) {
- proxyPool.release(mProxy);
- }
- mProxy = proxy;
- mAttributionFlags = attributionFlags;
- mAttributionChainId = attributionChainId;
-
- clientId.linkToDeath(this, 0);
- }
-
- /** @return Wall clock time of startOp event */
- public long getStartTime() {
- return mStartTime;
- }
-
- /** @return Elapsed time since boot of startOp event */
- public long getStartElapsedTime() {
- return mStartElapsedTime;
- }
-
- /** @return Id of the client that started the event */
- public @NonNull IBinder getClientId() {
- return mClientId;
- }
-
- /** @return uidstate used when calling startOp */
- public @AppOpsManager.UidState int getUidState() {
- return mUidState;
- }
-
- /** @return proxy tag for the access */
- public @Nullable OpEventProxyInfo getProxy() {
- return mProxy;
- }
-
- /** @return flags used for the access */
- public @OpFlags int getFlags() {
- return mFlags;
- }
-
- /** @return attributoin flags used for the access */
- public @AttributionFlags int getAttributionFlags() {
- return mAttributionFlags;
- }
-
- /** @return attribution chain id for the access */
- public int getAttributionChainId() {
- return mAttributionChainId;
- }
- }
-
- private final class AttributedOp {
- public final @Nullable String tag;
- public final @NonNull Op parent;
-
- /**
- * Last successful accesses (noteOp + finished startOp) for each uidState/opFlag combination
- *
- * <p>Key is {@link AppOpsManager#makeKey}
- */
- @GuardedBy("AppOpsService.this")
- private @Nullable LongSparseArray<NoteOpEvent> mAccessEvents;
-
- /**
- * Last rejected accesses for each uidState/opFlag combination
- *
- * <p>Key is {@link AppOpsManager#makeKey}
- */
- @GuardedBy("AppOpsService.this")
- private @Nullable LongSparseArray<NoteOpEvent> mRejectEvents;
-
- /**
- * Currently in progress startOp events
- *
- * <p>Key is clientId
- */
- @GuardedBy("AppOpsService.this")
- private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents;
-
- /**
- * Currently paused startOp events
- *
- * <p>Key is clientId
- */
- @GuardedBy("AppOpsService.this")
- private @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mPausedInProgressEvents;
-
- AttributedOp(@Nullable String tag, @NonNull Op parent) {
- this.tag = tag;
- this.parent = parent;
- }
-
- /**
- * Update state when noteOp was rejected or startOp->finishOp event finished
- *
- * @param proxyUid The uid of the proxy
- * @param proxyPackageName The package name of the proxy
- * @param proxyAttributionTag the attributionTag in the proxies package
- * @param uidState UID state of the app noteOp/startOp was called for
- * @param flags OpFlags of the call
- */
- public void accessed(int proxyUid, @Nullable String proxyPackageName,
- @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState,
- @OpFlags int flags) {
- long accessTime = System.currentTimeMillis();
- accessed(accessTime, -1, proxyUid, proxyPackageName,
- proxyAttributionTag, uidState, flags);
-
- mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid, parent.packageName,
- tag, uidState, flags, accessTime, AppOpsManager.ATTRIBUTION_FLAGS_NONE,
- AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
- }
-
- /**
- * Add an access that was previously collected.
- *
- * @param noteTime The time of the event
- * @param duration The duration of the event
- * @param proxyUid The uid of the proxy
- * @param proxyPackageName The package name of the proxy
- * @param proxyAttributionTag the attributionTag in the proxies package
- * @param uidState UID state of the app noteOp/startOp was called for
- * @param flags OpFlags of the call
- */
- public void accessed(long noteTime, long duration, int proxyUid,
- @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
- @AppOpsManager.UidState int uidState, @OpFlags int flags) {
- long key = makeKey(uidState, flags);
-
- if (mAccessEvents == null) {
- mAccessEvents = new LongSparseArray<>(1);
- }
-
- OpEventProxyInfo proxyInfo = null;
- if (proxyUid != Process.INVALID_UID) {
- proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
- proxyAttributionTag);
- }
-
- NoteOpEvent existingEvent = mAccessEvents.get(key);
- if (existingEvent != null) {
- existingEvent.reinit(noteTime, duration, proxyInfo, mOpEventProxyInfoPool);
- } else {
- mAccessEvents.put(key, new NoteOpEvent(noteTime, duration, proxyInfo));
- }
- }
-
- /**
- * Update state when noteOp/startOp was rejected.
- *
- * @param uidState UID state of the app noteOp is called for
- * @param flags OpFlags of the call
- */
- public void rejected(@AppOpsManager.UidState int uidState, @OpFlags int flags) {
- rejected(System.currentTimeMillis(), uidState, flags);
-
- mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid, parent.packageName,
- tag, uidState, flags);
- }
-
- /**
- * Add an rejection that was previously collected
- *
- * @param noteTime The time of the event
- * @param uidState UID state of the app noteOp/startOp was called for
- * @param flags OpFlags of the call
- */
- public void rejected(long noteTime, @AppOpsManager.UidState int uidState,
- @OpFlags int flags) {
- long key = makeKey(uidState, flags);
-
- if (mRejectEvents == null) {
- mRejectEvents = new LongSparseArray<>(1);
- }
-
- // We do not collect proxy information for rejections yet
- NoteOpEvent existingEvent = mRejectEvents.get(key);
- if (existingEvent != null) {
- existingEvent.reinit(noteTime, -1, null, mOpEventProxyInfoPool);
- } else {
- mRejectEvents.put(key, new NoteOpEvent(noteTime, -1, null));
- }
- }
-
- /**
- * Update state when start was called
- *
- * @param clientId Id of the startOp caller
- * @param proxyUid The UID of the proxy app
- * @param proxyPackageName The package name of the proxy app
- * @param proxyAttributionTag The attribution tag of the proxy app
- * @param uidState UID state of the app startOp is called for
- * @param flags The proxy flags
- * @param attributionFlags The attribution flags associated with this operation.
- * @param attributionChainId The if of the attribution chain this operations is a part of.
- */
- public void started(@NonNull IBinder clientId, int proxyUid,
- @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
- @AppOpsManager.UidState int uidState, @OpFlags int flags, @AttributionFlags
- int attributionFlags, int attributionChainId) throws RemoteException {
- started(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
- uidState, flags,/*triggerCallbackIfNeeded*/ true, attributionFlags,
- attributionChainId);
- }
-
- private void started(@NonNull IBinder clientId, int proxyUid,
- @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
- @AppOpsManager.UidState int uidState, @OpFlags int flags,
- boolean triggerCallbackIfNeeded, @AttributionFlags int attributionFlags,
- int attributionChainId) throws RemoteException {
- startedOrPaused(clientId, proxyUid, proxyPackageName,
- proxyAttributionTag, uidState, flags, triggerCallbackIfNeeded,
- /*triggerCallbackIfNeeded*/ true, attributionFlags, attributionChainId);
- }
-
- private void startedOrPaused(@NonNull IBinder clientId, int proxyUid,
- @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
- @AppOpsManager.UidState int uidState, @OpFlags int flags,
- boolean triggerCallbackIfNeeded, boolean isStarted, @AttributionFlags
- int attributionFlags, int attributionChainId) throws RemoteException {
- if (triggerCallbackIfNeeded && !parent.isRunning() && isStarted) {
- scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName,
- tag, true, attributionFlags, attributionChainId);
- }
-
- if (isStarted && mInProgressEvents == null) {
- mInProgressEvents = new ArrayMap<>(1);
- } else if (!isStarted && mPausedInProgressEvents == null) {
- mPausedInProgressEvents = new ArrayMap<>(1);
- }
- ArrayMap<IBinder, InProgressStartOpEvent> events = isStarted
- ? mInProgressEvents : mPausedInProgressEvents;
-
- long startTime = System.currentTimeMillis();
- InProgressStartOpEvent event = events.get(clientId);
- if (event == null) {
- event = mInProgressStartOpEventPool.acquire(startTime,
- SystemClock.elapsedRealtime(), clientId, tag,
- PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
- proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags,
- attributionFlags, attributionChainId);
- events.put(clientId, event);
- } else {
- if (uidState != event.mUidState) {
- onUidStateChanged(uidState);
- }
- }
-
- event.numUnfinishedStarts++;
-
- if (isStarted) {
- mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
- parent.packageName, tag, uidState, flags, startTime, attributionFlags,
- attributionChainId);
- }
- }
-
- /**
- * Update state when finishOp was called. Will finish started ops, and delete paused ops.
- *
- * @param clientId Id of the finishOp caller
- */
- public void finished(@NonNull IBinder clientId) {
- finished(clientId, true);
- }
-
- private void finished(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded) {
- finishOrPause(clientId, triggerCallbackIfNeeded, false);
- }
-
- /**
- * Update state when paused or finished is called. If pausing, it records the op as
- * stopping in the HistoricalRegistry, but does not delete it.
- */
- private void finishOrPause(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded,
- boolean isPausing) {
- int indexOfToken = isRunning() ? mInProgressEvents.indexOfKey(clientId) : -1;
- if (indexOfToken < 0) {
- finishPossiblyPaused(clientId, isPausing);
- return;
- }
-
- InProgressStartOpEvent event = mInProgressEvents.valueAt(indexOfToken);
- if (!isPausing) {
- event.numUnfinishedStarts--;
- }
- // If we are pausing, create a NoteOpEvent, but don't change the InProgress event
- if (event.numUnfinishedStarts == 0 || isPausing) {
- if (!isPausing) {
- event.finish();
- mInProgressEvents.removeAt(indexOfToken);
- }
-
- if (mAccessEvents == null) {
- mAccessEvents = new LongSparseArray<>(1);
- }
-
- OpEventProxyInfo proxyCopy = event.getProxy() != null
- ? new OpEventProxyInfo(event.getProxy()) : null;
-
- long accessDurationMillis =
- SystemClock.elapsedRealtime() - event.getStartElapsedTime();
- NoteOpEvent finishedEvent = new NoteOpEvent(event.getStartTime(),
- accessDurationMillis, proxyCopy);
- mAccessEvents.put(makeKey(event.getUidState(), event.getFlags()),
- finishedEvent);
-
- mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
- parent.packageName, tag, event.getUidState(),
- event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
- event.getAttributionFlags(), event.getAttributionChainId());
-
- if (!isPausing) {
- mInProgressStartOpEventPool.release(event);
- if (mInProgressEvents.isEmpty()) {
- mInProgressEvents = null;
-
- // TODO ntmyren: Also callback for single attribution tag activity changes
- if (triggerCallbackIfNeeded && !parent.isRunning()) {
- scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
- parent.packageName, tag, false, event.getAttributionFlags(),
- event.getAttributionChainId());
- }
- }
- }
- }
- }
-
- // Finish or pause (no-op) an already paused op
- private void finishPossiblyPaused(@NonNull IBinder clientId, boolean isPausing) {
- if (!isPaused()) {
- Slog.wtf(TAG, "No ops running or paused");
- return;
- }
-
- int indexOfToken = mPausedInProgressEvents.indexOfKey(clientId);
- if (indexOfToken < 0) {
- Slog.wtf(TAG, "No op running or paused for the client");
- return;
- } else if (isPausing) {
- // already paused
- return;
- }
-
- // no need to record a paused event finishing.
- InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(indexOfToken);
- event.numUnfinishedStarts--;
- if (event.numUnfinishedStarts == 0) {
- mPausedInProgressEvents.removeAt(indexOfToken);
- mInProgressStartOpEventPool.release(event);
- if (mPausedInProgressEvents.isEmpty()) {
- mPausedInProgressEvents = null;
- }
- }
- }
-
- /**
- * Create an event that will be started, if the op is unpaused.
- */
- public void createPaused(@NonNull IBinder clientId, int proxyUid,
- @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
- @AppOpsManager.UidState int uidState, @OpFlags int flags, @AttributionFlags
- int attributionFlags, int attributionChainId) throws RemoteException {
- startedOrPaused(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
- uidState, flags, true, false, attributionFlags, attributionChainId);
- }
-
- /**
- * Pause all currently started ops. This will create a HistoricalRegistry
- */
- public void pause() {
- if (!isRunning()) {
- return;
- }
-
- if (mPausedInProgressEvents == null) {
- mPausedInProgressEvents = new ArrayMap<>(1);
- }
-
- for (int i = 0; i < mInProgressEvents.size(); i++) {
- InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
- mPausedInProgressEvents.put(event.mClientId, event);
- finishOrPause(event.mClientId, true, true);
-
- scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
- parent.packageName, tag, false,
- event.getAttributionFlags(), event.getAttributionChainId());
- }
- mInProgressEvents = null;
- }
-
- /**
- * Unpause all currently paused ops. This will reinitialize their start and duration
- * times, but keep all other values the same
- */
- public void resume() {
- if (!isPaused()) {
- return;
- }
-
- if (mInProgressEvents == null) {
- mInProgressEvents = new ArrayMap<>(mPausedInProgressEvents.size());
- }
- boolean shouldSendActive = !mPausedInProgressEvents.isEmpty()
- && mInProgressEvents.isEmpty();
-
- long startTime = System.currentTimeMillis();
- for (int i = 0; i < mPausedInProgressEvents.size(); i++) {
- InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(i);
- mInProgressEvents.put(event.mClientId, event);
- event.mStartElapsedTime = SystemClock.elapsedRealtime();
- event.mStartTime = startTime;
- mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
- parent.packageName, tag, event.mUidState, event.mFlags, startTime,
- event.getAttributionFlags(), event.getAttributionChainId());
- if (shouldSendActive) {
- scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName,
- tag, true, event.getAttributionFlags(), event.getAttributionChainId());
- }
- // Note: this always sends MODE_ALLOWED, even if the mode is FOREGROUND
- // TODO ntmyren: figure out how to get the real mode.
- scheduleOpStartedIfNeededLocked(parent.op, parent.uid, parent.packageName,
- tag, event.getFlags(), MODE_ALLOWED, START_TYPE_RESUMED,
- event.getAttributionFlags(), event.getAttributionChainId());
- }
- mPausedInProgressEvents = null;
- }
-
- /**
- * Called in the case the client dies without calling finish first
- *
- * @param clientId The client that died
- */
- void onClientDeath(@NonNull IBinder clientId) {
- synchronized (AppOpsService.this) {
- if (!isPaused() && !isRunning()) {
- return;
- }
-
- ArrayMap<IBinder, InProgressStartOpEvent> events = isPaused()
- ? mPausedInProgressEvents : mInProgressEvents;
- InProgressStartOpEvent deadEvent = events.get(clientId);
- if (deadEvent != null) {
- deadEvent.numUnfinishedStarts = 1;
- }
-
- finished(clientId);
- }
- }
-
- /**
- * Notify that the state of the uid changed
- *
- * @param newState The new state
- */
- public void onUidStateChanged(@AppOpsManager.UidState int newState) {
- if (!isPaused() && !isRunning()) {
- return;
- }
-
- boolean isRunning = isRunning();
- ArrayMap<IBinder, AppOpsService.InProgressStartOpEvent> events =
- isRunning ? mInProgressEvents : mPausedInProgressEvents;
-
- int numInProgressEvents = events.size();
- List<IBinder> binders = new ArrayList<>(events.keySet());
- for (int i = 0; i < numInProgressEvents; i++) {
- InProgressStartOpEvent event = events.get(binders.get(i));
-
- if (event != null && event.getUidState() != newState) {
- try {
- // Remove all but one unfinished start count and then call finished() to
- // remove start event object
- int numPreviousUnfinishedStarts = event.numUnfinishedStarts;
- event.numUnfinishedStarts = 1;
- OpEventProxyInfo proxy = event.getProxy();
-
- finished(event.getClientId(), false);
-
- // Call started() to add a new start event object and then add the
- // previously removed unfinished start counts back
- if (proxy != null) {
- startedOrPaused(event.getClientId(), proxy.getUid(),
- proxy.getPackageName(), proxy.getAttributionTag(), newState,
- event.getFlags(), false, isRunning,
- event.getAttributionFlags(), event.getAttributionChainId());
- } else {
- startedOrPaused(event.getClientId(), Process.INVALID_UID, null, null,
- newState, event.getFlags(), false, isRunning,
- event.getAttributionFlags(), event.getAttributionChainId());
- }
-
- events = isRunning ? mInProgressEvents : mPausedInProgressEvents;
- InProgressStartOpEvent newEvent = events.get(binders.get(i));
- if (newEvent != null) {
- newEvent.numUnfinishedStarts += numPreviousUnfinishedStarts - 1;
- }
- } catch (RemoteException e) {
- if (DEBUG) Slog.e(TAG, "Cannot switch to new uidState " + newState);
- }
- }
- }
- }
-
- /**
- * Combine {@code a} and {@code b} and return the result. The result might be {@code a}
- * or {@code b}. If there is an event for the same key in both the later event is retained.
- */
- private @Nullable LongSparseArray<NoteOpEvent> add(@Nullable LongSparseArray<NoteOpEvent> a,
- @Nullable LongSparseArray<NoteOpEvent> b) {
- if (a == null) {
- return b;
- }
-
- if (b == null) {
- return a;
- }
-
- int numEventsToAdd = b.size();
- for (int i = 0; i < numEventsToAdd; i++) {
- long keyOfEventToAdd = b.keyAt(i);
- NoteOpEvent bEvent = b.valueAt(i);
- NoteOpEvent aEvent = a.get(keyOfEventToAdd);
-
- if (aEvent == null || bEvent.getNoteTime() > aEvent.getNoteTime()) {
- a.put(keyOfEventToAdd, bEvent);
- }
- }
-
- return a;
- }
-
- /**
- * Add all data from the {@code opToAdd} to this op.
- *
- * <p>If there is an event for the same key in both the later event is retained.
- * <p>{@code opToAdd} should not be used after this method is called.
- *
- * @param opToAdd The op to add
- */
- public void add(@NonNull AttributedOp opToAdd) {
- if (opToAdd.isRunning() || opToAdd.isPaused()) {
- ArrayMap<IBinder, InProgressStartOpEvent> ignoredEvents = opToAdd.isRunning()
- ? opToAdd.mInProgressEvents : opToAdd.mPausedInProgressEvents;
- Slog.w(TAG, "Ignoring " + ignoredEvents.size() + " app-ops, running: "
- + opToAdd.isRunning());
-
- int numInProgressEvents = ignoredEvents.size();
- for (int i = 0; i < numInProgressEvents; i++) {
- InProgressStartOpEvent event = ignoredEvents.valueAt(i);
-
- event.finish();
- mInProgressStartOpEventPool.release(event);
- }
- }
-
- mAccessEvents = add(mAccessEvents, opToAdd.mAccessEvents);
- mRejectEvents = add(mRejectEvents, opToAdd.mRejectEvents);
- }
-
- public boolean isRunning() {
- return mInProgressEvents != null && !mInProgressEvents.isEmpty();
- }
-
- public boolean isPaused() {
- return mPausedInProgressEvents != null && !mPausedInProgressEvents.isEmpty();
- }
-
- boolean hasAnyTime() {
- return (mAccessEvents != null && mAccessEvents.size() > 0)
- || (mRejectEvents != null && mRejectEvents.size() > 0);
- }
-
- /**
- * Clone a {@link LongSparseArray} and clone all values.
- */
- private @Nullable LongSparseArray<NoteOpEvent> deepClone(
- @Nullable LongSparseArray<NoteOpEvent> original) {
- if (original == null) {
- return original;
- }
-
- int size = original.size();
- LongSparseArray<NoteOpEvent> clone = new LongSparseArray<>(size);
- for (int i = 0; i < size; i++) {
- clone.put(original.keyAt(i), new NoteOpEvent(original.valueAt(i)));
- }
-
- return clone;
- }
-
- @NonNull AttributedOpEntry createAttributedOpEntryLocked() {
- LongSparseArray<NoteOpEvent> accessEvents = deepClone(mAccessEvents);
-
- // Add in progress events as access events
- if (isRunning()) {
- long now = SystemClock.elapsedRealtime();
- int numInProgressEvents = mInProgressEvents.size();
-
- if (accessEvents == null) {
- accessEvents = new LongSparseArray<>(numInProgressEvents);
- }
-
- for (int i = 0; i < numInProgressEvents; i++) {
- InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
-
- accessEvents.append(makeKey(event.getUidState(), event.getFlags()),
- new NoteOpEvent(event.getStartTime(), now - event.getStartElapsedTime(),
- event.getProxy()));
- }
- }
-
- LongSparseArray<NoteOpEvent> rejectEvents = deepClone(mRejectEvents);
-
- return new AttributedOpEntry(parent.op, isRunning(), accessEvents, rejectEvents);
- }
- }
-
final class Op {
int op;
int uid;
@@ -1481,10 +631,6 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
UserHandle.getUserId(this.uid));
}
- int evalMode() {
- return uidState.evalMode(op, getMode());
- }
-
void removeAttributionsWithNoTime() {
for (int i = mAttributions.size() - 1; i >= 0; i--) {
if (!mAttributions.valueAt(i).hasAnyTime()) {
@@ -1499,7 +645,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
attributedOp = mAttributions.get(attributionTag);
if (attributedOp == null) {
- attributedOp = new AttributedOp(attributionTag, parent);
+ attributedOp = new AttributedOp(AppOpsService.this, attributionTag, parent);
mAttributions.put(attributionTag, attributedOp);
}
@@ -1749,7 +895,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
/**
* Call {@link AttributedOp#onClientDeath attributedOp.onClientDeath(clientId)}.
*/
- private static void onClientDeath(@NonNull AttributedOp attributedOp,
+ static void onClientDeath(@NonNull AttributedOp attributedOp,
@NonNull IBinder clientId) {
attributedOp.onClientDeath(clientId);
}
@@ -1788,6 +934,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
mAppOpsServiceInterface =
new LegacyAppOpsServiceInterfaceImpl(this, this, handler, context, mSwitchedOps);
+ mAppOpsRestrictions = new AppOpsRestrictionsImpl(context, handler,
+ mAppOpsServiceInterface);
LockGuard.installLock(this, LockGuard.INDEX_APP_OPS);
mFile = new AtomicFile(storagePath, "appops");
@@ -1990,7 +1138,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
PackageInfo pi = getPackageManagerInternal().getPackageInfo(packageName,
PackageManager.GET_PERMISSIONS, Process.myUid(), mContext.getUserId());
if (isSamplingTarget(pi)) {
- synchronized (this) {
+ synchronized (AppOpsService.this) {
mRarelyUsedPackages.add(packageName);
}
}
@@ -2096,75 +1244,68 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
- /**
- * Update the pending state for the uid
- *
- * @param currentTime The current elapsed real time
- * @param uid The uid that has a pending state
- */
- private void updatePendingState(long currentTime, int uid) {
+ // The callback method from ForegroundPolicyInterface
+ private void onUidStateChanged(int uid, int state, boolean foregroundModeMayChange) {
synchronized (this) {
- mLastRealtime = max(currentTime, mLastRealtime);
- updatePendingStateIfNeededLocked(mUidStates.get(uid));
- }
- }
+ UidState uidState = getUidStateLocked(uid, true);
- public void updateUidProcState(int uid, int procState,
- @ActivityManager.ProcessCapability int capability) {
- synchronized (this) {
- final UidState uidState = getUidStateLocked(uid, true);
- final int newState = PROCESS_STATE_TO_UID_STATE[procState];
- if (uidState != null && (uidState.pendingState != newState
- || uidState.pendingCapability != capability)) {
- final int oldPendingState = uidState.pendingState;
- uidState.pendingState = newState;
- uidState.pendingCapability = capability;
- if (newState < uidState.state
- || (newState <= UID_STATE_MAX_LAST_NON_RESTRICTED
- && uidState.state > UID_STATE_MAX_LAST_NON_RESTRICTED)) {
- // We are moving to a more important state, or the new state may be in the
- // foreground and the old state is in the background, then always do it
- // immediately.
- commitUidPendingStateLocked(uidState);
- } else if (newState == uidState.state && capability != uidState.capability) {
- // No change on process state, but process capability has changed.
- commitUidPendingStateLocked(uidState);
- } else if (uidState.pendingStateCommitTime == 0) {
- // We are moving to a less important state for the first time,
- // delay the application for a bit.
- final long settleTime;
- if (uidState.state <= UID_STATE_TOP) {
- settleTime = mConstants.TOP_STATE_SETTLE_TIME;
- } else if (uidState.state <= UID_STATE_FOREGROUND_SERVICE) {
- settleTime = mConstants.FG_SERVICE_STATE_SETTLE_TIME;
- } else {
- settleTime = mConstants.BG_STATE_SETTLE_TIME;
+ if (uidState != null && foregroundModeMayChange && uidState.hasForegroundWatchers) {
+ for (int fgi = uidState.foregroundOps.size() - 1; fgi >= 0; fgi--) {
+ if (!uidState.foregroundOps.valueAt(fgi)) {
+ continue;
}
- final long commitTime = SystemClock.elapsedRealtime() + settleTime;
- uidState.pendingStateCommitTime = commitTime;
+ final int code = uidState.foregroundOps.keyAt(fgi);
- mHandler.sendMessageDelayed(
- PooledLambda.obtainMessage(AppOpsService::updatePendingState, this,
- commitTime + 1, uid), settleTime + 1);
+ if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)
+ && uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpChangedForAllPkgsInUid,
+ this, code, uidState.uid, true, null));
+ } else if (uidState.pkgOps != null) {
+ final ArraySet<OnOpModeChangedListener> listenerSet =
+ mAppOpsServiceInterface.getOpModeChangedListeners(code);
+ if (listenerSet != null) {
+ for (int cbi = listenerSet.size() - 1; cbi >= 0; cbi--) {
+ final OnOpModeChangedListener listener = listenerSet.valueAt(cbi);
+ if ((listener.getFlags()
+ & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0
+ || !listener.isWatchingUid(uidState.uid)) {
+ continue;
+ }
+ for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) {
+ final Op op = uidState.pkgOps.valueAt(pkgi).get(code);
+ if (op == null) {
+ continue;
+ }
+ if (op.getMode() == AppOpsManager.MODE_FOREGROUND) {
+ mHandler.sendMessage(PooledLambda.obtainMessage(
+ AppOpsService::notifyOpChanged,
+ this, listenerSet.valueAt(cbi), code, uidState.uid,
+ uidState.pkgOps.keyAt(pkgi)));
+ }
+ }
+ }
+ }
+ }
}
+ }
- if (uidState.pkgOps != null) {
- int numPkgs = uidState.pkgOps.size();
- for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
- Ops ops = uidState.pkgOps.valueAt(pkgNum);
+ if (uidState != null && uidState.pkgOps != null) {
+ int numPkgs = uidState.pkgOps.size();
+ for (int pkgNum = 0; pkgNum < numPkgs; pkgNum++) {
+ Ops ops = uidState.pkgOps.valueAt(pkgNum);
- int numOps = ops.size();
- for (int opNum = 0; opNum < numOps; opNum++) {
- Op op = ops.valueAt(opNum);
+ int numOps = ops.size();
+ for (int opNum = 0; opNum < numOps; opNum++) {
+ Op op = ops.valueAt(opNum);
- int numAttributions = op.mAttributions.size();
- for (int attributionNum = 0; attributionNum < numAttributions;
- attributionNum++) {
- AttributedOp attributedOp = op.mAttributions.valueAt(
- attributionNum);
+ int numAttributions = op.mAttributions.size();
+ for (int attributionNum = 0; attributionNum < numAttributions;
+ attributionNum++) {
+ AttributedOp attributedOp = op.mAttributions.valueAt(
+ attributionNum);
- attributedOp.onUidStateChanged(newState);
- }
+ attributedOp.onUidStateChanged(state);
}
}
}
@@ -2172,6 +1313,22 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
+ /**
+ * Notify the proc state or capability has changed for a certain UID.
+ */
+ public void updateUidProcState(int uid, int procState,
+ @ActivityManager.ProcessCapability int capability) {
+ synchronized (this) {
+ getUidStateTracker().updateUidProcState(uid, procState, capability);
+ if (!mUidStates.contains(uid)) {
+ UidState uidState = new UidState(uid);
+ mUidStates.put(uid, uidState);
+ onUidStateChanged(uid,
+ AppOpsUidStateTracker.processStateToUidState(procState), false);
+ }
+ }
+ }
+
public void shutdown() {
Slog.w(TAG, "Writing app ops before shutdown...");
boolean doWrite = false;
@@ -3162,7 +2319,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
- return raw ? op.getMode() : op.evalMode();
+ return raw ? op.getMode() : op.uidState.evalMode(op.op, op.getMode());
}
}
@@ -3380,7 +2537,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
final int switchCode = AppOpsManager.opToSwitch(code);
final UidState uidState = ops.uidState;
if (isOpRestrictedLocked(uid, code, packageName, attributionTag, pvr.bypass, false)) {
- attributedOp.rejected(uidState.state, flags);
+ attributedOp.rejected(uidState.getState(), flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_IGNORED);
return new SyncNotedAppOp(AppOpsManager.MODE_IGNORED, code, attributionTag,
@@ -3394,7 +2551,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
if (DEBUG) Slog.d(TAG, "noteOperation: uid reject #" + uidMode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName + " flags: " + AppOpsManager.flagsToString(flags));
- attributedOp.rejected(uidState.state, flags);
+ attributedOp.rejected(uidState.getState(), flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
uidMode);
return new SyncNotedAppOp(uidMode, code, attributionTag, packageName);
@@ -3402,12 +2559,12 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
: op;
- final int mode = switchOp.evalMode();
+ final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
if (mode != AppOpsManager.MODE_ALLOWED) {
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName + " flags: " + AppOpsManager.flagsToString(flags));
- attributedOp.rejected(uidState.state, flags);
+ attributedOp.rejected(uidState.getState(), flags);
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
mode);
return new SyncNotedAppOp(mode, code, attributionTag, packageName);
@@ -3422,7 +2579,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
scheduleOpNotedIfNeededLocked(code, uid, packageName, attributionTag, flags,
AppOpsManager.MODE_ALLOWED);
- attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag, uidState.state,
+ attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag,
+ uidState.getState(),
flags);
if (shouldCollectAsyncNotedOp) {
@@ -3913,7 +3071,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
+ packageName + " flags: " + AppOpsManager.flagsToString(flags));
}
if (!dryRun) {
- attributedOp.rejected(uidState.state, flags);
+ attributedOp.rejected(uidState.getState(), flags);
scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
flags, uidMode, startType, attributionFlags, attributionChainId);
}
@@ -3922,14 +3080,14 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
} else {
final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, uid, true)
: op;
- final int mode = switchOp.evalMode();
+ final int mode = switchOp.uidState.evalMode(switchOp.op, switchOp.getMode());
if (mode != AppOpsManager.MODE_ALLOWED
&& (!startIfModeDefault || mode != MODE_DEFAULT)) {
if (DEBUG) Slog.d(TAG, "startOperation: reject #" + mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName + " flags: " + AppOpsManager.flagsToString(flags));
if (!dryRun) {
- attributedOp.rejected(uidState.state, flags);
+ attributedOp.rejected(uidState.getState(), flags);
scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag,
flags, mode, startType, attributionFlags, attributionChainId);
}
@@ -3943,12 +3101,12 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
try {
if (isRestricted) {
attributedOp.createPaused(clientId, proxyUid, proxyPackageName,
- proxyAttributionTag, uidState.state, flags, attributionFlags,
- attributionChainId);
+ proxyAttributionTag, uidState.getState(), flags,
+ attributionFlags, attributionChainId);
} else {
attributedOp.started(clientId, proxyUid, proxyPackageName,
- proxyAttributionTag, uidState.state, flags, attributionFlags,
- attributionChainId);
+ proxyAttributionTag, uidState.getState(), flags,
+ attributionFlags, attributionChainId);
startType = START_TYPE_STARTED;
}
} catch (RemoteException e) {
@@ -4080,7 +3238,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
- private void scheduleOpActiveChangedIfNeededLocked(int code, int uid, @NonNull
+ void scheduleOpActiveChangedIfNeededLocked(int code, int uid, @NonNull
String packageName, @Nullable String attributionTag, boolean active, @AttributionFlags
int attributionFlags, int attributionChainId) {
ArraySet<ActiveCallback> dispatchedCallbacks = null;
@@ -4133,7 +3291,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
}
- private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName,
+ void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName,
String attributionTag, @OpFlags int flags, @Mode int result,
@AppOpsManager.OnOpStartedListener.StartedType int startedType,
@AttributionFlags int attributionFlags, int attributionChainId) {
@@ -4252,7 +3410,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
public boolean shouldCollectNotes(int opCode) {
Preconditions.checkArgumentInRange(opCode, 0, _NUM_OP - 1, "opCode");
- if (ArrayUtils.contains(WATCHABLE_NON_PERMISSION_OPS, opCode)) {
+ if (AppOpsManager.shouldForceCollectNoteForOp(opCode)) {
return true;
}
@@ -4362,101 +3520,14 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
uidState = new UidState(uid);
mUidStates.put(uid, uidState);
- } else {
- updatePendingStateIfNeededLocked(uidState);
- }
- return uidState;
- }
-
- /**
- * Check if the pending state should be updated and do so if needed
- *
- * @param uidState The uidState that might have a pending state
- */
- private void updatePendingStateIfNeededLocked(@NonNull UidState uidState) {
- if (uidState != null) {
- if (uidState.pendingStateCommitTime != 0) {
- if (uidState.pendingStateCommitTime < mLastRealtime) {
- commitUidPendingStateLocked(uidState);
- } else {
- mLastRealtime = SystemClock.elapsedRealtime();
- if (uidState.pendingStateCommitTime < mLastRealtime) {
- commitUidPendingStateLocked(uidState);
- }
- }
- }
}
- }
- private void commitUidPendingStateLocked(UidState uidState) {
- if (uidState.hasForegroundWatchers) {
- for (int fgi = uidState.foregroundOps.size() - 1; fgi >= 0; fgi--) {
- if (!uidState.foregroundOps.valueAt(fgi)) {
- continue;
- }
- final int code = uidState.foregroundOps.keyAt(fgi);
- // For location ops we consider fg state only if the fg service
- // is of location type, for all other ops any fg service will do.
- final long firstUnrestrictedUidState = resolveFirstUnrestrictedUidState(code);
- final boolean resolvedLastFg = uidState.state <= firstUnrestrictedUidState;
- final boolean resolvedNowFg = uidState.pendingState <= firstUnrestrictedUidState;
- if (resolvedLastFg == resolvedNowFg
- && uidState.capability == uidState.pendingCapability
- && uidState.appWidgetVisible == uidState.pendingAppWidgetVisible) {
- continue;
- }
-
- if (uidState.getUidMode(code) != AppOpsManager.opToDefaultMode(code)
- && uidState.getUidMode(code) == AppOpsManager.MODE_FOREGROUND) {
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsService::notifyOpChangedForAllPkgsInUid,
- this, code, uidState.uid, true, null));
- } else if (uidState.pkgOps != null) {
- final ArraySet<OnOpModeChangedListener> listenerSet =
- mAppOpsServiceInterface.getOpModeChangedListeners(code);
- if (listenerSet != null) {
- for (int cbi = listenerSet.size() - 1; cbi >= 0; cbi--) {
- final OnOpModeChangedListener listener = listenerSet.valueAt(cbi);
- if ((listener.getFlags()
- & AppOpsManager.WATCH_FOREGROUND_CHANGES) == 0
- || !listener.isWatchingUid(uidState.uid)) {
- continue;
- }
- for (int pkgi = uidState.pkgOps.size() - 1; pkgi >= 0; pkgi--) {
- final Op op = uidState.pkgOps.valueAt(pkgi).get(code);
- if (op == null) {
- continue;
- }
- if (op.getMode() == AppOpsManager.MODE_FOREGROUND) {
- mHandler.sendMessage(PooledLambda.obtainMessage(
- AppOpsService::notifyOpChanged,
- this, listenerSet.valueAt(cbi), code, uidState.uid,
- uidState.pkgOps.keyAt(pkgi)));
- }
- }
- }
- }
- }
- }
- }
- uidState.state = uidState.pendingState;
- uidState.capability = uidState.pendingCapability;
- uidState.appWidgetVisible = uidState.pendingAppWidgetVisible;
- uidState.pendingStateCommitTime = 0;
+ return uidState;
}
private void updateAppWidgetVisibility(SparseArray<String> uidPackageNames, boolean visible) {
synchronized (this) {
- for (int i = uidPackageNames.size() - 1; i >= 0; i--) {
- final int uid = uidPackageNames.keyAt(i);
- final UidState uidState = getUidStateLocked(uid, true);
- if (uidState != null && (uidState.pendingAppWidgetVisible != visible)) {
- uidState.pendingAppWidgetVisible = visible;
- if (uidState.pendingAppWidgetVisible != uidState.appWidgetVisible) {
- commitUidPendingStateLocked(uidState);
- }
- }
- }
+ getUidStateTracker().updateAppWidgetVisibility(uidPackageNames, visible);
}
}
@@ -4524,9 +3595,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
final PackageManager pm = mContext.getPackageManager();
final String supplementalPackageName = pm.getSdkSandboxPackageName();
if (Objects.equals(packageName, supplementalPackageName)) {
- int supplementalAppId = pm.getPackageUid(supplementalPackageName,
- PackageManager.PackageInfoFlags.of(0));
- uid = UserHandle.getUid(UserHandle.getUserId(uid), supplementalAppId);
+ uid = pm.getPackageUidAsUser(supplementalPackageName,
+ PackageManager.PackageInfoFlags.of(0), UserHandle.getUserId(uid));
}
} catch (PackageManager.NameNotFoundException e) {
// Shouldn't happen for the supplemental package
@@ -5747,6 +4817,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
pw.println(" Only output the watcher sections.");
pw.println(" --history");
pw.println(" Only output history.");
+ pw.println(" --uid-state-changes");
+ pw.println(" Include logs about uid state changes.");
}
private void dumpStatesLocked(@NonNull PrintWriter pw, @Nullable String filterAttributionTag,
@@ -5852,10 +4924,11 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
long maxNumStarts = 0;
int numInProgressEvents = attributedOp.mInProgressEvents.size();
for (int i = 0; i < numInProgressEvents; i++) {
- InProgressStartOpEvent event = attributedOp.mInProgressEvents.valueAt(i);
+ AttributedOp.InProgressStartOpEvent event =
+ attributedOp.mInProgressEvents.valueAt(i);
earliestElapsedTime = Math.min(earliestElapsedTime, event.getStartElapsedTime());
- maxNumStarts = Math.max(maxNumStarts, event.numUnfinishedStarts);
+ maxNumStarts = Math.max(maxNumStarts, event.mNumUnfinishedStarts);
}
pw.print(prefix + "Running start at: ");
@@ -5883,8 +4956,10 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
// TODO ntmyren: Remove the dumpHistory and dumpFilter
boolean dumpHistory = false;
boolean includeDiscreteOps = false;
+ boolean dumpUidStateChangeLogs = false;
int nDiscreteOps = 10;
@HistoricalOpsRequestFilter int dumpFilter = 0;
+ boolean dumpAll = false;
if (args != null) {
for (int i = 0; i < args.length; i++) {
@@ -5894,6 +4969,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
return;
} else if ("-a".equals(arg)) {
// dump all data
+ dumpAll = true;
} else if ("--op".equals(arg)) {
i++;
if (i >= args.length) {
@@ -5963,6 +5039,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
} else if (arg.length() > 0 && arg.charAt(0) == '-') {
pw.println("Unknown option: " + arg);
return;
+ } else if ("--uid-state-changes".equals(arg)) {
+ dumpUidStateChangeLogs = true;
} else {
pw.println("Unknown command: " + arg);
return;
@@ -6207,31 +5285,7 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
pw.print(" Uid "); UserHandle.formatUid(pw, uidState.uid); pw.println(":");
- pw.print(" state=");
- pw.println(AppOpsManager.getUidStateName(uidState.state));
- if (uidState.state != uidState.pendingState) {
- pw.print(" pendingState=");
- pw.println(AppOpsManager.getUidStateName(uidState.pendingState));
- }
- pw.print(" capability=");
- ActivityManager.printCapabilitiesFull(pw, uidState.capability);
- pw.println();
- if (uidState.capability != uidState.pendingCapability) {
- pw.print(" pendingCapability=");
- ActivityManager.printCapabilitiesFull(pw, uidState.pendingCapability);
- pw.println();
- }
- pw.print(" appWidgetVisible=");
- pw.println(uidState.appWidgetVisible);
- if (uidState.appWidgetVisible != uidState.pendingAppWidgetVisible) {
- pw.print(" pendingAppWidgetVisible=");
- pw.println(uidState.pendingAppWidgetVisible);
- }
- if (uidState.pendingStateCommitTime != 0) {
- pw.print(" pendingStateCommitTime=");
- TimeUtils.formatDuration(uidState.pendingStateCommitTime, nowElapsed, pw);
- pw.println();
- }
+ uidState.dump(pw, nowElapsed);
if (uidState.foregroundOps != null && (dumpMode < 0
|| dumpMode == AppOpsManager.MODE_FOREGROUND)) {
pw.println(" foregroundOps:");
@@ -6309,124 +5363,8 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
pw.println();
}
- final int globalRestrictionCount = mOpGlobalRestrictions.size();
- for (int i = 0; i < globalRestrictionCount; i++) {
- IBinder token = mOpGlobalRestrictions.keyAt(i);
- ClientGlobalRestrictionState restrictionState = mOpGlobalRestrictions.valueAt(i);
- ArraySet<Integer> restrictedOps = restrictionState.mRestrictedOps;
-
- pw.println(" Global restrictions for token " + token + ":");
- StringBuilder restrictedOpsValue = new StringBuilder();
- restrictedOpsValue.append("[");
- final int restrictedOpCount = restrictedOps.size();
- for (int j = 0; j < restrictedOpCount; j++) {
- if (restrictedOpsValue.length() > 1) {
- restrictedOpsValue.append(", ");
- }
- restrictedOpsValue.append(AppOpsManager.opToName(restrictedOps.valueAt(j)));
- }
- restrictedOpsValue.append("]");
- pw.println(" Restricted ops: " + restrictedOpsValue);
-
- }
-
- final int userRestrictionCount = mOpUserRestrictions.size();
- for (int i = 0; i < userRestrictionCount; i++) {
- IBinder token = mOpUserRestrictions.keyAt(i);
- ClientUserRestrictionState restrictionState = mOpUserRestrictions.valueAt(i);
- boolean printedTokenHeader = false;
-
- if (dumpMode >= 0 || dumpWatchers || dumpHistory) {
- continue;
- }
-
- final int restrictionCount = restrictionState.perUserRestrictions != null
- ? restrictionState.perUserRestrictions.size() : 0;
- if (restrictionCount > 0 && dumpPackage == null) {
- boolean printedOpsHeader = false;
- for (int j = 0; j < restrictionCount; j++) {
- int userId = restrictionState.perUserRestrictions.keyAt(j);
- boolean[] restrictedOps = restrictionState.perUserRestrictions.valueAt(j);
- if (restrictedOps == null) {
- continue;
- }
- if (dumpOp >= 0 && (dumpOp >= restrictedOps.length
- || !restrictedOps[dumpOp])) {
- continue;
- }
- if (!printedTokenHeader) {
- pw.println(" User restrictions for token " + token + ":");
- printedTokenHeader = true;
- }
- if (!printedOpsHeader) {
- pw.println(" Restricted ops:");
- printedOpsHeader = true;
- }
- StringBuilder restrictedOpsValue = new StringBuilder();
- restrictedOpsValue.append("[");
- final int restrictedOpCount = restrictedOps.length;
- for (int k = 0; k < restrictedOpCount; k++) {
- if (restrictedOps[k]) {
- if (restrictedOpsValue.length() > 1) {
- restrictedOpsValue.append(", ");
- }
- restrictedOpsValue.append(AppOpsManager.opToName(k));
- }
- }
- restrictedOpsValue.append("]");
- pw.print(" "); pw.print("user: "); pw.print(userId);
- pw.print(" restricted ops: "); pw.println(restrictedOpsValue);
- }
- }
-
- final int excludedPackageCount = restrictionState.perUserExcludedPackageTags != null
- ? restrictionState.perUserExcludedPackageTags.size() : 0;
- if (excludedPackageCount > 0 && dumpOp < 0) {
- IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
- ipw.increaseIndent();
- boolean printedPackagesHeader = false;
- for (int j = 0; j < excludedPackageCount; j++) {
- int userId = restrictionState.perUserExcludedPackageTags.keyAt(j);
- PackageTagsList packageNames =
- restrictionState.perUserExcludedPackageTags.valueAt(j);
- if (packageNames == null) {
- continue;
- }
- boolean hasPackage;
- if (dumpPackage != null) {
- hasPackage = packageNames.includes(dumpPackage);
- } else {
- hasPackage = true;
- }
- if (!hasPackage) {
- continue;
- }
- if (!printedTokenHeader) {
- ipw.println("User restrictions for token " + token + ":");
- printedTokenHeader = true;
- }
-
- ipw.increaseIndent();
- if (!printedPackagesHeader) {
- ipw.println("Excluded packages:");
- printedPackagesHeader = true;
- }
-
- ipw.increaseIndent();
- ipw.print("user: ");
- ipw.print(userId);
- ipw.println(" packages: ");
-
- ipw.increaseIndent();
- packageNames.dump(ipw);
-
- ipw.decreaseIndent();
- ipw.decreaseIndent();
- ipw.decreaseIndent();
- }
- ipw.decreaseIndent();
- }
- }
+ boolean showUserRestrictions = !(dumpMode < 0 && !dumpWatchers && !dumpHistory);
+ mAppOpsRestrictions.dumpRestrictions(pw, dumpOp, dumpPackage, showUserRestrictions);
if (!dumpHistory && !dumpWatchers) {
pw.println();
@@ -6438,6 +5376,12 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
pw.println(" AppOps policy not set.");
}
}
+
+ if (dumpAll || dumpUidStateChangeLogs) {
+ pw.println();
+ pw.println("Uid State Changes Event Log:");
+ getUidStateTracker().dumpEvents(pw);
+ }
}
// Must not hold the appops lock
@@ -7038,8 +5982,6 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
private final class ClientUserRestrictionState implements DeathRecipient {
private final IBinder token;
- SparseArray<boolean[]> perUserRestrictions;
- SparseArray<PackageTagsList> perUserExcludedPackageTags;
ClientUserRestrictionState(IBinder token)
throws RemoteException {
@@ -7049,134 +5991,29 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
public boolean setRestriction(int code, boolean restricted,
PackageTagsList excludedPackageTags, int userId) {
- boolean changed = false;
-
- if (perUserRestrictions == null && restricted) {
- perUserRestrictions = new SparseArray<>();
- }
-
- int[] users;
- if (userId == UserHandle.USER_ALL) {
- // TODO(b/162888972): this call is returning all users, not just live ones - we
- // need to either fix the method called, or rename the variable
- List<UserInfo> liveUsers = UserManager.get(mContext).getUsers();
-
- users = new int[liveUsers.size()];
- for (int i = 0; i < liveUsers.size(); i++) {
- users[i] = liveUsers.get(i).id;
- }
- } else {
- users = new int[]{userId};
- }
-
- if (perUserRestrictions != null) {
- int numUsers = users.length;
-
- for (int i = 0; i < numUsers; i++) {
- int thisUserId = users[i];
-
- boolean[] userRestrictions = perUserRestrictions.get(thisUserId);
- if (userRestrictions == null && restricted) {
- userRestrictions = new boolean[AppOpsManager._NUM_OP];
- perUserRestrictions.put(thisUserId, userRestrictions);
- }
- if (userRestrictions != null && userRestrictions[code] != restricted) {
- userRestrictions[code] = restricted;
- if (!restricted && isDefault(userRestrictions)) {
- perUserRestrictions.remove(thisUserId);
- userRestrictions = null;
- }
- changed = true;
- }
-
- if (userRestrictions != null) {
- final boolean noExcludedPackages =
- excludedPackageTags == null || excludedPackageTags.isEmpty();
- if (perUserExcludedPackageTags == null && !noExcludedPackages) {
- perUserExcludedPackageTags = new SparseArray<>();
- }
- if (perUserExcludedPackageTags != null) {
- if (noExcludedPackages) {
- perUserExcludedPackageTags.remove(thisUserId);
- if (perUserExcludedPackageTags.size() <= 0) {
- perUserExcludedPackageTags = null;
- }
- } else {
- perUserExcludedPackageTags.put(thisUserId, excludedPackageTags);
- }
- changed = true;
- }
- }
- }
- }
-
- return changed;
+ return mAppOpsRestrictions.setUserRestriction(token, userId, code,
+ restricted, excludedPackageTags);
}
- public boolean hasRestriction(int restriction, String packageName, String attributionTag,
+ public boolean hasRestriction(int code, String packageName, String attributionTag,
int userId, boolean isCheckOp) {
- if (perUserRestrictions == null) {
- return false;
- }
- boolean[] restrictions = perUserRestrictions.get(userId);
- if (restrictions == null) {
- return false;
- }
- if (!restrictions[restriction]) {
- return false;
- }
- if (perUserExcludedPackageTags == null) {
- return true;
- }
- PackageTagsList perUserExclusions = perUserExcludedPackageTags.get(userId);
- if (perUserExclusions == null) {
- return true;
- }
-
- // TODO (b/240617242) add overload for checkOp to support attribution tags
- if (isCheckOp) {
- return !perUserExclusions.includes(packageName);
- }
- return !perUserExclusions.contains(packageName, attributionTag);
+ return mAppOpsRestrictions.getUserRestriction(token, userId, code, packageName,
+ attributionTag, isCheckOp);
}
public void removeUser(int userId) {
- if (perUserExcludedPackageTags != null) {
- perUserExcludedPackageTags.remove(userId);
- if (perUserExcludedPackageTags.size() <= 0) {
- perUserExcludedPackageTags = null;
- }
- }
- if (perUserRestrictions != null) {
- perUserRestrictions.remove(userId);
- if (perUserRestrictions.size() <= 0) {
- perUserRestrictions = null;
- }
- }
+ mAppOpsRestrictions.clearUserRestrictions(token, userId);
}
public boolean isDefault() {
- return perUserRestrictions == null || perUserRestrictions.size() <= 0;
+ return !mAppOpsRestrictions.hasUserRestrictions(token);
}
@Override
public void binderDied() {
synchronized (AppOpsService.this) {
+ mAppOpsRestrictions.clearUserRestrictions(token);
mOpUserRestrictions.remove(token);
- if (perUserRestrictions == null) {
- return;
- }
- final int userCount = perUserRestrictions.size();
- for (int i = 0; i < userCount; i++) {
- final boolean[] restrictions = perUserRestrictions.valueAt(i);
- final int restrictionCount = restrictions.length;
- for (int j = 0; j < restrictionCount; j++) {
- if (restrictions[j]) {
- final int changedCode = j;
- mHandler.post(() -> notifyWatchersOfChange(changedCode, UID_ANY));
- }
- }
- }
destroy();
}
}
@@ -7184,23 +6021,10 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
public void destroy() {
token.unlinkToDeath(this, 0);
}
-
- private boolean isDefault(boolean[] array) {
- if (ArrayUtils.isEmpty(array)) {
- return true;
- }
- for (boolean value : array) {
- if (value) {
- return false;
- }
- }
- return true;
- }
}
private final class ClientGlobalRestrictionState implements DeathRecipient {
final IBinder mToken;
- final ArraySet<Integer> mRestrictedOps = new ArraySet<>();
ClientGlobalRestrictionState(IBinder token)
throws RemoteException {
@@ -7209,23 +6033,21 @@ public class AppOpsService extends IAppOpsService.Stub implements PersistenceSch
}
boolean setRestriction(int code, boolean restricted) {
- if (restricted) {
- return mRestrictedOps.add(code);
- } else {
- return mRestrictedOps.remove(code);
- }
+ return mAppOpsRestrictions.setGlobalRestriction(mToken, code, restricted);
}
boolean hasRestriction(int code) {
- return mRestrictedOps.contains(code);
+ return mAppOpsRestrictions.getGlobalRestriction(mToken, code);
}
boolean isDefault() {
- return mRestrictedOps.isEmpty();
+ return !mAppOpsRestrictions.hasGlobalRestrictions(mToken);
}
@Override
public void binderDied() {
+ mAppOpsRestrictions.clearGlobalRestrictions(mToken);
+ mOpGlobalRestrictions.remove(mToken);
destroy();
}
diff --git a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
index c4a9a4b5d727..18f659e4c62a 100644
--- a/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
+++ b/services/core/java/com/android/server/appop/AppOpsServiceInterface.java
@@ -148,6 +148,14 @@ public interface AppOpsServiceInterface {
/**
* Temporary API which will be removed once we can safely untangle the methods that use this.
* Notify that the app-op's mode is changed by triggering the change listener.
+ * @param op App-op whose mode has changed
+ * @param uid user id associated with the app-op (or, if UID_ANY, notifies all users)
+ */
+ void notifyWatchersOfChange(int op, int uid);
+
+ /**
+ * Temporary API which will be removed once we can safely untangle the methods that use this.
+ * Notify that the app-op's mode is changed by triggering the change listener.
* @param changedListener the change listener.
* @param op App-op whose mode has changed
* @param uid user id associated with the app-op
@@ -198,5 +206,4 @@ public interface AppOpsServiceInterface {
* @param printWriter writer to dump to.
*/
boolean dumpListeners(int dumpOp, int dumpUid, String dumpPackage, PrintWriter printWriter);
-
}
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
new file mode 100644
index 000000000000..742bf4b6ebc7
--- /dev/null
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTracker.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+import static android.app.ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+import static android.app.ActivityManager.PROCESS_STATE_RECEIVER;
+import static android.app.ActivityManager.PROCESS_STATE_TOP;
+import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN;
+import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
+import static android.app.AppOpsManager.UID_STATE_CACHED;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
+import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
+import static android.app.AppOpsManager.UID_STATE_TOP;
+
+import android.annotation.CallbackExecutor;
+import android.util.SparseArray;
+
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+
+interface AppOpsUidStateTracker {
+
+ // Map from process states to the uid states we track.
+ static int processStateToUidState(int procState) {
+ if (procState == PROCESS_STATE_UNKNOWN) {
+ return UID_STATE_CACHED;
+ }
+
+ if (procState <= PROCESS_STATE_PERSISTENT_UI) {
+ return UID_STATE_PERSISTENT;
+ }
+
+ if (procState <= PROCESS_STATE_TOP) {
+ return UID_STATE_TOP;
+ }
+
+ if (procState <= PROCESS_STATE_BOUND_TOP) {
+ return UID_STATE_FOREGROUND;
+ }
+
+ if (procState <= PROCESS_STATE_FOREGROUND_SERVICE) {
+ return UID_STATE_FOREGROUND_SERVICE;
+ }
+
+ if (procState <= PROCESS_STATE_BOUND_FOREGROUND_SERVICE) {
+ return UID_STATE_FOREGROUND;
+ }
+
+ if (procState <= PROCESS_STATE_RECEIVER) {
+ return UID_STATE_BACKGROUND;
+ }
+
+ return UID_STATE_CACHED;
+ }
+
+ /*
+ * begin data pushed from appopsservice
+ */
+
+ void updateUidProcState(int uid, int procState, int capability);
+
+ void updateAppWidgetVisibility(SparseArray<String> uidPackageNames, boolean visible);
+
+ /*
+ * end data pushed from appopsservice
+ */
+
+ /**
+ * Gets the {@link android.app.AppOpsManager.UidState} that the uid current is in.
+ */
+ int getUidState(int uid);
+
+ /**
+ * Given a uid, code, and mode, resolve any foregroundness to MODE_IGNORED or MODE_ALLOWED
+ */
+ int evalMode(int uid, int code, int mode);
+
+ /**
+ * Listen to changes in {@link android.app.AppOpsManager.UidState}
+ */
+ void addUidStateChangedCallback(@CallbackExecutor Executor executor,
+ UidStateChangedCallback callback);
+
+ /**
+ * Remove a {@link UidStateChangedCallback}
+ */
+ void removeUidStateChangedCallback(UidStateChangedCallback callback);
+
+ interface UidStateChangedCallback {
+ /**
+ * Invoked when a UID's {@link android.app.AppOpsManager.UidState} changes.
+ * @param uid The uid that changed.
+ * @param uidState The state that was changed to.
+ * @param foregroundModeMayChange True if there may be a op in MODE_FOREGROUND whose
+ * evaluated result may have changed.
+ */
+ void onUidStateChanged(int uid, int uidState, boolean foregroundModeMayChange);
+ }
+
+ void dumpUidState(PrintWriter pw, int uid, long nowElapsed);
+
+ void dumpEvents(PrintWriter pw);
+}
diff --git a/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
new file mode 100644
index 000000000000..3c281d13c769
--- /dev/null
+++ b/services/core/java/com/android/server/appop/AppOpsUidStateTrackerImpl.java
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
+import static android.app.ActivityManager.ProcessCapability;
+import static android.app.AppOpsManager.MIN_PRIORITY_UID_STATE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
+import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
+import static android.app.AppOpsManager.UID_STATE_TOP;
+
+import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.os.Handler;
+import android.util.ArrayMap;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+import android.util.SparseLongArray;
+import android.util.TimeUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.Clock;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.io.PrintWriter;
+import java.util.concurrent.Executor;
+
+class AppOpsUidStateTrackerImpl implements AppOpsUidStateTracker {
+
+ private static final String LOG_TAG = AppOpsUidStateTrackerImpl.class.getSimpleName();
+
+ private final DelayableExecutor mExecutor;
+ private final Clock mClock;
+ private ActivityManagerInternal mActivityManagerInternal;
+ private AppOpsService.Constants mConstants;
+
+ private SparseIntArray mUidStates = new SparseIntArray();
+ private SparseIntArray mPendingUidStates = new SparseIntArray();
+ private SparseIntArray mCapability = new SparseIntArray();
+ private SparseIntArray mPendingCapability = new SparseIntArray();
+ private SparseBooleanArray mVisibleAppWidget = new SparseBooleanArray();
+ private SparseBooleanArray mPendingVisibleAppWidget = new SparseBooleanArray();
+ private SparseLongArray mPendingCommitTime = new SparseLongArray();
+ private SparseBooleanArray mPendingGone = new SparseBooleanArray();
+
+ private ArrayMap<UidStateChangedCallback, Executor>
+ mUidStateChangedCallbacks = new ArrayMap<>();
+
+ private final EventLog mEventLog;
+
+ @VisibleForTesting
+ interface DelayableExecutor extends Executor {
+
+ void execute(Runnable runnable);
+
+ void executeDelayed(Runnable runnable, long delay);
+ }
+
+ AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal,
+ Handler handler, Executor lockingExecutor, Clock clock,
+ AppOpsService.Constants constants) {
+
+ this(activityManagerInternal, new DelayableExecutor() {
+ @Override
+ public void execute(Runnable runnable) {
+ handler.post(() -> lockingExecutor.execute(runnable));
+ }
+
+ @Override
+ public void executeDelayed(Runnable runnable, long delay) {
+ handler.postDelayed(() -> lockingExecutor.execute(runnable), delay);
+ }
+ }, clock, constants, handler.getLooper().getThread());
+ }
+
+ @VisibleForTesting
+ AppOpsUidStateTrackerImpl(ActivityManagerInternal activityManagerInternal,
+ DelayableExecutor executor, Clock clock, AppOpsService.Constants constants,
+ Thread executorThread) {
+ mActivityManagerInternal = activityManagerInternal;
+ mExecutor = executor;
+ mClock = clock;
+ mConstants = constants;
+
+ mEventLog = new EventLog(executor, executorThread);
+ }
+
+ @Override
+ public int getUidState(int uid) {
+ return getUidStateLocked(uid);
+ }
+
+ private int getUidStateLocked(int uid) {
+ updateUidPendingStateIfNeeded(uid);
+ return mUidStates.get(uid, MIN_PRIORITY_UID_STATE);
+ }
+
+ @Override
+ public int evalMode(int uid, int code, int mode) {
+ if (mode != AppOpsManager.MODE_FOREGROUND) {
+ return mode;
+ }
+
+ int uidStateValue;
+ int capability;
+ boolean visibleAppWidget;
+ boolean pendingTop;
+ boolean tempAllowlist;
+ uidStateValue = getUidState(uid);
+ capability = getUidCapability(uid);
+ visibleAppWidget = getUidVisibleAppWidget(uid);
+ pendingTop = mActivityManagerInternal.isPendingTopUid(uid);
+ tempAllowlist = mActivityManagerInternal.isTempAllowlistedForFgsWhileInUse(uid);
+
+ int result = evalMode(uidStateValue, code, mode, capability, visibleAppWidget, pendingTop,
+ tempAllowlist);
+ mEventLog.logEvalForegroundMode(uid, uidStateValue, capability, code, result);
+ return result;
+ }
+
+ private static int evalMode(int uidState, int code, int mode, int capability,
+ boolean appWidgetVisible, boolean pendingTop, boolean tempAllowlist) {
+ if (mode != AppOpsManager.MODE_FOREGROUND) {
+ return mode;
+ }
+
+ if (appWidgetVisible || pendingTop || tempAllowlist) {
+ return MODE_ALLOWED;
+ }
+
+ switch (code) {
+ case AppOpsManager.OP_FINE_LOCATION:
+ case AppOpsManager.OP_COARSE_LOCATION:
+ case AppOpsManager.OP_MONITOR_LOCATION:
+ case AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION:
+ if ((capability & PROCESS_CAPABILITY_FOREGROUND_LOCATION) == 0) {
+ return MODE_IGNORED;
+ } else {
+ return MODE_ALLOWED;
+ }
+ case OP_CAMERA:
+ if ((capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) == 0) {
+ return MODE_IGNORED;
+ } else {
+ return MODE_ALLOWED;
+ }
+ case OP_RECORD_AUDIO:
+ if ((capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) == 0) {
+ return MODE_IGNORED;
+ } else {
+ return MODE_ALLOWED;
+ }
+ }
+
+ if (uidState > AppOpsManager.resolveFirstUnrestrictedUidState(code)) {
+ return MODE_IGNORED;
+ }
+
+ return MODE_ALLOWED;
+ }
+
+ @Override
+ public void addUidStateChangedCallback(Executor executor, UidStateChangedCallback callback) {
+ if (mUidStateChangedCallbacks.containsKey(callback)) {
+ throw new IllegalStateException("Callback is already registered.");
+ }
+
+ mUidStateChangedCallbacks.put(callback, executor);
+ }
+
+ @Override
+ public void removeUidStateChangedCallback(UidStateChangedCallback callback) {
+ if (!mUidStateChangedCallbacks.containsKey(callback)) {
+ throw new IllegalStateException("Callback is not registered.");
+ }
+ mUidStateChangedCallbacks.remove(callback);
+ }
+
+ @Override
+ public void updateAppWidgetVisibility(SparseArray<String> uidPackageNames, boolean visible) {
+ int numUids = uidPackageNames.size();
+ for (int i = 0; i < numUids; i++) {
+ int uid = uidPackageNames.keyAt(i);
+ mPendingVisibleAppWidget.put(uid, visible);
+
+ commitUidPendingState(uid);
+ }
+ }
+
+ @Override
+ public void updateUidProcState(int uid, int procState, int capability) {
+ mEventLog.logUpdateUidProcState(uid, procState, capability);
+
+ int uidState = processStateToUidState(procState);
+
+ int prevUidState = mUidStates.get(uid, AppOpsManager.MIN_PRIORITY_UID_STATE);
+ int prevCapability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
+ int pendingUidState = mPendingUidStates.get(uid, MIN_PRIORITY_UID_STATE);
+ int pendingCapability = mPendingCapability.get(uid, PROCESS_CAPABILITY_NONE);
+ long pendingStateCommitTime = mPendingCommitTime.get(uid, 0);
+ if ((pendingStateCommitTime == 0
+ && (uidState != prevUidState || capability != prevCapability))
+ || (pendingStateCommitTime != 0
+ && (uidState != pendingUidState || capability != pendingCapability))) {
+ mPendingUidStates.put(uid, uidState);
+ mPendingCapability.put(uid, capability);
+
+ if (procState == PROCESS_STATE_NONEXISTENT) {
+ mPendingGone.put(uid, true);
+ commitUidPendingState(uid);
+ } else if (uidState < prevUidState
+ || (uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+ && prevUidState > UID_STATE_MAX_LAST_NON_RESTRICTED)) {
+ // We are moving to a more important state, or the new state may be in the
+ // foreground and the old state is in the background, then always do it
+ // immediately.
+ commitUidPendingState(uid);
+ } else if (uidState == prevUidState && capability != prevCapability) {
+ // No change on process state, but process capability has changed.
+ commitUidPendingState(uid);
+ } else if (uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED) {
+ // We are moving to a less important state, but it doesn't cross the restriction
+ // threshold.
+ commitUidPendingState(uid);
+ } else if (pendingStateCommitTime == 0) {
+ // We are moving to a less important state for the first time,
+ // delay the application for a bit.
+ final long settleTime;
+ if (prevUidState <= UID_STATE_TOP) {
+ settleTime = mConstants.TOP_STATE_SETTLE_TIME;
+ } else if (prevUidState <= UID_STATE_FOREGROUND_SERVICE) {
+ settleTime = mConstants.FG_SERVICE_STATE_SETTLE_TIME;
+ } else {
+ settleTime = mConstants.BG_STATE_SETTLE_TIME;
+ }
+ final long commitTime = mClock.elapsedRealtime() + settleTime;
+ mPendingCommitTime.put(uid, commitTime);
+
+ mExecutor.executeDelayed(PooledLambda.obtainRunnable(
+ AppOpsUidStateTrackerImpl::updateUidPendingStateIfNeeded, this,
+ uid), settleTime + 1);
+ }
+ }
+ }
+
+ @Override
+ public void dumpUidState(PrintWriter pw, int uid, long nowElapsed) {
+ int state = mUidStates.get(uid, MIN_PRIORITY_UID_STATE);
+ // if no pendingState set to state to suppress output
+ int pendingState = mPendingUidStates.get(uid, state);
+ pw.print(" state=");
+ pw.println(AppOpsManager.getUidStateName(state));
+ if (state != pendingState) {
+ pw.print(" pendingState=");
+ pw.println(AppOpsManager.getUidStateName(pendingState));
+ }
+ int capability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
+ // if no pendingCapability set to capability to suppress output
+ int pendingCapability = mPendingCapability.get(uid, capability);
+ pw.print(" capability=");
+ ActivityManager.printCapabilitiesFull(pw, capability);
+ pw.println();
+ if (capability != pendingCapability) {
+ pw.print(" pendingCapability=");
+ ActivityManager.printCapabilitiesFull(pw, pendingCapability);
+ pw.println();
+ }
+ boolean appWidgetVisible = mVisibleAppWidget.get(uid, false);
+ // if no pendingAppWidgetVisible set to appWidgetVisible to suppress output
+ boolean pendingAppWidgetVisible = mPendingVisibleAppWidget.get(uid, appWidgetVisible);
+ pw.print(" appWidgetVisible=");
+ pw.println(appWidgetVisible);
+ if (appWidgetVisible != pendingAppWidgetVisible) {
+ pw.print(" pendingAppWidgetVisible=");
+ pw.println(pendingAppWidgetVisible);
+ }
+ long pendingStateCommitTime = mPendingCommitTime.get(uid, 0);
+ if (pendingStateCommitTime != 0) {
+ pw.print(" pendingStateCommitTime=");
+ TimeUtils.formatDuration(pendingStateCommitTime, nowElapsed, pw);
+ pw.println();
+ }
+ }
+
+ @Override
+ public void dumpEvents(PrintWriter pw) {
+ mEventLog.dumpEvents(pw);
+ }
+
+ private void updateUidPendingStateIfNeeded(int uid) {
+ updateUidPendingStateIfNeededLocked(uid);
+ }
+
+ private void updateUidPendingStateIfNeededLocked(int uid) {
+ long pendingCommitTime = mPendingCommitTime.get(uid, 0);
+ if (pendingCommitTime != 0) {
+ long currentTime = mClock.elapsedRealtime();
+ if (currentTime < mPendingCommitTime.get(uid)) {
+ return;
+ }
+ commitUidPendingState(uid);
+ }
+ }
+
+ private void commitUidPendingState(int uid) {
+ int pendingUidState = mPendingUidStates.get(uid, MIN_PRIORITY_UID_STATE);
+ int pendingCapability = mPendingCapability.get(uid, PROCESS_CAPABILITY_NONE);
+ boolean pendingVisibleAppWidget = mPendingVisibleAppWidget.get(uid, false);
+
+ int uidState = mUidStates.get(uid, MIN_PRIORITY_UID_STATE);
+ int capability = mCapability.get(uid, PROCESS_CAPABILITY_NONE);
+ boolean visibleAppWidget = mVisibleAppWidget.get(uid, false);
+
+ if (uidState != pendingUidState
+ || capability != pendingCapability
+ || visibleAppWidget != pendingVisibleAppWidget) {
+ boolean foregroundChange = uidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+ != pendingUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+ || capability != pendingCapability
+ || visibleAppWidget != pendingVisibleAppWidget;
+
+ if (foregroundChange) {
+ // To save on memory usage, log only interesting changes.
+ mEventLog.logCommitUidState(uid, pendingUidState, pendingCapability,
+ pendingVisibleAppWidget);
+ }
+
+ for (int i = 0; i < mUidStateChangedCallbacks.size(); i++) {
+ UidStateChangedCallback cb = mUidStateChangedCallbacks.keyAt(i);
+ Executor executor = mUidStateChangedCallbacks.valueAt(i);
+
+ executor.execute(PooledLambda.obtainRunnable(
+ UidStateChangedCallback::onUidStateChanged, cb, uid, pendingUidState,
+ foregroundChange));
+ }
+ }
+
+ if (mPendingGone.get(uid, false)) {
+ mUidStates.delete(uid);
+ mCapability.delete(uid);
+ mVisibleAppWidget.delete(uid);
+ mPendingGone.delete(uid);
+ } else {
+ mUidStates.put(uid, pendingUidState);
+ mCapability.put(uid, pendingCapability);
+ mVisibleAppWidget.put(uid, pendingVisibleAppWidget);
+ }
+
+ mPendingUidStates.delete(uid);
+ mPendingCapability.delete(uid);
+ mPendingVisibleAppWidget.delete(uid);
+ mPendingCommitTime.delete(uid);
+ }
+
+ private @ProcessCapability int getUidCapability(int uid) {
+ return mCapability.get(uid, ActivityManager.PROCESS_CAPABILITY_NONE);
+ }
+
+ private boolean getUidVisibleAppWidget(int uid) {
+ return mVisibleAppWidget.get(uid, false);
+ }
+
+ private static class EventLog {
+
+ // These seems a bit too verbose and not as useful, turning off for now.
+ // DCE should be able to remove most associated code.
+ // Memory usage: 16 * size bytes
+ private static final int UPDATE_UID_PROC_STATE_LOG_MAX_SIZE = 0;
+ // Memory usage: 20 * size bytes
+ private static final int COMMIT_UID_STATE_LOG_MAX_SIZE = 200;
+ // Memory usage: 24 * size bytes
+ private static final int EVAL_FOREGROUND_MODE_MAX_SIZE = 200;
+
+ private final DelayableExecutor mExecutor;
+ private final Thread mExecutorThread;
+
+ private int[][] mUpdateUidProcStateLog = new int[UPDATE_UID_PROC_STATE_LOG_MAX_SIZE][3];
+ private long[] mUpdateUidProcStateLogTimestamps =
+ new long[UPDATE_UID_PROC_STATE_LOG_MAX_SIZE];
+ private int mUpdateUidProcStateLogSize = 0;
+ private int mUpdateUidProcStateLogHead = 0;
+
+ private int[][] mCommitUidStateLog = new int[COMMIT_UID_STATE_LOG_MAX_SIZE][4];
+ private long[] mCommitUidStateLogTimestamps = new long[COMMIT_UID_STATE_LOG_MAX_SIZE];
+ private int mCommitUidStateLogSize = 0;
+ private int mCommitUidStateLogHead = 0;
+
+ private int[][] mEvalForegroundModeLog = new int[EVAL_FOREGROUND_MODE_MAX_SIZE][5];
+ private long[] mEvalForegroundModeLogTimestamps = new long[EVAL_FOREGROUND_MODE_MAX_SIZE];
+ private int mEvalForegroundModeLogSize = 0;
+ private int mEvalForegroundModeLogHead = 0;
+
+ EventLog(DelayableExecutor executor, Thread executorThread) {
+ mExecutor = executor;
+ mExecutorThread = executorThread;
+ }
+
+ void logUpdateUidProcState(int uid, int procState, int capability) {
+ if (UPDATE_UID_PROC_STATE_LOG_MAX_SIZE == 0) {
+ return;
+ }
+ mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logUpdateUidProcStateAsync,
+ this, System.currentTimeMillis(), uid, procState, capability));
+ }
+
+ void logUpdateUidProcStateAsync(long timestamp, int uid, int procState, int capability) {
+ int idx = (mUpdateUidProcStateLogHead + mUpdateUidProcStateLogSize)
+ % UPDATE_UID_PROC_STATE_LOG_MAX_SIZE;
+ if (mUpdateUidProcStateLogSize == UPDATE_UID_PROC_STATE_LOG_MAX_SIZE) {
+ mUpdateUidProcStateLogHead =
+ (mUpdateUidProcStateLogHead + 1) % UPDATE_UID_PROC_STATE_LOG_MAX_SIZE;
+ } else {
+ mUpdateUidProcStateLogSize++;
+ }
+
+ mUpdateUidProcStateLog[idx][0] = uid;
+ mUpdateUidProcStateLog[idx][1] = procState;
+ mUpdateUidProcStateLog[idx][2] = capability;
+ mUpdateUidProcStateLogTimestamps[idx] = timestamp;
+ }
+
+ void logCommitUidState(int uid, int uidState, int capability, boolean visible) {
+ if (COMMIT_UID_STATE_LOG_MAX_SIZE == 0) {
+ return;
+ }
+ mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logCommitUidStateAsync,
+ this, System.currentTimeMillis(), uid, uidState, capability, visible));
+ }
+
+ void logCommitUidStateAsync(long timestamp, int uid, int uidState, int capability,
+ boolean visible) {
+ int idx = (mCommitUidStateLogHead + mCommitUidStateLogSize)
+ % COMMIT_UID_STATE_LOG_MAX_SIZE;
+ if (mCommitUidStateLogSize == COMMIT_UID_STATE_LOG_MAX_SIZE) {
+ mCommitUidStateLogHead =
+ (mCommitUidStateLogHead + 1) % COMMIT_UID_STATE_LOG_MAX_SIZE;
+ } else {
+ mCommitUidStateLogSize++;
+ }
+
+ mCommitUidStateLog[idx][0] = uid;
+ mCommitUidStateLog[idx][1] = uidState;
+ mCommitUidStateLog[idx][2] = capability;
+ mCommitUidStateLog[idx][3] = visible ? 1 : 0;
+ mCommitUidStateLogTimestamps[idx] = timestamp;
+ }
+
+ void logEvalForegroundMode(int uid, int uidState, int capability, int code, int result) {
+ if (EVAL_FOREGROUND_MODE_MAX_SIZE == 0) {
+ return;
+ }
+ mExecutor.execute(PooledLambda.obtainRunnable(EventLog::logEvalForegroundModeAsync,
+ this, System.currentTimeMillis(), uid, uidState, capability, code, result));
+ }
+
+ void logEvalForegroundModeAsync(long timestamp, int uid, int uidState, int capability,
+ int code, int result) {
+ int idx = (mEvalForegroundModeLogHead + mEvalForegroundModeLogSize)
+ % EVAL_FOREGROUND_MODE_MAX_SIZE;
+ if (mEvalForegroundModeLogSize == EVAL_FOREGROUND_MODE_MAX_SIZE) {
+ mEvalForegroundModeLogHead =
+ (mEvalForegroundModeLogHead + 1) % EVAL_FOREGROUND_MODE_MAX_SIZE;
+ } else {
+ mEvalForegroundModeLogSize++;
+ }
+
+ mEvalForegroundModeLog[idx][0] = uid;
+ mEvalForegroundModeLog[idx][1] = uidState;
+ mEvalForegroundModeLog[idx][2] = capability;
+ mEvalForegroundModeLog[idx][3] = code;
+ mEvalForegroundModeLog[idx][4] = result;
+ mEvalForegroundModeLogTimestamps[idx] = timestamp;
+ }
+
+ void dumpEvents(PrintWriter pw) {
+ int updateIdx = 0;
+ int commitIdx = 0;
+ int evalIdx = 0;
+
+ while (updateIdx < mUpdateUidProcStateLogSize
+ || commitIdx < mCommitUidStateLogSize
+ || evalIdx < mEvalForegroundModeLogSize) {
+ int updatePtr = 0;
+ int commitPtr = 0;
+ int evalPtr = 0;
+ if (UPDATE_UID_PROC_STATE_LOG_MAX_SIZE != 0) {
+ updatePtr = (mUpdateUidProcStateLogHead + updateIdx)
+ % UPDATE_UID_PROC_STATE_LOG_MAX_SIZE;
+ }
+ if (COMMIT_UID_STATE_LOG_MAX_SIZE != 0) {
+ commitPtr = (mCommitUidStateLogHead + commitIdx)
+ % COMMIT_UID_STATE_LOG_MAX_SIZE;
+ }
+ if (EVAL_FOREGROUND_MODE_MAX_SIZE != 0) {
+ evalPtr = (mEvalForegroundModeLogHead + evalIdx)
+ % EVAL_FOREGROUND_MODE_MAX_SIZE;
+ }
+
+ long aTimestamp = updateIdx < mUpdateUidProcStateLogSize
+ ? mUpdateUidProcStateLogTimestamps[updatePtr] : Long.MAX_VALUE;
+ long bTimestamp = commitIdx < mCommitUidStateLogSize
+ ? mCommitUidStateLogTimestamps[commitPtr] : Long.MAX_VALUE;
+ long cTimestamp = evalIdx < mEvalForegroundModeLogSize
+ ? mEvalForegroundModeLogTimestamps[evalPtr] : Long.MAX_VALUE;
+
+ if (aTimestamp <= bTimestamp && aTimestamp <= cTimestamp) {
+ dumpUpdateUidProcState(pw, updatePtr);
+ updateIdx++;
+ } else if (bTimestamp <= cTimestamp) {
+ dumpCommitUidState(pw, commitPtr);
+ commitIdx++;
+ } else {
+ dumpEvalForegroundMode(pw, evalPtr);
+ evalIdx++;
+ }
+ }
+ }
+
+ void dumpUpdateUidProcState(PrintWriter pw, int idx) {
+ long timestamp = mUpdateUidProcStateLogTimestamps[idx];
+ int uid = mUpdateUidProcStateLog[idx][0];
+ int procState = mUpdateUidProcStateLog[idx][1];
+ int capability = mUpdateUidProcStateLog[idx][2];
+
+ TimeUtils.dumpTime(pw, timestamp);
+
+ pw.print(" UPDATE_UID_PROC_STATE");
+
+ pw.print(" uid=");
+ pw.print(String.format("%-8d", uid));
+
+ pw.print(" procState=");
+ pw.print(String.format("%-30s", ActivityManager.procStateToString(procState)));
+
+ pw.print(" capability=");
+ pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
+
+ pw.println();
+ }
+
+ void dumpCommitUidState(PrintWriter pw, int idx) {
+ long timestamp = mCommitUidStateLogTimestamps[idx];
+ int uid = mCommitUidStateLog[idx][0];
+ int uidState = mCommitUidStateLog[idx][1];
+ int capability = mCommitUidStateLog[idx][2];
+ boolean visibleAppWidget = mCommitUidStateLog[idx][3] != 0;
+
+ TimeUtils.dumpTime(pw, timestamp);
+
+ pw.print(" COMMIT_UID_STATE ");
+
+ pw.print(" uid=");
+ pw.print(String.format("%-8d", uid));
+
+ pw.print(" uidState=");
+ pw.print(String.format("%-30s", AppOpsManager.uidStateToString(uidState)));
+
+ pw.print(" capability=");
+ pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
+
+ pw.print(" visibleAppWidget=");
+ pw.print(visibleAppWidget);
+
+ pw.println();
+ }
+
+ void dumpEvalForegroundMode(PrintWriter pw, int idx) {
+ long timestamp = mEvalForegroundModeLogTimestamps[idx];
+ int uid = mEvalForegroundModeLog[idx][0];
+ int uidState = mEvalForegroundModeLog[idx][1];
+ int capability = mEvalForegroundModeLog[idx][2];
+ int code = mEvalForegroundModeLog[idx][3];
+ int result = mEvalForegroundModeLog[idx][4];
+
+ TimeUtils.dumpTime(pw, timestamp);
+
+ pw.print(" EVAL_FOREGROUND_MODE ");
+
+ pw.print(" uid=");
+ pw.print(String.format("%-8d", uid));
+
+ pw.print(" uidState=");
+ pw.print(String.format("%-30s", AppOpsManager.uidStateToString(uidState)));
+
+ pw.print(" capability=");
+ pw.print(ActivityManager.getCapabilitiesSummary(capability) + " ");
+
+ pw.print(" code=");
+ pw.print(String.format("%-20s", AppOpsManager.opToName(code)));
+
+ pw.print(" result=");
+ pw.print(AppOpsManager.modeToName(result));
+
+ pw.println();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
new file mode 100644
index 000000000000..dcc36bcf6149
--- /dev/null
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -0,0 +1,870 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_RESUMED;
+import static android.app.AppOpsManager.makeKey;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+import android.util.Pools;
+import android.util.Slog;
+
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+final class AttributedOp {
+ private final @NonNull AppOpsService mAppOpsService;
+ public final @Nullable String tag;
+ public final @NonNull AppOpsService.Op parent;
+
+ /**
+ * Last successful accesses (noteOp + finished startOp) for each uidState/opFlag combination
+ *
+ * <p>Key is {@link AppOpsManager#makeKey}
+ */
+ // TODO(b/248108338)
+ // @GuardedBy("mAppOpsService")
+ private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> mAccessEvents;
+
+ /**
+ * Last rejected accesses for each uidState/opFlag combination
+ *
+ * <p>Key is {@link AppOpsManager#makeKey}
+ */
+ // TODO(b/248108338)
+ // @GuardedBy("mAppOpsService")
+ private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> mRejectEvents;
+
+ /**
+ * Currently in progress startOp events
+ *
+ * <p>Key is clientId
+ */
+ // TODO(b/248108338)
+ // @GuardedBy("mAppOpsService")
+ @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mInProgressEvents;
+
+ /**
+ * Currently paused startOp events
+ *
+ * <p>Key is clientId
+ */
+ // TODO(b/248108338)
+ // @GuardedBy("mAppOpsService")
+ @Nullable ArrayMap<IBinder, InProgressStartOpEvent> mPausedInProgressEvents;
+
+ AttributedOp(@NonNull AppOpsService appOpsService, @Nullable String tag,
+ @NonNull AppOpsService.Op parent) {
+ mAppOpsService = appOpsService;
+ this.tag = tag;
+ this.parent = parent;
+ }
+
+ /**
+ * Update state when noteOp was rejected or startOp->finishOp event finished
+ *
+ * @param proxyUid The uid of the proxy
+ * @param proxyPackageName The package name of the proxy
+ * @param proxyAttributionTag the attributionTag in the proxies package
+ * @param uidState UID state of the app noteOp/startOp was called for
+ * @param flags OpFlags of the call
+ */
+ public void accessed(int proxyUid, @Nullable String proxyPackageName,
+ @Nullable String proxyAttributionTag, @AppOpsManager.UidState int uidState,
+ @AppOpsManager.OpFlags int flags) {
+ long accessTime = System.currentTimeMillis();
+ accessed(accessTime, -1, proxyUid, proxyPackageName,
+ proxyAttributionTag, uidState, flags);
+
+ mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
+ parent.packageName, tag, uidState, flags, accessTime,
+ AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE);
+ }
+
+ /**
+ * Add an access that was previously collected.
+ *
+ * @param noteTime The time of the event
+ * @param duration The duration of the event
+ * @param proxyUid The uid of the proxy
+ * @param proxyPackageName The package name of the proxy
+ * @param proxyAttributionTag the attributionTag in the proxies package
+ * @param uidState UID state of the app noteOp/startOp was called for
+ * @param flags OpFlags of the call
+ */
+ @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
+ public void accessed(long noteTime, long duration, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) {
+ long key = makeKey(uidState, flags);
+
+ if (mAccessEvents == null) {
+ mAccessEvents = new LongSparseArray<>(1);
+ }
+
+ AppOpsManager.OpEventProxyInfo proxyInfo = null;
+ if (proxyUid != Process.INVALID_UID) {
+ proxyInfo = mAppOpsService.mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
+ proxyAttributionTag);
+ }
+
+ AppOpsManager.NoteOpEvent existingEvent = mAccessEvents.get(key);
+ if (existingEvent != null) {
+ existingEvent.reinit(noteTime, duration, proxyInfo,
+ mAppOpsService.mOpEventProxyInfoPool);
+ } else {
+ mAccessEvents.put(key, new AppOpsManager.NoteOpEvent(noteTime, duration, proxyInfo));
+ }
+ }
+
+ /**
+ * Update state when noteOp/startOp was rejected.
+ *
+ * @param uidState UID state of the app noteOp is called for
+ * @param flags OpFlags of the call
+ */
+ public void rejected(@AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags) {
+ rejected(System.currentTimeMillis(), uidState, flags);
+
+ mAppOpsService.mHistoricalRegistry.incrementOpRejected(parent.op, parent.uid,
+ parent.packageName, tag, uidState, flags);
+ }
+
+ /**
+ * Add an rejection that was previously collected
+ *
+ * @param noteTime The time of the event
+ * @param uidState UID state of the app noteOp/startOp was called for
+ * @param flags OpFlags of the call
+ */
+ @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
+ public void rejected(long noteTime, @AppOpsManager.UidState int uidState,
+ @AppOpsManager.OpFlags int flags) {
+ long key = makeKey(uidState, flags);
+
+ if (mRejectEvents == null) {
+ mRejectEvents = new LongSparseArray<>(1);
+ }
+
+ // We do not collect proxy information for rejections yet
+ AppOpsManager.NoteOpEvent existingEvent = mRejectEvents.get(key);
+ if (existingEvent != null) {
+ existingEvent.reinit(noteTime, -1, null, mAppOpsService.mOpEventProxyInfoPool);
+ } else {
+ mRejectEvents.put(key, new AppOpsManager.NoteOpEvent(noteTime, -1, null));
+ }
+ }
+
+ /**
+ * Update state when start was called
+ *
+ * @param clientId Id of the startOp caller
+ * @param proxyUid The UID of the proxy app
+ * @param proxyPackageName The package name of the proxy app
+ * @param proxyAttributionTag The attribution tag of the proxy app
+ * @param uidState UID state of the app startOp is called for
+ * @param flags The proxy flags
+ * @param attributionFlags The attribution flags associated with this operation.
+ * @param attributionChainId The if of the attribution chain this operations is a part of.
+ */
+ public void started(@NonNull IBinder clientId, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
+ @AppOpsManager.AttributionFlags
+ int attributionFlags, int attributionChainId) throws RemoteException {
+ started(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
+ uidState, flags, /*triggerCallbackIfNeeded*/ true, attributionFlags,
+ attributionChainId);
+ }
+
+ private void started(@NonNull IBinder clientId, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
+ boolean triggerCallbackIfNeeded, @AppOpsManager.AttributionFlags int attributionFlags,
+ int attributionChainId) throws RemoteException {
+ startedOrPaused(clientId, proxyUid, proxyPackageName,
+ proxyAttributionTag, uidState, flags, triggerCallbackIfNeeded,
+ /*triggerCallbackIfNeeded*/ true, attributionFlags, attributionChainId);
+ }
+
+ @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
+ private void startedOrPaused(@NonNull IBinder clientId, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
+ boolean triggerCallbackIfNeeded, boolean isStarted, @AppOpsManager.AttributionFlags
+ int attributionFlags, int attributionChainId) throws RemoteException {
+ if (triggerCallbackIfNeeded && !parent.isRunning() && isStarted) {
+ mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, tag, true, attributionFlags, attributionChainId);
+ }
+
+ if (isStarted && mInProgressEvents == null) {
+ mInProgressEvents = new ArrayMap<>(1);
+ } else if (!isStarted && mPausedInProgressEvents == null) {
+ mPausedInProgressEvents = new ArrayMap<>(1);
+ }
+ ArrayMap<IBinder, InProgressStartOpEvent> events = isStarted
+ ? mInProgressEvents : mPausedInProgressEvents;
+
+ long startTime = System.currentTimeMillis();
+ InProgressStartOpEvent event = events.get(clientId);
+ if (event == null) {
+ event = mAppOpsService.mInProgressStartOpEventPool.acquire(startTime,
+ SystemClock.elapsedRealtime(), clientId, tag,
+ PooledLambda.obtainRunnable(AppOpsService::onClientDeath, this, clientId),
+ proxyUid, proxyPackageName, proxyAttributionTag, uidState, flags,
+ attributionFlags, attributionChainId);
+ events.put(clientId, event);
+ } else {
+ if (uidState != event.getUidState()) {
+ onUidStateChanged(uidState);
+ }
+ }
+
+ event.mNumUnfinishedStarts++;
+
+ if (isStarted) {
+ mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
+ parent.packageName, tag, uidState, flags, startTime, attributionFlags,
+ attributionChainId);
+ }
+ }
+
+ /**
+ * Update state when finishOp was called. Will finish started ops, and delete paused ops.
+ *
+ * @param clientId Id of the finishOp caller
+ */
+ public void finished(@NonNull IBinder clientId) {
+ finished(clientId, true);
+ }
+
+ private void finished(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded) {
+ finishOrPause(clientId, triggerCallbackIfNeeded, false);
+ }
+
+ /**
+ * Update state when paused or finished is called. If pausing, it records the op as
+ * stopping in the HistoricalRegistry, but does not delete it.
+ */
+ @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
+ private void finishOrPause(@NonNull IBinder clientId, boolean triggerCallbackIfNeeded,
+ boolean isPausing) {
+ int indexOfToken = isRunning() ? mInProgressEvents.indexOfKey(clientId) : -1;
+ if (indexOfToken < 0) {
+ finishPossiblyPaused(clientId, isPausing);
+ return;
+ }
+
+ InProgressStartOpEvent event = mInProgressEvents.valueAt(indexOfToken);
+ if (!isPausing) {
+ event.mNumUnfinishedStarts--;
+ }
+ // If we are pausing, create a NoteOpEvent, but don't change the InProgress event
+ if (event.mNumUnfinishedStarts == 0 || isPausing) {
+ if (!isPausing) {
+ event.finish();
+ mInProgressEvents.removeAt(indexOfToken);
+ }
+
+ if (mAccessEvents == null) {
+ mAccessEvents = new LongSparseArray<>(1);
+ }
+
+ AppOpsManager.OpEventProxyInfo proxyCopy = event.getProxy() != null
+ ? new AppOpsManager.OpEventProxyInfo(event.getProxy()) : null;
+
+ long accessDurationMillis =
+ SystemClock.elapsedRealtime() - event.getStartElapsedTime();
+ AppOpsManager.NoteOpEvent finishedEvent = new AppOpsManager.NoteOpEvent(
+ event.getStartTime(),
+ accessDurationMillis, proxyCopy);
+ mAccessEvents.put(makeKey(event.getUidState(), event.getFlags()),
+ finishedEvent);
+
+ mAppOpsService.mHistoricalRegistry.increaseOpAccessDuration(parent.op, parent.uid,
+ parent.packageName, tag, event.getUidState(),
+ event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
+ event.getAttributionFlags(), event.getAttributionChainId());
+
+ if (!isPausing) {
+ mAppOpsService.mInProgressStartOpEventPool.release(event);
+ if (mInProgressEvents.isEmpty()) {
+ mInProgressEvents = null;
+
+ // TODO ntmyren: Also callback for single attribution tag activity changes
+ if (triggerCallbackIfNeeded && !parent.isRunning()) {
+ mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op,
+ parent.uid, parent.packageName, tag, false,
+ event.getAttributionFlags(), event.getAttributionChainId());
+ }
+ }
+ }
+ }
+ }
+
+ // Finish or pause (no-op) an already paused op
+ @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
+ private void finishPossiblyPaused(@NonNull IBinder clientId, boolean isPausing) {
+ if (!isPaused()) {
+ Slog.wtf(AppOpsService.TAG, "No ops running or paused");
+ return;
+ }
+
+ int indexOfToken = mPausedInProgressEvents.indexOfKey(clientId);
+ if (indexOfToken < 0) {
+ Slog.wtf(AppOpsService.TAG, "No op running or paused for the client");
+ return;
+ } else if (isPausing) {
+ // already paused
+ return;
+ }
+
+ // no need to record a paused event finishing.
+ InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(indexOfToken);
+ event.mNumUnfinishedStarts--;
+ if (event.mNumUnfinishedStarts == 0) {
+ mPausedInProgressEvents.removeAt(indexOfToken);
+ mAppOpsService.mInProgressStartOpEventPool.release(event);
+ if (mPausedInProgressEvents.isEmpty()) {
+ mPausedInProgressEvents = null;
+ }
+ }
+ }
+
+ /**
+ * Create an event that will be started, if the op is unpaused.
+ */
+ public void createPaused(@NonNull IBinder clientId, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
+ @AppOpsManager.AttributionFlags
+ int attributionFlags, int attributionChainId) throws RemoteException {
+ startedOrPaused(clientId, proxyUid, proxyPackageName, proxyAttributionTag,
+ uidState, flags, true, false, attributionFlags, attributionChainId);
+ }
+
+ /**
+ * Pause all currently started ops. This will create a HistoricalRegistry
+ */
+ public void pause() {
+ if (!isRunning()) {
+ return;
+ }
+
+ if (mPausedInProgressEvents == null) {
+ mPausedInProgressEvents = new ArrayMap<>(1);
+ }
+
+ for (int i = 0; i < mInProgressEvents.size(); i++) {
+ InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
+ mPausedInProgressEvents.put(event.getClientId(), event);
+ finishOrPause(event.getClientId(), true, true);
+
+ mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, tag, false,
+ event.getAttributionFlags(), event.getAttributionChainId());
+ }
+ mInProgressEvents = null;
+ }
+
+ /**
+ * Unpause all currently paused ops. This will reinitialize their start and duration
+ * times, but keep all other values the same
+ */
+ public void resume() {
+ if (!isPaused()) {
+ return;
+ }
+
+ if (mInProgressEvents == null) {
+ mInProgressEvents = new ArrayMap<>(mPausedInProgressEvents.size());
+ }
+ boolean shouldSendActive = !mPausedInProgressEvents.isEmpty()
+ && mInProgressEvents.isEmpty();
+
+ long startTime = System.currentTimeMillis();
+ for (int i = 0; i < mPausedInProgressEvents.size(); i++) {
+ InProgressStartOpEvent event = mPausedInProgressEvents.valueAt(i);
+ mInProgressEvents.put(event.getClientId(), event);
+ event.setStartElapsedTime(SystemClock.elapsedRealtime());
+ event.setStartTime(startTime);
+ mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
+ parent.packageName, tag, event.getUidState(), event.getFlags(), startTime,
+ event.getAttributionFlags(), event.getAttributionChainId());
+ if (shouldSendActive) {
+ mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, tag, true, event.getAttributionFlags(),
+ event.getAttributionChainId());
+ }
+ // Note: this always sends MODE_ALLOWED, even if the mode is FOREGROUND
+ // TODO ntmyren: figure out how to get the real mode.
+ mAppOpsService.scheduleOpStartedIfNeededLocked(parent.op, parent.uid,
+ parent.packageName, tag, event.getFlags(), MODE_ALLOWED, START_TYPE_RESUMED,
+ event.getAttributionFlags(), event.getAttributionChainId());
+ }
+ mPausedInProgressEvents = null;
+ }
+
+ /**
+ * Called in the case the client dies without calling finish first
+ *
+ * @param clientId The client that died
+ */
+ void onClientDeath(@NonNull IBinder clientId) {
+ synchronized (mAppOpsService) {
+ if (!isPaused() && !isRunning()) {
+ return;
+ }
+
+ ArrayMap<IBinder, InProgressStartOpEvent> events = isPaused()
+ ? mPausedInProgressEvents : mInProgressEvents;
+ InProgressStartOpEvent deadEvent = events.get(clientId);
+ if (deadEvent != null) {
+ deadEvent.mNumUnfinishedStarts = 1;
+ }
+
+ finished(clientId);
+ }
+ }
+
+ /**
+ * Notify that the state of the uid changed
+ *
+ * @param newState The new state
+ */
+ public void onUidStateChanged(@AppOpsManager.UidState int newState) {
+ if (!isPaused() && !isRunning()) {
+ return;
+ }
+
+ boolean isRunning = isRunning();
+ ArrayMap<IBinder, InProgressStartOpEvent> events =
+ isRunning ? mInProgressEvents : mPausedInProgressEvents;
+
+ int numInProgressEvents = events.size();
+ List<IBinder> binders = new ArrayList<>(events.keySet());
+ for (int i = 0; i < numInProgressEvents; i++) {
+ InProgressStartOpEvent event = events.get(binders.get(i));
+
+ if (event != null && event.getUidState() != newState) {
+ try {
+ // Remove all but one unfinished start count and then call finished() to
+ // remove start event object
+ int numPreviousUnfinishedStarts = event.mNumUnfinishedStarts;
+ event.mNumUnfinishedStarts = 1;
+ AppOpsManager.OpEventProxyInfo proxy = event.getProxy();
+
+ finished(event.getClientId(), false);
+
+ // Call started() to add a new start event object and then add the
+ // previously removed unfinished start counts back
+ if (proxy != null) {
+ startedOrPaused(event.getClientId(), proxy.getUid(),
+ proxy.getPackageName(), proxy.getAttributionTag(), newState,
+ event.getFlags(), false, isRunning,
+ event.getAttributionFlags(), event.getAttributionChainId());
+ } else {
+ startedOrPaused(event.getClientId(), Process.INVALID_UID, null, null,
+ newState, event.getFlags(), false, isRunning,
+ event.getAttributionFlags(), event.getAttributionChainId());
+ }
+
+ events = isRunning ? mInProgressEvents : mPausedInProgressEvents;
+ InProgressStartOpEvent newEvent = events.get(binders.get(i));
+ if (newEvent != null) {
+ newEvent.mNumUnfinishedStarts += numPreviousUnfinishedStarts - 1;
+ }
+ } catch (RemoteException e) {
+ if (AppOpsService.DEBUG) {
+ Slog.e(AppOpsService.TAG,
+ "Cannot switch to new uidState " + newState);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Combine {@code a} and {@code b} and return the result. The result might be {@code a}
+ * or {@code b}. If there is an event for the same key in both the later event is retained.
+ */
+ private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> add(
+ @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> a,
+ @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> b) {
+ if (a == null) {
+ return b;
+ }
+
+ if (b == null) {
+ return a;
+ }
+
+ int numEventsToAdd = b.size();
+ for (int i = 0; i < numEventsToAdd; i++) {
+ long keyOfEventToAdd = b.keyAt(i);
+ AppOpsManager.NoteOpEvent bEvent = b.valueAt(i);
+ AppOpsManager.NoteOpEvent aEvent = a.get(keyOfEventToAdd);
+
+ if (aEvent == null || bEvent.getNoteTime() > aEvent.getNoteTime()) {
+ a.put(keyOfEventToAdd, bEvent);
+ }
+ }
+
+ return a;
+ }
+
+ /**
+ * Add all data from the {@code opToAdd} to this op.
+ *
+ * <p>If there is an event for the same key in both the later event is retained.
+ * <p>{@code opToAdd} should not be used after this method is called.
+ *
+ * @param opToAdd The op to add
+ */
+ @SuppressWarnings("GuardedBy") // Lock is held on mAppOpsService
+ public void add(@NonNull AttributedOp opToAdd) {
+ if (opToAdd.isRunning() || opToAdd.isPaused()) {
+ ArrayMap<IBinder, InProgressStartOpEvent> ignoredEvents =
+ opToAdd.isRunning()
+ ? opToAdd.mInProgressEvents : opToAdd.mPausedInProgressEvents;
+ Slog.w(AppOpsService.TAG, "Ignoring " + ignoredEvents.size() + " app-ops, running: "
+ + opToAdd.isRunning());
+
+ int numInProgressEvents = ignoredEvents.size();
+ for (int i = 0; i < numInProgressEvents; i++) {
+ InProgressStartOpEvent event = ignoredEvents.valueAt(i);
+
+ event.finish();
+ mAppOpsService.mInProgressStartOpEventPool.release(event);
+ }
+ }
+
+ mAccessEvents = add(mAccessEvents, opToAdd.mAccessEvents);
+ mRejectEvents = add(mRejectEvents, opToAdd.mRejectEvents);
+ }
+
+ public boolean isRunning() {
+ return mInProgressEvents != null && !mInProgressEvents.isEmpty();
+ }
+
+ public boolean isPaused() {
+ return mPausedInProgressEvents != null && !mPausedInProgressEvents.isEmpty();
+ }
+
+ boolean hasAnyTime() {
+ return (mAccessEvents != null && mAccessEvents.size() > 0)
+ || (mRejectEvents != null && mRejectEvents.size() > 0);
+ }
+
+ /**
+ * Clone a {@link LongSparseArray} and clone all values.
+ */
+ private @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> deepClone(
+ @Nullable LongSparseArray<AppOpsManager.NoteOpEvent> original) {
+ if (original == null) {
+ return original;
+ }
+
+ int size = original.size();
+ LongSparseArray<AppOpsManager.NoteOpEvent> clone = new LongSparseArray<>(size);
+ for (int i = 0; i < size; i++) {
+ clone.put(original.keyAt(i), new AppOpsManager.NoteOpEvent(original.valueAt(i)));
+ }
+
+ return clone;
+ }
+
+ @NonNull AppOpsManager.AttributedOpEntry createAttributedOpEntryLocked() {
+ LongSparseArray<AppOpsManager.NoteOpEvent> accessEvents = deepClone(mAccessEvents);
+
+ // Add in progress events as access events
+ if (isRunning()) {
+ long now = SystemClock.elapsedRealtime();
+ int numInProgressEvents = mInProgressEvents.size();
+
+ if (accessEvents == null) {
+ accessEvents = new LongSparseArray<>(numInProgressEvents);
+ }
+
+ for (int i = 0; i < numInProgressEvents; i++) {
+ InProgressStartOpEvent event = mInProgressEvents.valueAt(i);
+
+ accessEvents.append(makeKey(event.getUidState(), event.getFlags()),
+ new AppOpsManager.NoteOpEvent(event.getStartTime(),
+ now - event.getStartElapsedTime(),
+ event.getProxy()));
+ }
+ }
+
+ LongSparseArray<AppOpsManager.NoteOpEvent> rejectEvents = deepClone(mRejectEvents);
+
+ return new AppOpsManager.AttributedOpEntry(parent.op, isRunning(), accessEvents,
+ rejectEvents);
+ }
+
+ /** A in progress startOp->finishOp event */
+ static final class InProgressStartOpEvent implements IBinder.DeathRecipient {
+ /** Wall clock time of startOp event (not monotonic) */
+ private long mStartTime;
+
+ /** Elapsed time since boot of startOp event */
+ private long mStartElapsedTime;
+
+ /** Id of the client that started the event */
+ private @NonNull IBinder mClientId;
+
+ /** The attribution tag for this operation */
+ private @Nullable String mAttributionTag;
+
+ /** To call when client dies */
+ private @NonNull Runnable mOnDeath;
+
+ /** uidstate used when calling startOp */
+ private @AppOpsManager.UidState int mUidState;
+
+ /** Proxy information of the startOp event */
+ private @Nullable AppOpsManager.OpEventProxyInfo mProxy;
+
+ /** Proxy flag information */
+ private @AppOpsManager.OpFlags int mFlags;
+
+ /** How many times the op was started but not finished yet */
+ int mNumUnfinishedStarts;
+
+ /** The attribution flags related to this event */
+ private @AppOpsManager.AttributionFlags int mAttributionFlags;
+
+ /** The id of the attribution chain this even is a part of */
+ private int mAttributionChainId;
+
+ /**
+ * Create a new {@link InProgressStartOpEvent}.
+ *
+ * @param startTime The time {@link #startOperation} was called
+ * @param startElapsedTime The elapsed time when {@link #startOperation} was called
+ * @param clientId The client id of the caller of {@link #startOperation}
+ * @param attributionTag The attribution tag for the operation.
+ * @param onDeath The code to execute on client death
+ * @param uidState The uidstate of the app {@link #startOperation} was called for
+ * @param attributionFlags the attribution flags for this operation.
+ * @param attributionChainId the unique id of the attribution chain this op is a part of.
+ * @param proxy The proxy information, if {@link #startProxyOperation} was
+ * called
+ * @param flags The trusted/nontrusted/self flags.
+ * @throws RemoteException If the client is dying
+ */
+ InProgressStartOpEvent(long startTime, long startElapsedTime,
+ @NonNull IBinder clientId, @Nullable String attributionTag,
+ @NonNull Runnable onDeath, @AppOpsManager.UidState int uidState,
+ @Nullable AppOpsManager.OpEventProxyInfo proxy, @AppOpsManager.OpFlags int flags,
+ @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId)
+ throws RemoteException {
+ mStartTime = startTime;
+ mStartElapsedTime = startElapsedTime;
+ mClientId = clientId;
+ mAttributionTag = attributionTag;
+ mOnDeath = onDeath;
+ mUidState = uidState;
+ mProxy = proxy;
+ mFlags = flags;
+ mAttributionFlags = attributionFlags;
+ mAttributionChainId = attributionChainId;
+
+ clientId.linkToDeath(this, 0);
+ }
+
+ /** Clean up event */
+ public void finish() {
+ try {
+ mClientId.unlinkToDeath(this, 0);
+ } catch (NoSuchElementException e) {
+ // Either not linked, or already unlinked. Either way, nothing to do.
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ mOnDeath.run();
+ }
+
+ /**
+ * Reinit existing object with new state.
+ *
+ * @param startTime The time {@link #startOperation} was called
+ * @param startElapsedTime The elapsed time when {@link #startOperation} was called
+ * @param clientId The client id of the caller of {@link #startOperation}
+ * @param attributionTag The attribution tag for this operation.
+ * @param onDeath The code to execute on client death
+ * @param uidState The uidstate of the app {@link #startOperation} was called for
+ * @param flags The flags relating to the proxy
+ * @param proxy The proxy information, if {@link #startProxyOperation}
+ * was called
+ * @param attributionFlags the attribution flags for this operation.
+ * @param attributionChainId the unique id of the attribution chain this op is a part of.
+ * @param proxyPool The pool to release
+ * previous {@link AppOpsManager.OpEventProxyInfo} to
+ * @throws RemoteException If the client is dying
+ */
+ public void reinit(long startTime, long startElapsedTime, @NonNull IBinder clientId,
+ @Nullable String attributionTag, @NonNull Runnable onDeath,
+ @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
+ @Nullable AppOpsManager.OpEventProxyInfo proxy,
+ @AppOpsManager.AttributionFlags int attributionFlags,
+ int attributionChainId,
+ @NonNull Pools.Pool<AppOpsManager.OpEventProxyInfo> proxyPool
+ ) throws RemoteException {
+ mStartTime = startTime;
+ mStartElapsedTime = startElapsedTime;
+ mClientId = clientId;
+ mAttributionTag = attributionTag;
+ mOnDeath = onDeath;
+ mUidState = uidState;
+ mFlags = flags;
+
+ if (mProxy != null) {
+ proxyPool.release(mProxy);
+ }
+ mProxy = proxy;
+ mAttributionFlags = attributionFlags;
+ mAttributionChainId = attributionChainId;
+
+ clientId.linkToDeath(this, 0);
+ }
+
+ /** @return Wall clock time of startOp event */
+ public long getStartTime() {
+ return mStartTime;
+ }
+
+ /** @return Elapsed time since boot of startOp event */
+ public long getStartElapsedTime() {
+ return mStartElapsedTime;
+ }
+
+ /** @return Id of the client that started the event */
+ public @NonNull IBinder getClientId() {
+ return mClientId;
+ }
+
+ /** @return uidstate used when calling startOp */
+ public @AppOpsManager.UidState int getUidState() {
+ return mUidState;
+ }
+
+ /** @return proxy tag for the access */
+ public @Nullable AppOpsManager.OpEventProxyInfo getProxy() {
+ return mProxy;
+ }
+
+ /** @return flags used for the access */
+ public @AppOpsManager.OpFlags int getFlags() {
+ return mFlags;
+ }
+
+ /** @return attributoin flags used for the access */
+ public @AppOpsManager.AttributionFlags int getAttributionFlags() {
+ return mAttributionFlags;
+ }
+
+ /** @return attribution chain id for the access */
+ public int getAttributionChainId() {
+ return mAttributionChainId;
+ }
+
+ public void setStartTime(long startTime) {
+ mStartTime = startTime;
+ }
+
+ public void setStartElapsedTime(long startElapsedTime) {
+ mStartElapsedTime = startElapsedTime;
+ }
+ }
+
+ /**
+ * An unsynchronized pool of {@link InProgressStartOpEvent} objects.
+ */
+ static class InProgressStartOpEventPool extends Pools.SimplePool<InProgressStartOpEvent> {
+ private OpEventProxyInfoPool mOpEventProxyInfoPool;
+
+ InProgressStartOpEventPool(OpEventProxyInfoPool opEventProxyInfoPool,
+ int maxUnusedPooledObjects) {
+ super(maxUnusedPooledObjects);
+ this.mOpEventProxyInfoPool = opEventProxyInfoPool;
+ }
+
+ InProgressStartOpEvent acquire(long startTime, long elapsedTime, @NonNull IBinder clientId,
+ @Nullable String attributionTag, @NonNull Runnable onDeath, int proxyUid,
+ @Nullable String proxyPackageName, @Nullable String proxyAttributionTag,
+ @AppOpsManager.UidState int uidState, @AppOpsManager.OpFlags int flags,
+ @AppOpsManager.AttributionFlags
+ int attributionFlags, int attributionChainId) throws RemoteException {
+
+ InProgressStartOpEvent recycled = acquire();
+
+ AppOpsManager.OpEventProxyInfo proxyInfo = null;
+ if (proxyUid != Process.INVALID_UID) {
+ proxyInfo = mOpEventProxyInfoPool.acquire(proxyUid, proxyPackageName,
+ proxyAttributionTag);
+ }
+
+ if (recycled != null) {
+ recycled.reinit(startTime, elapsedTime, clientId, attributionTag, onDeath,
+ uidState, flags, proxyInfo, attributionFlags, attributionChainId,
+ mOpEventProxyInfoPool);
+ return recycled;
+ }
+
+ return new InProgressStartOpEvent(startTime, elapsedTime, clientId, attributionTag,
+ onDeath, uidState, proxyInfo, flags, attributionFlags, attributionChainId);
+ }
+ }
+
+ /**
+ * An unsynchronized pool of {@link AppOpsManager.OpEventProxyInfo} objects.
+ */
+ static class OpEventProxyInfoPool extends Pools.SimplePool<AppOpsManager.OpEventProxyInfo> {
+ OpEventProxyInfoPool(int maxUnusedPooledObjects) {
+ super(maxUnusedPooledObjects);
+ }
+
+ AppOpsManager.OpEventProxyInfo acquire(@IntRange(from = 0) int uid,
+ @Nullable String packageName,
+ @Nullable String attributionTag) {
+ AppOpsManager.OpEventProxyInfo recycled = acquire();
+ if (recycled != null) {
+ recycled.reinit(uid, packageName, attributionTag);
+ return recycled;
+ }
+
+ return new AppOpsManager.OpEventProxyInfo(uid, packageName, attributionTag);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
index 802669728b24..f6fff351c232 100644
--- a/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
+++ b/services/core/java/com/android/server/appop/LegacyAppOpsServiceInterfaceImpl.java
@@ -333,6 +333,18 @@ public class LegacyAppOpsServiceInterfaceImpl implements AppOpsServiceInterface
}
@Override
+ public void notifyWatchersOfChange(int code, int uid) {
+ ArraySet<OnOpModeChangedListener> listenerSet = getOpModeChangedListeners(code);
+ if (listenerSet == null) {
+ return;
+ }
+ for (int i = 0; i < listenerSet.size(); i++) {
+ final OnOpModeChangedListener listener = listenerSet.valueAt(i);
+ notifyOpChanged(listener, code, uid, null);
+ }
+ }
+
+ @Override
public void notifyOpChanged(@NonNull OnOpModeChangedListener onModeChangedListener, int code,
int uid, @Nullable String packageName) {
Objects.requireNonNull(onModeChangedListener);
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 454894e2f26e..e5d40d93909f 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -1150,18 +1150,29 @@ public final class PlaybackActivityMonitor
}
return builder.toString();
case AudioPlaybackConfiguration.PLAYER_UPDATE_MUTED:
- builder.append(" muteFromMasterMute:").append(
- (mEventValue & PLAYER_MUTE_MASTER) != 0);
- builder.append(" muteFromStreamVolume:").append(
- (mEventValue & PLAYER_MUTE_STREAM_VOLUME) != 0);
- builder.append(" muteFromStreamMute:").append(
- (mEventValue & PLAYER_MUTE_STREAM_MUTED) != 0);
- builder.append(" muteFromPlaybackRestricted:").append(
- (mEventValue & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0);
- builder.append(" muteFromClientVolume:").append(
- (mEventValue & PLAYER_MUTE_CLIENT_VOLUME) != 0);
- builder.append(" muteFromVolumeShaper:").append(
- (mEventValue & PLAYER_MUTE_VOLUME_SHAPER) != 0);
+ builder.append(" source:");
+ if (mEventValue <= 0) {
+ builder.append("none ");
+ } else {
+ if ((mEventValue & PLAYER_MUTE_MASTER) != 0) {
+ builder.append("masterMute ");
+ }
+ if ((mEventValue & PLAYER_MUTE_STREAM_VOLUME) != 0) {
+ builder.append("streamVolume ");
+ }
+ if ((mEventValue & PLAYER_MUTE_STREAM_MUTED) != 0) {
+ builder.append("streamMute ");
+ }
+ if ((mEventValue & PLAYER_MUTE_PLAYBACK_RESTRICTED) != 0) {
+ builder.append("playbackRestricted ");
+ }
+ if ((mEventValue & PLAYER_MUTE_CLIENT_VOLUME) != 0) {
+ builder.append("clientVolume ");
+ }
+ if ((mEventValue & PLAYER_MUTE_VOLUME_SHAPER) != 0) {
+ builder.append("volumeShaper ");
+ }
+ }
return builder.toString();
default:
return builder.toString();
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
index ab553a864ad6..3ede0a2597d9 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
+++ b/services/core/java/com/android/server/broadcastradio/BroadcastRadioService.java
@@ -21,21 +21,23 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.radio.IRadioService;
-import android.util.Slog;
import com.android.server.SystemService;
+import java.util.ArrayList;
+
public class BroadcastRadioService extends SystemService {
- private static final String TAG = "BcRadioSrv";
private final IRadioService mServiceImpl;
+
public BroadcastRadioService(Context context) {
super(context);
- mServiceImpl = new BroadcastRadioServiceHidl(this);
+ ArrayList<String> serviceNameList = IRadioServiceAidlImpl.getServicesNames();
+ mServiceImpl = serviceNameList.isEmpty() ? new IRadioServiceHidlImpl(this)
+ : new IRadioServiceAidlImpl(this, serviceNameList);
}
@Override
public void onStart() {
- Slog.v(TAG, "BroadcastRadioService onStart()");
publishBinderService(Context.RADIO_SERVICE, mServiceImpl.asBinder());
}
diff --git a/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
new file mode 100644
index 000000000000..0770062cd4d3
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceAidlImpl.java
@@ -0,0 +1,124 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.broadcastradio;
+
+import android.hardware.broadcastradio.IBroadcastRadio;
+import android.hardware.radio.IAnnouncementListener;
+import android.hardware.radio.ICloseHandle;
+import android.hardware.radio.IRadioService;
+import android.hardware.radio.ITuner;
+import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.RadioManager;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import com.android.server.utils.Slogf;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Wrapper for AIDL interface for BroadcastRadio HAL
+ */
+final class IRadioServiceAidlImpl extends IRadioService.Stub {
+ private static final String TAG = "BcRadioSrvAidl";
+
+ private static final List<String> SERVICE_NAMES = Arrays.asList(
+ IBroadcastRadio.DESCRIPTOR + "/amfm", IBroadcastRadio.DESCRIPTOR + "/dab");
+
+ private final com.android.server.broadcastradio.aidl.BroadcastRadioServiceImpl mHalAidl;
+ private final BroadcastRadioService mService;
+
+ /**
+ * Gets names of all AIDL BroadcastRadio HAL services available.
+ */
+ public static ArrayList<String> getServicesNames() {
+ ArrayList<String> serviceList = new ArrayList<>();
+ for (int i = 0; i < SERVICE_NAMES.size(); i++) {
+ IBinder serviceBinder = ServiceManager.waitForDeclaredService(SERVICE_NAMES.get(i));
+ if (serviceBinder != null) {
+ serviceList.add(SERVICE_NAMES.get(i));
+ }
+ }
+ return serviceList;
+ }
+
+ IRadioServiceAidlImpl(BroadcastRadioService service, ArrayList<String> serviceList) {
+ Slogf.i(TAG, "Initialize BroadcastRadioServiceAidl(%s)", service);
+ mService = Objects.requireNonNull(service);
+ mHalAidl =
+ new com.android.server.broadcastradio.aidl.BroadcastRadioServiceImpl(serviceList);
+ }
+
+ @Override
+ public List<RadioManager.ModuleProperties> listModules() {
+ mService.enforcePolicyAccess();
+ return mHalAidl.listModules();
+ }
+
+ @Override
+ public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
+ boolean withAudio, ITunerCallback callback) throws RemoteException {
+ if (isDebugEnabled()) {
+ Slogf.d(TAG, "Opening module %d", moduleId);
+ }
+ mService.enforcePolicyAccess();
+ if (callback == null) {
+ throw new IllegalArgumentException("Callback must not be null");
+ }
+ return mHalAidl.openSession(moduleId, bandConfig, withAudio, callback);
+ }
+
+ @Override
+ public ICloseHandle addAnnouncementListener(int[] enabledTypes,
+ IAnnouncementListener listener) {
+ if (isDebugEnabled()) {
+ Slogf.d(TAG, "Adding announcement listener for %s", Arrays.toString(enabledTypes));
+ }
+ Objects.requireNonNull(enabledTypes);
+ Objects.requireNonNull(listener);
+ mService.enforcePolicyAccess();
+
+ return mHalAidl.addAnnouncementListener(enabledTypes, listener);
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter radioPrintWriter = new IndentingPrintWriter(printWriter);
+ radioPrintWriter.printf("BroadcastRadioService\n");
+
+ radioPrintWriter.increaseIndent();
+ radioPrintWriter.printf("AIDL HAL:\n");
+
+ radioPrintWriter.increaseIndent();
+ mHalAidl.dumpInfo(radioPrintWriter);
+ radioPrintWriter.decreaseIndent();
+
+ radioPrintWriter.decreaseIndent();
+ }
+
+ private static boolean isDebugEnabled() {
+ return Log.isLoggable(TAG, Log.DEBUG);
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/BroadcastRadioServiceHidl.java b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
index 5cb6770e7909..28b6d02581be 100644
--- a/services/core/java/com/android/server/broadcastradio/BroadcastRadioServiceHidl.java
+++ b/services/core/java/com/android/server/broadcastradio/IRadioServiceHidlImpl.java
@@ -41,7 +41,7 @@ import java.util.OptionalInt;
/**
* Wrapper for HIDL interface for BroadcastRadio HAL
*/
-final class BroadcastRadioServiceHidl extends IRadioService.Stub {
+final class IRadioServiceHidlImpl extends IRadioService.Stub {
private static final String TAG = "BcRadioSrvHidl";
private final com.android.server.broadcastradio.hal1.BroadcastRadioService mHal1;
@@ -52,7 +52,7 @@ final class BroadcastRadioServiceHidl extends IRadioService.Stub {
private final BroadcastRadioService mService;
private final List<RadioManager.ModuleProperties> mV1Modules;
- BroadcastRadioServiceHidl(BroadcastRadioService service) {
+ IRadioServiceHidlImpl(BroadcastRadioService service) {
mService = Objects.requireNonNull(service);
mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService(mLock);
mV1Modules = mHal1.loadModules();
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
new file mode 100644
index 000000000000..b618aa3d65dc
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/aidl/AnnouncementAggregator.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.broadcastradio.aidl;
+
+import android.annotation.Nullable;
+import android.hardware.radio.Announcement;
+import android.hardware.radio.IAnnouncementListener;
+import android.hardware.radio.ICloseHandle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.utils.Slogf;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Announcement aggregator extending {@link ICloseHandle} to support broadcast radio announcement
+ */
+public final class AnnouncementAggregator extends ICloseHandle.Stub {
+ private static final String TAG = "BcRadioAidlSrv.AnnAggr";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Object mLock;
+ private final IAnnouncementListener mListener;
+ private final IBinder.DeathRecipient mDeathRecipient = new DeathRecipient();
+
+ @GuardedBy("mLock")
+ private final List<ModuleWatcher> mModuleWatchers = new ArrayList<>();
+
+ @GuardedBy("mLock")
+ private boolean mIsClosed;
+
+ /**
+ * Constructs Announcement aggregator with AnnouncementListener of BroadcastRadio AIDL HAL.
+ */
+ public AnnouncementAggregator(IAnnouncementListener listener, Object lock) {
+ mListener = Objects.requireNonNull(listener, "listener cannot be null");
+ mLock = Objects.requireNonNull(lock, "lock cannot be null");
+ try {
+ listener.asBinder().linkToDeath(mDeathRecipient, /* flags= */ 0);
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ }
+ }
+
+ private final class ModuleWatcher extends IAnnouncementListener.Stub {
+
+ @Nullable
+ private ICloseHandle mCloseHandle;
+
+ public List<Announcement> mCurrentList = new ArrayList<>();
+
+ public void onListUpdated(List<Announcement> active) {
+ if (DEBUG) {
+ Slogf.d(TAG, "onListUpdate for %s", active);
+ }
+ mCurrentList = Objects.requireNonNull(active, "active cannot be null");
+ AnnouncementAggregator.this.onListUpdated();
+ }
+
+ public void setCloseHandle(ICloseHandle closeHandle) {
+ if (DEBUG) {
+ Slogf.d(TAG, "Set close handle %s", closeHandle);
+ }
+ mCloseHandle = Objects.requireNonNull(closeHandle, "closeHandle cannot be null");
+ }
+
+ public void close() throws RemoteException {
+ if (DEBUG) {
+ Slogf.d(TAG, "Close module watcher.");
+ }
+ if (mCloseHandle != null) mCloseHandle.close();
+ }
+
+ public void dumpInfo(IndentingPrintWriter pw) {
+ pw.printf("ModuleWatcher:\n");
+
+ pw.increaseIndent();
+ pw.printf("Close handle: %s\n", mCloseHandle);
+ pw.printf("Current announcement list: %s\n", mCurrentList);
+ pw.decreaseIndent();
+ }
+ }
+
+ private class DeathRecipient implements IBinder.DeathRecipient {
+ public void binderDied() {
+ try {
+ close();
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, ex, "Cannot close Announcement aggregator for DeathRecipient");
+ }
+ }
+ }
+
+ private void onListUpdated() {
+ if (DEBUG) {
+ Slogf.d(TAG, "onListUpdated()");
+ }
+ synchronized (mLock) {
+ if (mIsClosed) {
+ Slogf.e(TAG, "Announcement aggregator is closed, it shouldn't receive callbacks");
+ return;
+ }
+ List<Announcement> combined = new ArrayList<>(mModuleWatchers.size());
+ for (int i = 0; i < mModuleWatchers.size(); i++) {
+ combined.addAll(mModuleWatchers.get(i).mCurrentList);
+ }
+ try {
+ mListener.onListUpdated(combined);
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, ex, "mListener.onListUpdated() failed");
+ }
+ }
+ }
+
+ /**
+ * Watches the given RadioModule by adding Announcement Listener to it
+ */
+ public void watchModule(RadioModule radioModule, int[] enabledTypes) {
+ if (DEBUG) {
+ Slogf.d(TAG, "Watch module for %s with enabled types %s",
+ radioModule, Arrays.toString(enabledTypes));
+ }
+ synchronized (mLock) {
+ if (mIsClosed) {
+ throw new IllegalStateException("Failed to watch module"
+ + "since announcement aggregator has already been closed");
+ }
+
+ ModuleWatcher watcher = new ModuleWatcher();
+ ICloseHandle closeHandle;
+ try {
+ closeHandle = radioModule.addAnnouncementListener(watcher, enabledTypes);
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, ex, "Failed to add announcement listener");
+ return;
+ }
+ watcher.setCloseHandle(closeHandle);
+ mModuleWatchers.add(watcher);
+ }
+ }
+
+ @Override
+ public void close() throws RemoteException {
+ if (DEBUG) {
+ Slogf.d(TAG, "Close watchModule");
+ }
+ synchronized (mLock) {
+ if (mIsClosed) {
+ Slogf.w(TAG, "Announcement aggregator has already been closed.");
+ return;
+ }
+
+ mListener.asBinder().unlinkToDeath(mDeathRecipient, /* flags= */ 0);
+
+ for (int i = 0; i < mModuleWatchers.size(); i++) {
+ ModuleWatcher moduleWatcher = mModuleWatchers.get(i);
+ try {
+ moduleWatcher.close();
+ } catch (Exception e) {
+ Slogf.e(TAG, "Failed to close module watcher %s: %s",
+ moduleWatcher, e);
+ }
+ }
+ mModuleWatchers.clear();
+
+ mIsClosed = true;
+ }
+ }
+
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ IndentingPrintWriter announcementPrintWriter = new IndentingPrintWriter(printWriter);
+ announcementPrintWriter.printf("AnnouncementAggregator\n");
+
+ announcementPrintWriter.increaseIndent();
+ synchronized (mLock) {
+ announcementPrintWriter.printf("Is session closed? %s\n", mIsClosed ? "Yes" : "No");
+ announcementPrintWriter.printf("Module Watchers [%d]:\n", mModuleWatchers.size());
+
+ announcementPrintWriter.increaseIndent();
+ for (int i = 0; i < mModuleWatchers.size(); i++) {
+ mModuleWatchers.get(i).dumpInfo(announcementPrintWriter);
+ }
+ announcementPrintWriter.decreaseIndent();
+
+ }
+ announcementPrintWriter.decreaseIndent();
+ }
+
+}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
new file mode 100644
index 000000000000..71ba2968baec
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/aidl/BroadcastRadioServiceImpl.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.broadcastradio.aidl;
+
+import android.annotation.Nullable;
+import android.hardware.broadcastradio.IBroadcastRadio;
+import android.hardware.radio.IAnnouncementListener;
+import android.hardware.radio.ICloseHandle;
+import android.hardware.radio.ITuner;
+import android.hardware.radio.ITunerCallback;
+import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioTuner;
+import android.os.IBinder;
+import android.os.IServiceCallback;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArrayMap;
+import android.util.IndentingPrintWriter;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.utils.Slogf;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+/**
+ * Broadcast radio service using BroadcastRadio AIDL HAL
+ */
+public final class BroadcastRadioServiceImpl {
+ private static final String TAG = "BcRadioAidlSrv";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private int mNextModuleId;
+
+ @GuardedBy("mLock")
+ private final Map<String, Integer> mServiceNameToModuleIdMap = new ArrayMap<>();
+
+ // Map from module ID to RadioModule created by mServiceListener.onRegistration().
+ @GuardedBy("mLock")
+ private final SparseArray<RadioModule> mModules = new SparseArray<>();
+
+ private final IServiceCallback.Stub mServiceListener = new IServiceCallback.Stub() {
+ @Override
+ public void onRegistration(String name, final IBinder newBinder) {
+ Slogf.i(TAG, "onRegistration for %s", name);
+ Integer moduleId;
+ synchronized (mLock) {
+ // If the service has been registered before, reuse its previous module ID.
+ moduleId = mServiceNameToModuleIdMap.get(name);
+ boolean newService = false;
+ if (moduleId == null) {
+ newService = true;
+ moduleId = mNextModuleId;
+ }
+
+ RadioModule radioModule =
+ RadioModule.tryLoadingModule(moduleId, name, newBinder, mLock);
+ if (radioModule == null) {
+ Slogf.w(TAG, "No module %s with id %d (HAL AIDL)", name, moduleId);
+ return;
+ }
+ try {
+ radioModule.setInternalHalCallback();
+ } catch (RemoteException ex) {
+ Slogf.wtf(TAG, ex, "Broadcast radio module %s with id %d (HAL AIDL) "
+ + "cannot register HAL callback", name, moduleId);
+ return;
+ }
+ if (DEBUG) {
+ Slogf.d(TAG, "Loaded broadcast radio module %s with id %d (HAL AIDL)",
+ name, moduleId);
+ }
+ RadioModule prevModule = mModules.get(moduleId);
+ mModules.put(moduleId, radioModule);
+ if (prevModule != null) {
+ prevModule.closeSessions(RadioTuner.ERROR_HARDWARE_FAILURE);
+ }
+
+ if (newService) {
+ mServiceNameToModuleIdMap.put(name, moduleId);
+ mNextModuleId++;
+ }
+
+ try {
+ BroadcastRadioDeathRecipient deathRecipient =
+ new BroadcastRadioDeathRecipient(moduleId);
+ radioModule.getService().asBinder().linkToDeath(deathRecipient, moduleId);
+ } catch (RemoteException ex) {
+ Slogf.w(TAG, "Service has already died, so remove its entry from mModules.");
+ mModules.remove(moduleId);
+ }
+ }
+ }
+ };
+
+ private final class BroadcastRadioDeathRecipient implements IBinder.DeathRecipient {
+ private final int mModuleId;
+
+ BroadcastRadioDeathRecipient(int moduleId) {
+ mModuleId = moduleId;
+ }
+
+ @Override
+ public void binderDied() {
+ Slogf.i(TAG, "ServiceDied for module id %d", mModuleId);
+ synchronized (mLock) {
+ RadioModule prevModule = mModules.removeReturnOld(mModuleId);
+ if (prevModule != null) {
+ prevModule.closeSessions(RadioTuner.ERROR_HARDWARE_FAILURE);
+ }
+
+ for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) {
+ if (entry.getValue() == mModuleId) {
+ Slogf.w(TAG, "Service %s died, removed RadioModule with ID %d",
+ entry.getKey(), mModuleId);
+ return;
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Constructs BroadcastRadioServiceImpl using AIDL HAL using the list of names of AIDL
+ * BroadcastRadio HAL services {@code serviceNameList}
+ */
+ public BroadcastRadioServiceImpl(ArrayList<String> serviceNameList) {
+ mNextModuleId = 0;
+ if (DEBUG) {
+ Slogf.d(TAG, "Initializing BroadcastRadioServiceImpl %s",
+ IBroadcastRadio.DESCRIPTOR);
+ }
+ for (int i = 0; i < serviceNameList.size(); i++) {
+ try {
+ ServiceManager.registerForNotifications(serviceNameList.get(i), mServiceListener);
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, ex, "failed to register for service notifications for service %s",
+ serviceNameList.get(i));
+ }
+ }
+ }
+
+ /**
+ * Gets all AIDL {@link com.android.server.broadcastradio.aidl.RadioModule}.
+ */
+ public List<RadioManager.ModuleProperties> listModules() {
+ synchronized (mLock) {
+ List<RadioManager.ModuleProperties> moduleList = new ArrayList<>(mModules.size());
+ for (int i = 0; i < mModules.size(); i++) {
+ moduleList.add(mModules.valueAt(i).mProperties);
+ }
+ return moduleList;
+ }
+ }
+
+ /**
+ * Gets the AIDL RadioModule for the given {@code moduleId}. Null will be returned if not found.
+ */
+ public boolean hasModule(int id) {
+ synchronized (mLock) {
+ return mModules.contains(id);
+ }
+ }
+
+ /**
+ * Returns whether any AIDL {@link com.android.server.broadcastradio.aidl.RadioModule} exists.
+ */
+ public boolean hasAnyModules() {
+ synchronized (mLock) {
+ return mModules.size() != 0;
+ }
+ }
+
+ /**
+ * Opens {@link ITuner} session for the AIDL
+ * {@link com.android.server.broadcastradio.aidl.RadioModule} given {@code moduleId}.
+ */
+ @Nullable
+ public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
+ boolean withAudio, ITunerCallback callback) throws RemoteException {
+ if (DEBUG) {
+ Slogf.d(TAG, "Open AIDL radio session");
+ }
+ Objects.requireNonNull(callback);
+
+ if (!withAudio) {
+ throw new IllegalArgumentException("Non-audio sessions not supported with AIDL HAL");
+ }
+
+ RadioModule radioModule;
+ synchronized (mLock) {
+ radioModule = mModules.get(moduleId);
+ if (radioModule == null) {
+ Slogf.e(TAG, "Invalid module ID %d", moduleId);
+ return null;
+ }
+ }
+
+ TunerSession tunerSession = radioModule.openSession(callback);
+ if (legacyConfig != null) {
+ tunerSession.setConfiguration(legacyConfig);
+ }
+ return tunerSession;
+ }
+
+ /**
+ * Adds AnnouncementListener for every
+ * {@link com.android.server.broadcastradio.aidl.RadioModule}.
+ */
+ public ICloseHandle addAnnouncementListener(int[] enabledTypes,
+ IAnnouncementListener listener) {
+ if (DEBUG) {
+ Slogf.d(TAG, "Add AnnouncementListener with enable types %s",
+ Arrays.toString(enabledTypes));
+ }
+ AnnouncementAggregator aggregator = new AnnouncementAggregator(listener, mLock);
+ boolean anySupported = false;
+ synchronized (mLock) {
+ for (int i = 0; i < mModules.size(); i++) {
+ try {
+ aggregator.watchModule(mModules.valueAt(i), enabledTypes);
+ anySupported = true;
+ } catch (UnsupportedOperationException ex) {
+ Slogf.w(TAG, ex, "Announcements not supported for this module");
+ }
+ }
+ }
+ if (!anySupported) {
+ Slogf.w(TAG, "There are no HAL modules that support announcements");
+ }
+ return aggregator;
+ }
+
+ /**
+ * Dump state of broadcastradio service for AIDL HAL.
+ *
+ * @param pw The file to which {@link BroadcastRadioServiceImpl} state is dumped.
+ */
+ public void dumpInfo(IndentingPrintWriter pw) {
+ synchronized (mLock) {
+ pw.printf("Next module id available: %d\n", mNextModuleId);
+ pw.printf("ServiceName to module id map:\n");
+
+ pw.increaseIndent();
+ for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) {
+ pw.printf("Service name: %s, module id: %d\n", entry.getKey(), entry.getValue());
+ }
+ pw.decreaseIndent();
+
+ pw.printf("Radio modules [%d]:\n", mModules.size());
+
+ pw.increaseIndent();
+ for (int i = 0; i < mModules.size(); i++) {
+ pw.printf("Module id=%d:\n", mModules.keyAt(i));
+
+ pw.increaseIndent();
+ mModules.valueAt(i).dumpInfo(pw);
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
new file mode 100644
index 000000000000..d90f9c47f10a
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.broadcastradio.aidl;
+
+import android.annotation.Nullable;
+import android.hardware.broadcastradio.AmFmRegionConfig;
+import android.hardware.broadcastradio.Announcement;
+import android.hardware.broadcastradio.DabTableEntry;
+import android.hardware.broadcastradio.IdentifierType;
+import android.hardware.broadcastradio.Metadata;
+import android.hardware.broadcastradio.ProgramFilter;
+import android.hardware.broadcastradio.ProgramIdentifier;
+import android.hardware.broadcastradio.ProgramInfo;
+import android.hardware.broadcastradio.ProgramListChunk;
+import android.hardware.broadcastradio.Properties;
+import android.hardware.broadcastradio.Result;
+import android.hardware.broadcastradio.VendorKeyValue;
+import android.hardware.radio.ProgramList;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.hardware.radio.RadioMetadata;
+import android.os.ParcelableException;
+import android.os.ServiceSpecificException;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IntArray;
+
+import com.android.server.utils.Slogf;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A utils class converting data types between AIDL broadcast radio HAL and
+ * {@link android.hardware.radio}
+ */
+final class ConversionUtils {
+ // TODO(b/241118988): Add unit test for ConversionUtils class
+ private static final String TAG = "BcRadioAidlSrv.convert";
+
+ private ConversionUtils() {
+ throw new UnsupportedOperationException("ConversionUtils class is noninstantiable");
+ }
+
+ static RuntimeException throwOnError(RuntimeException halException, String action) {
+ if (!(halException instanceof ServiceSpecificException)) {
+ return new ParcelableException(new RuntimeException(
+ action + ": unknown error"));
+ }
+ int result = ((ServiceSpecificException) halException).errorCode;
+ switch (result) {
+ case Result.UNKNOWN_ERROR:
+ return new ParcelableException(new RuntimeException(action
+ + ": UNKNOWN_ERROR"));
+ case Result.INTERNAL_ERROR:
+ return new ParcelableException(new RuntimeException(action
+ + ": INTERNAL_ERROR"));
+ case Result.INVALID_ARGUMENTS:
+ return new IllegalArgumentException(action + ": INVALID_ARGUMENTS");
+ case Result.INVALID_STATE:
+ return new IllegalStateException(action + ": INVALID_STATE");
+ case Result.NOT_SUPPORTED:
+ return new UnsupportedOperationException(action + ": NOT_SUPPORTED");
+ case Result.TIMEOUT:
+ return new ParcelableException(new RuntimeException(action + ": TIMEOUT"));
+ default:
+ return new ParcelableException(new RuntimeException(
+ action + ": unknown error (" + result + ")"));
+ }
+ }
+
+ static VendorKeyValue[] vendorInfoToHalVendorKeyValues(@Nullable Map<String, String> info) {
+ if (info == null) {
+ return new VendorKeyValue[]{};
+ }
+
+ ArrayList<VendorKeyValue> list = new ArrayList<>();
+ for (Map.Entry<String, String> entry : info.entrySet()) {
+ VendorKeyValue elem = new VendorKeyValue();
+ elem.key = entry.getKey();
+ elem.value = entry.getValue();
+ if (elem.key == null || elem.value == null) {
+ Slogf.w(TAG, "VendorKeyValue contains invalid entry: key = %s, value = %s",
+ elem.key, elem.value);
+ continue;
+ }
+ list.add(elem);
+ }
+
+ return list.toArray(VendorKeyValue[]::new);
+ }
+
+ static Map<String, String> vendorInfoFromHalVendorKeyValues(@Nullable VendorKeyValue[] info) {
+ if (info == null) {
+ return Collections.emptyMap();
+ }
+
+ Map<String, String> map = new ArrayMap<>();
+ for (VendorKeyValue kvp : info) {
+ if (kvp.key == null || kvp.value == null) {
+ Slogf.w(TAG, "VendorKeyValue contains invalid entry: key = %s, value = %s",
+ kvp.key, kvp.value);
+ continue;
+ }
+ map.put(kvp.key, kvp.value);
+ }
+
+ return map;
+ }
+
+ @ProgramSelector.ProgramType
+ private static int identifierTypeToProgramType(
+ @ProgramSelector.IdentifierType int idType) {
+ switch (idType) {
+ case ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY:
+ case ProgramSelector.IDENTIFIER_TYPE_RDS_PI:
+ // TODO(b/69958423): verify AM/FM with frequency range
+ return ProgramSelector.PROGRAM_TYPE_FM;
+ case ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT:
+ // TODO(b/69958423): verify AM/FM with frequency range
+ return ProgramSelector.PROGRAM_TYPE_FM_HD;
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_SCID:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY:
+ return ProgramSelector.PROGRAM_TYPE_DAB;
+ case ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID:
+ case ProgramSelector.IDENTIFIER_TYPE_DRMO_FREQUENCY:
+ return ProgramSelector.PROGRAM_TYPE_DRMO;
+ case ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID:
+ case ProgramSelector.IDENTIFIER_TYPE_SXM_CHANNEL:
+ return ProgramSelector.PROGRAM_TYPE_SXM;
+ }
+ if (idType >= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_START
+ && idType <= ProgramSelector.IDENTIFIER_TYPE_VENDOR_PRIMARY_END) {
+ return idType;
+ }
+ return ProgramSelector.PROGRAM_TYPE_INVALID;
+ }
+
+ private static int[] identifierTypesToProgramTypes(int[] idTypes) {
+ Set<Integer> programTypes = new ArraySet<>();
+
+ for (int i = 0; i < idTypes.length; i++) {
+ int pType = identifierTypeToProgramType(idTypes[i]);
+
+ if (pType == ProgramSelector.PROGRAM_TYPE_INVALID) continue;
+
+ programTypes.add(pType);
+ if (pType == ProgramSelector.PROGRAM_TYPE_FM) {
+ // TODO(b/69958423): verify AM/FM with region info
+ programTypes.add(ProgramSelector.PROGRAM_TYPE_AM);
+ }
+ if (pType == ProgramSelector.PROGRAM_TYPE_FM_HD) {
+ // TODO(b/69958423): verify AM/FM with region info
+ programTypes.add(ProgramSelector.PROGRAM_TYPE_AM_HD);
+ }
+ }
+
+ int[] programTypesArray = new int[programTypes.size()];
+ int i = 0;
+ for (int programType : programTypes) {
+ programTypesArray[i++] = programType;
+ }
+ return programTypesArray;
+ }
+
+ private static RadioManager.BandDescriptor[] amfmConfigToBands(
+ @Nullable AmFmRegionConfig config) {
+ if (config == null) {
+ return new RadioManager.BandDescriptor[0];
+ }
+
+ int len = config.ranges.length;
+ List<RadioManager.BandDescriptor> bands = new ArrayList<>();
+
+ // Just a placeholder value.
+ int region = RadioManager.REGION_ITU_1;
+
+ for (int i = 0; i < len; i++) {
+ Utils.FrequencyBand bandType = Utils.getBand(config.ranges[i].lowerBound);
+ if (bandType == Utils.FrequencyBand.UNKNOWN) {
+ Slogf.e(TAG, "Unknown frequency band at %d kHz", config.ranges[i].lowerBound);
+ continue;
+ }
+ if (bandType == Utils.FrequencyBand.FM) {
+ bands.add(new RadioManager.FmBandDescriptor(region, RadioManager.BAND_FM,
+ config.ranges[i].lowerBound, config.ranges[i].upperBound,
+ config.ranges[i].spacing,
+
+ // TODO(b/69958777): stereo, rds, ta, af, ea
+ /* stereo= */ true, /* rds= */ true, /* ta= */ true, /* af= */ true,
+ /* ea= */ true
+ ));
+ } else { // AM
+ bands.add(new RadioManager.AmBandDescriptor(region, RadioManager.BAND_AM,
+ config.ranges[i].lowerBound, config.ranges[i].upperBound,
+ config.ranges[i].spacing,
+
+ // TODO(b/69958777): stereo
+ /* stereo= */ true
+ ));
+ }
+ }
+
+ return bands.toArray(RadioManager.BandDescriptor[]::new);
+ }
+
+ @Nullable
+ private static Map<String, Integer> dabConfigFromHalDabTableEntries(
+ @Nullable DabTableEntry[] config) {
+ if (config == null) {
+ return null;
+ }
+ Map<String, Integer> dabConfig = new ArrayMap<>();
+ for (int i = 0; i < config.length; i++) {
+ dabConfig.put(config[i].label, config[i].frequencyKhz);
+ }
+ return dabConfig;
+ }
+
+ static RadioManager.ModuleProperties propertiesFromHalProperties(int id,
+ String serviceName, Properties prop,
+ @Nullable AmFmRegionConfig amfmConfig, @Nullable DabTableEntry[] dabConfig) {
+ Objects.requireNonNull(serviceName);
+ Objects.requireNonNull(prop);
+
+ int[] supportedProgramTypes = identifierTypesToProgramTypes(prop.supportedIdentifierTypes);
+
+ return new RadioManager.ModuleProperties(
+ id,
+ serviceName,
+
+ // There is no Class concept in HAL AIDL.
+ RadioManager.CLASS_AM_FM,
+
+ prop.maker,
+ prop.product,
+ prop.version,
+ prop.serial,
+
+ // HAL AIDL only supports single tuner and audio source per
+ // HAL implementation instance.
+ /* numTuners= */ 1,
+ /* numAudioSources= */ 1,
+ /* isInitializationRequired= */ false,
+ /* isCaptureSupported= */ false,
+
+ amfmConfigToBands(amfmConfig),
+ /* isBgScanSupported= */ true,
+ supportedProgramTypes,
+ prop.supportedIdentifierTypes,
+ dabConfigFromHalDabTableEntries(dabConfig),
+ vendorInfoFromHalVendorKeyValues(prop.vendorInfo)
+ );
+ }
+
+ static ProgramIdentifier identifierToHalProgramIdentifier(ProgramSelector.Identifier id) {
+ ProgramIdentifier hwId = new ProgramIdentifier();
+ hwId.type = id.getType();
+ hwId.value = id.getValue();
+ return hwId;
+ }
+
+ @Nullable
+ static ProgramSelector.Identifier identifierFromHalProgramIdentifier(
+ ProgramIdentifier id) {
+ if (id.type == IdentifierType.INVALID) {
+ return null;
+ }
+ return new ProgramSelector.Identifier(id.type, id.value);
+ }
+
+ static android.hardware.broadcastradio.ProgramSelector programSelectorToHalProgramSelector(
+ ProgramSelector sel) {
+ android.hardware.broadcastradio.ProgramSelector hwSel =
+ new android.hardware.broadcastradio.ProgramSelector();
+
+ hwSel.primaryId = identifierToHalProgramIdentifier(sel.getPrimaryId());
+ ProgramSelector.Identifier[] secondaryIds = sel.getSecondaryIds();
+ ArrayList<ProgramIdentifier> secondaryIdList = new ArrayList<>(secondaryIds.length);
+ for (int i = 0; i < secondaryIds.length; i++) {
+ secondaryIdList.add(identifierToHalProgramIdentifier(secondaryIds[i]));
+ }
+ hwSel.secondaryIds = secondaryIdList.toArray(ProgramIdentifier[]::new);
+ return hwSel;
+ }
+
+ private static boolean isEmpty(
+ android.hardware.broadcastradio.ProgramSelector sel) {
+ return sel.primaryId.type == IdentifierType.INVALID && sel.primaryId.value == 0
+ && sel.secondaryIds.length == 0;
+ }
+
+ @Nullable
+ static ProgramSelector programSelectorFromHalProgramSelector(
+ android.hardware.broadcastradio.ProgramSelector sel) {
+ if (isEmpty(sel)) {
+ return null;
+ }
+
+ List<ProgramSelector.Identifier> secondaryIdList = new ArrayList<>();
+ for (int i = 0; i < sel.secondaryIds.length; i++) {
+ if (sel.secondaryIds[i] != null) {
+ secondaryIdList.add(identifierFromHalProgramIdentifier(sel.secondaryIds[i]));
+ }
+ }
+
+ return new ProgramSelector(
+ identifierTypeToProgramType(sel.primaryId.type),
+ Objects.requireNonNull(identifierFromHalProgramIdentifier(sel.primaryId)),
+ secondaryIdList.toArray(new ProgramSelector.Identifier[0]),
+ /* vendorIds= */ null);
+ }
+
+ private static RadioMetadata radioMetadataFromHalMetadata(Metadata[] meta) {
+ RadioMetadata.Builder builder = new RadioMetadata.Builder();
+
+ for (int i = 0; i < meta.length; i++) {
+ switch (meta[i].getTag()) {
+ case Metadata.rdsPs:
+ builder.putString(RadioMetadata.METADATA_KEY_RDS_PS, meta[i].getRdsPs());
+ break;
+ case Metadata.rdsPty:
+ builder.putInt(RadioMetadata.METADATA_KEY_RDS_PTY, meta[i].getRdsPty());
+ break;
+ case Metadata.rbdsPty:
+ builder.putInt(RadioMetadata.METADATA_KEY_RBDS_PTY, meta[i].getRbdsPty());
+ break;
+ case Metadata.rdsRt:
+ builder.putString(RadioMetadata.METADATA_KEY_RDS_RT, meta[i].getRdsRt());
+ break;
+ case Metadata.songTitle:
+ builder.putString(RadioMetadata.METADATA_KEY_TITLE, meta[i].getSongTitle());
+ break;
+ case Metadata.songArtist:
+ builder.putString(RadioMetadata.METADATA_KEY_ARTIST, meta[i].getSongArtist());
+ break;
+ case Metadata.songAlbum:
+ builder.putString(RadioMetadata.METADATA_KEY_ALBUM, meta[i].getSongAlbum());
+ break;
+ case Metadata.stationIcon:
+ builder.putInt(RadioMetadata.METADATA_KEY_ICON, meta[i].getStationIcon());
+ break;
+ case Metadata.albumArt:
+ builder.putInt(RadioMetadata.METADATA_KEY_ART, meta[i].getAlbumArt());
+ break;
+ case Metadata.programName:
+ builder.putString(RadioMetadata.METADATA_KEY_PROGRAM_NAME,
+ meta[i].getProgramName());
+ break;
+ case Metadata.dabEnsembleName:
+ builder.putString(RadioMetadata.METADATA_KEY_DAB_ENSEMBLE_NAME,
+ meta[i].getDabEnsembleName());
+ break;
+ case Metadata.dabEnsembleNameShort:
+ builder.putString(RadioMetadata.METADATA_KEY_DAB_ENSEMBLE_NAME_SHORT,
+ meta[i].getDabEnsembleNameShort());
+ break;
+ case Metadata.dabServiceName:
+ builder.putString(RadioMetadata.METADATA_KEY_DAB_SERVICE_NAME,
+ meta[i].getDabServiceName());
+ break;
+ case Metadata.dabServiceNameShort:
+ builder.putString(RadioMetadata.METADATA_KEY_DAB_SERVICE_NAME_SHORT,
+ meta[i].getDabServiceNameShort());
+ break;
+ case Metadata.dabComponentName:
+ builder.putString(RadioMetadata.METADATA_KEY_DAB_COMPONENT_NAME,
+ meta[i].getDabComponentName());
+ break;
+ case Metadata.dabComponentNameShort:
+ builder.putString(RadioMetadata.METADATA_KEY_DAB_COMPONENT_NAME_SHORT,
+ meta[i].getDabComponentNameShort());
+ break;
+ default:
+ Slogf.w(TAG, "Ignored unknown metadata entry: %s", meta[i]);
+ break;
+ }
+
+ }
+
+ return builder.build();
+ }
+
+ static RadioManager.ProgramInfo programInfoFromHalProgramInfo(ProgramInfo info) {
+ Collection<ProgramSelector.Identifier> relatedContent = new ArrayList<>();
+ if (info.relatedContent != null) {
+ for (int i = 0; i < info.relatedContent.length; i++) {
+ ProgramSelector.Identifier relatedContentId =
+ identifierFromHalProgramIdentifier(info.relatedContent[i]);
+ if (relatedContentId != null) {
+ relatedContent.add(relatedContentId);
+ }
+ }
+ }
+
+ return new RadioManager.ProgramInfo(
+ Objects.requireNonNull(programSelectorFromHalProgramSelector(info.selector)),
+ identifierFromHalProgramIdentifier(info.logicallyTunedTo),
+ identifierFromHalProgramIdentifier(info.physicallyTunedTo),
+ relatedContent,
+ info.infoFlags,
+ info.signalQuality,
+ radioMetadataFromHalMetadata(info.metadata),
+ vendorInfoFromHalVendorKeyValues(info.vendorInfo)
+ );
+ }
+
+ static ProgramFilter filterToHalProgramFilter(@Nullable ProgramList.Filter filter) {
+ if (filter == null) {
+ filter = new ProgramList.Filter();
+ }
+
+ ProgramFilter hwFilter = new ProgramFilter();
+
+ IntArray identifierTypeList = new IntArray(filter.getIdentifierTypes().size());
+ ArrayList<ProgramIdentifier> identifiersList = new ArrayList<>();
+ Iterator<Integer> typeIterator = filter.getIdentifierTypes().iterator();
+ while (typeIterator.hasNext()) {
+ identifierTypeList.add(typeIterator.next());
+ }
+ Iterator<ProgramSelector.Identifier> idIterator = filter.getIdentifiers().iterator();
+ while (idIterator.hasNext()) {
+ identifiersList.add(identifierToHalProgramIdentifier(idIterator.next()));
+ }
+
+ hwFilter.identifierTypes = identifierTypeList.toArray();
+ hwFilter.identifiers = identifiersList.toArray(ProgramIdentifier[]::new);
+ hwFilter.includeCategories = filter.areCategoriesIncluded();
+ hwFilter.excludeModifications = filter.areModificationsExcluded();
+
+ return hwFilter;
+ }
+
+ static ProgramList.Chunk chunkFromHalProgramListChunk(ProgramListChunk chunk) {
+ Set<RadioManager.ProgramInfo> modified = new ArraySet<>(chunk.modified.length);
+ for (int i = 0; i < chunk.modified.length; i++) {
+ modified.add(programInfoFromHalProgramInfo(chunk.modified[i]));
+ }
+ Set<ProgramSelector.Identifier> removed = new ArraySet<>();
+ if (chunk.removed != null) {
+ for (int i = 0; i < chunk.removed.length; i++) {
+ ProgramSelector.Identifier removedId =
+ identifierFromHalProgramIdentifier(chunk.removed[i]);
+ if (removedId != null) {
+ removed.add(removedId);
+ }
+ }
+ }
+ return new ProgramList.Chunk(chunk.purge, chunk.complete, modified, removed);
+ }
+
+ public static android.hardware.radio.Announcement announcementFromHalAnnouncement(
+ Announcement hwAnnouncement) {
+ return new android.hardware.radio.Announcement(
+ Objects.requireNonNull(programSelectorFromHalProgramSelector(
+ hwAnnouncement.selector)),
+ hwAnnouncement.type,
+ vendorInfoFromHalVendorKeyValues(hwAnnouncement.vendorInfo)
+ );
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ProgramInfoCache.java b/services/core/java/com/android/server/broadcastradio/aidl/ProgramInfoCache.java
new file mode 100644
index 000000000000..095a5fa1cf30
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ProgramInfoCache.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.broadcastradio.aidl;
+
+import android.annotation.Nullable;
+import android.hardware.radio.ProgramList;
+import android.hardware.radio.ProgramSelector;
+import android.hardware.radio.RadioManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A class to filter and update program info for HAL clients from broadcast radio AIDL HAL
+ */
+final class ProgramInfoCache {
+
+ /**
+ * Maximum number of {@link RadioManager#ProgramInfo} elements that will be put into a
+ * ProgramList.Chunk.mModified array. Used to try to ensure a single ProgramList.Chunk
+ * stays within the AIDL data size limit.
+ */
+ private static final int MAX_NUM_MODIFIED_PER_CHUNK = 100;
+
+ /**
+ * Maximum number of {@link ProgramSelector#Identifier} elements that will be put
+ * into the removed array of {@link ProgramList#Chunk}. Used to try to ensure a single
+ * {@link ProgramList#Chunk} stays within the AIDL data size limit.
+ */
+ private static final int MAX_NUM_REMOVED_PER_CHUNK = 500;
+
+ /**
+ * Map from primary identifier to corresponding {@link RadioManager#ProgramInfo}.
+ */
+ private final Map<ProgramSelector.Identifier, RadioManager.ProgramInfo> mProgramInfoMap =
+ new ArrayMap<>();
+
+ /**
+ * Flag indicating whether mProgramInfoMap is considered complete based upon the received
+ * updates.
+ */
+ private boolean mComplete = true;
+
+ /**
+ * Optional filter used in {@link ProgramInfoCache#filterAndUpdateFromInternal}. Usually this
+ * field is null for a HAL-side cache and non-null for an AIDL-side cache.
+ */
+ @Nullable private final ProgramList.Filter mFilter;
+
+ ProgramInfoCache(@Nullable ProgramList.Filter filter) {
+ mFilter = filter;
+ }
+
+ @VisibleForTesting
+ ProgramInfoCache(@Nullable ProgramList.Filter filter, boolean complete,
+ RadioManager.ProgramInfo... programInfos) {
+ mFilter = filter;
+ mComplete = complete;
+ for (int i = 0; i < programInfos.length; i++) {
+ mProgramInfoMap.put(programInfos[i].getSelector().getPrimaryId(), programInfos[i]);
+ }
+ }
+
+ @VisibleForTesting
+ boolean programInfosAreExactly(RadioManager.ProgramInfo... programInfos) {
+ Map<ProgramSelector.Identifier, RadioManager.ProgramInfo> expectedMap = new ArrayMap<>();
+ for (int i = 0; i < programInfos.length; i++) {
+ expectedMap.put(programInfos[i].getSelector().getPrimaryId(), programInfos[i]);
+ }
+ return expectedMap.equals(mProgramInfoMap);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("ProgramInfoCache(mComplete = ");
+ sb.append(mComplete);
+ sb.append(", mFilter = ");
+ sb.append(mFilter);
+ sb.append(", mProgramInfoMap = [");
+ mProgramInfoMap.forEach((id, programInfo) -> {
+ sb.append(", ");
+ sb.append(programInfo);
+ });
+ return sb.append("])").toString();
+ }
+
+ public boolean isComplete() {
+ return mComplete;
+ }
+
+ @Nullable
+ public ProgramList.Filter getFilter() {
+ return mFilter;
+ }
+
+ @VisibleForTesting
+ void updateFromHalProgramListChunk(
+ android.hardware.broadcastradio.ProgramListChunk chunk) {
+ if (chunk.purge) {
+ mProgramInfoMap.clear();
+ }
+ for (int i = 0; i < chunk.modified.length; i++) {
+ RadioManager.ProgramInfo programInfo =
+ ConversionUtils.programInfoFromHalProgramInfo(chunk.modified[i]);
+ mProgramInfoMap.put(programInfo.getSelector().getPrimaryId(), programInfo);
+ }
+ if (chunk.removed != null) {
+ for (int i = 0; i < chunk.removed.length; i++) {
+ mProgramInfoMap.remove(
+ ConversionUtils.identifierFromHalProgramIdentifier(chunk.removed[i]));
+ }
+ }
+ mComplete = chunk.complete;
+ }
+
+ List<ProgramList.Chunk> filterAndUpdateFromInternal(ProgramInfoCache other,
+ boolean purge) {
+ return filterAndUpdateFromInternal(other, purge, MAX_NUM_MODIFIED_PER_CHUNK,
+ MAX_NUM_REMOVED_PER_CHUNK);
+ }
+
+ @VisibleForTesting
+ List<ProgramList.Chunk> filterAndUpdateFromInternal(ProgramInfoCache other,
+ boolean purge, int maxNumModifiedPerChunk, int maxNumRemovedPerChunk) {
+ if (purge) {
+ mProgramInfoMap.clear();
+ }
+ // If mProgramInfoMap is empty, we treat this update as a purge because this might be the
+ // first update to an AIDL client that changed its filter.
+ if (mProgramInfoMap.isEmpty()) {
+ purge = true;
+ }
+
+ Set<RadioManager.ProgramInfo> modified = new ArraySet<>();
+ Set<ProgramSelector.Identifier> removed = new ArraySet<>(mProgramInfoMap.keySet());
+ for (Map.Entry<ProgramSelector.Identifier, RadioManager.ProgramInfo> entry
+ : other.mProgramInfoMap.entrySet()) {
+ ProgramSelector.Identifier id = entry.getKey();
+ if (!passesFilter(id)) {
+ continue;
+ }
+ removed.remove(id);
+
+ RadioManager.ProgramInfo newInfo = entry.getValue();
+ if (!shouldIncludeInModified(newInfo)) {
+ continue;
+ }
+ mProgramInfoMap.put(id, newInfo);
+ modified.add(newInfo);
+ }
+ for (ProgramSelector.Identifier rem : removed) {
+ mProgramInfoMap.remove(rem);
+ }
+ mComplete = other.mComplete;
+ return buildChunks(purge, mComplete, modified, maxNumModifiedPerChunk, removed,
+ maxNumRemovedPerChunk);
+ }
+
+ @Nullable
+ List<ProgramList.Chunk> filterAndApplyChunk(ProgramList.Chunk chunk) {
+ return filterAndApplyChunkInternal(chunk, MAX_NUM_MODIFIED_PER_CHUNK,
+ MAX_NUM_REMOVED_PER_CHUNK);
+ }
+
+ @VisibleForTesting
+ @Nullable
+ List<ProgramList.Chunk> filterAndApplyChunkInternal(ProgramList.Chunk chunk,
+ int maxNumModifiedPerChunk, int maxNumRemovedPerChunk) {
+ if (chunk.isPurge()) {
+ mProgramInfoMap.clear();
+ }
+
+ Set<RadioManager.ProgramInfo> modified = new ArraySet<>();
+ Set<ProgramSelector.Identifier> removed = new ArraySet<>();
+ for (RadioManager.ProgramInfo info : chunk.getModified()) {
+ ProgramSelector.Identifier id = info.getSelector().getPrimaryId();
+ if (!passesFilter(id) || !shouldIncludeInModified(info)) {
+ continue;
+ }
+ mProgramInfoMap.put(id, info);
+ modified.add(info);
+ }
+ for (ProgramSelector.Identifier id : chunk.getRemoved()) {
+ if (mProgramInfoMap.containsKey(id)) {
+ mProgramInfoMap.remove(id);
+ removed.add(id);
+ }
+ }
+ if (modified.isEmpty() && removed.isEmpty() && mComplete == chunk.isComplete()
+ && !chunk.isPurge()) {
+ return null;
+ }
+ mComplete = chunk.isComplete();
+ return buildChunks(chunk.isPurge(), mComplete, modified, maxNumModifiedPerChunk, removed,
+ maxNumRemovedPerChunk);
+ }
+
+ private boolean passesFilter(ProgramSelector.Identifier id) {
+ if (mFilter == null) {
+ return true;
+ }
+ if (!mFilter.getIdentifierTypes().isEmpty()
+ && !mFilter.getIdentifierTypes().contains(id.getType())) {
+ return false;
+ }
+ if (!mFilter.getIdentifiers().isEmpty() && !mFilter.getIdentifiers().contains(id)) {
+ return false;
+ }
+ return mFilter.areCategoriesIncluded() || !id.isCategoryType();
+ }
+
+ private boolean shouldIncludeInModified(RadioManager.ProgramInfo newInfo) {
+ RadioManager.ProgramInfo oldInfo = mProgramInfoMap.get(
+ newInfo.getSelector().getPrimaryId());
+ if (oldInfo == null) {
+ return true;
+ }
+ if (mFilter != null && mFilter.areModificationsExcluded()) {
+ return false;
+ }
+ return !oldInfo.equals(newInfo);
+ }
+
+ private static int roundUpFraction(int numerator, int denominator) {
+ return (numerator / denominator) + (numerator % denominator > 0 ? 1 : 0);
+ }
+
+ private static List<ProgramList.Chunk> buildChunks(boolean purge, boolean complete,
+ @Nullable Collection<RadioManager.ProgramInfo> modified, int maxNumModifiedPerChunk,
+ @Nullable Collection<ProgramSelector.Identifier> removed, int maxNumRemovedPerChunk) {
+ // Communication protocol requires that if purge is set, removed is empty.
+ if (purge) {
+ removed = null;
+ }
+
+ // Determine number of chunks we need to send.
+ int numChunks = purge ? 1 : 0;
+ if (modified != null) {
+ numChunks = Math.max(numChunks,
+ roundUpFraction(modified.size(), maxNumModifiedPerChunk));
+ }
+ if (removed != null) {
+ numChunks = Math.max(numChunks, roundUpFraction(removed.size(), maxNumRemovedPerChunk));
+ }
+ if (numChunks == 0) {
+ return new ArrayList<>();
+ }
+
+ // Try to make similarly-sized chunks by evenly distributing elements from modified and
+ // removed among them.
+ int modifiedPerChunk = 0;
+ int removedPerChunk = 0;
+ Iterator<RadioManager.ProgramInfo> modifiedIter = null;
+ Iterator<ProgramSelector.Identifier> removedIter = null;
+ if (modified != null) {
+ modifiedPerChunk = roundUpFraction(modified.size(), numChunks);
+ modifiedIter = modified.iterator();
+ }
+ if (removed != null) {
+ removedPerChunk = roundUpFraction(removed.size(), numChunks);
+ removedIter = removed.iterator();
+ }
+ List<ProgramList.Chunk> chunks = new ArrayList<>(numChunks);
+ for (int i = 0; i < numChunks; i++) {
+ ArraySet<RadioManager.ProgramInfo> modifiedChunk = new ArraySet<>();
+ ArraySet<ProgramSelector.Identifier> removedChunk = new ArraySet<>();
+ if (modifiedIter != null) {
+ for (int j = 0; j < modifiedPerChunk && modifiedIter.hasNext(); j++) {
+ modifiedChunk.add(modifiedIter.next());
+ }
+ }
+ if (removedIter != null) {
+ for (int j = 0; j < removedPerChunk && removedIter.hasNext(); j++) {
+ removedChunk.add(removedIter.next());
+ }
+ }
+ chunks.add(new ProgramList.Chunk(purge && i == 0, complete && (i == numChunks - 1),
+ modifiedChunk, removedChunk));
+ }
+ return chunks;
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioLogger.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioLogger.java
new file mode 100644
index 000000000000..cca351bc0d73
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioLogger.java
@@ -0,0 +1,52 @@
+/**
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.broadcastradio.aidl;
+
+import android.text.TextUtils;
+import android.util.IndentingPrintWriter;
+import android.util.LocalLog;
+import android.util.Log;
+
+import com.android.server.utils.Slogf;
+
+/**
+ * Event logger to log and dump events of radio module and tuner session
+ * for AIDL broadcast radio HAL
+ */
+final class RadioLogger {
+ private final String mTag;
+ private final boolean mDebug;
+ private final LocalLog mEventLogger;
+
+ RadioLogger(String tag, int loggerQueueSize) {
+ mTag = tag;
+ mDebug = Log.isLoggable(mTag, Log.DEBUG);
+ mEventLogger = new LocalLog(loggerQueueSize);
+ }
+
+ void logRadioEvent(String logFormat, Object... args) {
+ String log = TextUtils.formatSimple(logFormat, args);
+ mEventLogger.log(log);
+ if (mDebug) {
+ Slogf.d(mTag, logFormat, args);
+ }
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ mEventLogger.dump(pw);
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
new file mode 100644
index 000000000000..c6dc43197630
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/aidl/RadioModule.java
@@ -0,0 +1,523 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.broadcastradio.aidl;
+
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.hardware.broadcastradio.AmFmRegionConfig;
+import android.hardware.broadcastradio.Announcement;
+import android.hardware.broadcastradio.DabTableEntry;
+import android.hardware.broadcastradio.IAnnouncementListener;
+import android.hardware.broadcastradio.IBroadcastRadio;
+import android.hardware.broadcastradio.ICloseHandle;
+import android.hardware.broadcastradio.ITunerCallback;
+import android.hardware.broadcastradio.ProgramInfo;
+import android.hardware.broadcastradio.ProgramListChunk;
+import android.hardware.broadcastradio.ProgramSelector;
+import android.hardware.broadcastradio.VendorKeyValue;
+import android.hardware.radio.RadioManager;
+import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.utils.Slogf;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+final class RadioModule {
+ private static final String TAG = "BcRadioAidlSrv.module";
+ private static final int RADIO_EVENT_LOGGER_QUEUE_SIZE = 25;
+
+ private final IBroadcastRadio mService;
+ public final RadioManager.ModuleProperties mProperties;
+
+ private final Object mLock;
+ private final Handler mHandler;
+ private final RadioLogger mLogger;
+
+ /**
+ * Tracks antenna state reported by HAL (if any).
+ */
+ @GuardedBy("mLock")
+ private Boolean mAntennaConnected;
+
+ @GuardedBy("mLock")
+ private RadioManager.ProgramInfo mCurrentProgramInfo;
+
+ @GuardedBy("mLock")
+ private final ProgramInfoCache mProgramInfoCache = new ProgramInfoCache(null);
+
+ @GuardedBy("mLock")
+ private android.hardware.radio.ProgramList.Filter mUnionOfAidlProgramFilters;
+
+ /**
+ * Set of active AIDL tuner sessions created through openSession().
+ */
+ @GuardedBy("mLock")
+ private final ArraySet<TunerSession> mAidlTunerSessions = new ArraySet<>();
+
+ /**
+ * Callback registered with the HAL to relay callbacks to AIDL clients.
+ */
+ private final ITunerCallback mHalTunerCallback = new ITunerCallback.Stub() {
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
+
+ public void onTuneFailed(int result, ProgramSelector programSelector) {
+ fireLater(() -> {
+ synchronized (mLock) {
+ android.hardware.radio.ProgramSelector csel =
+ ConversionUtils.programSelectorFromHalProgramSelector(programSelector);
+ fanoutAidlCallbackLocked(cb -> cb.onTuneFailed(result, csel));
+ }
+ });
+ }
+
+ @Override
+ public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
+ fireLater(() -> {
+ synchronized (mLock) {
+ mCurrentProgramInfo =
+ ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
+ RadioManager.ProgramInfo currentProgramInfo = mCurrentProgramInfo;
+ fanoutAidlCallbackLocked(cb -> {
+ cb.onCurrentProgramInfoChanged(currentProgramInfo);
+ });
+ }
+ });
+ }
+
+ @Override
+ public void onProgramListUpdated(ProgramListChunk programListChunk) {
+ fireLater(() -> {
+ synchronized (mLock) {
+ android.hardware.radio.ProgramList.Chunk chunk =
+ ConversionUtils.chunkFromHalProgramListChunk(programListChunk);
+ mProgramInfoCache.filterAndApplyChunk(chunk);
+
+ for (int i = 0; i < mAidlTunerSessions.size(); i++) {
+ mAidlTunerSessions.valueAt(i).onMergedProgramListUpdateFromHal(chunk);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void onAntennaStateChange(boolean connected) {
+ fireLater(() -> {
+ synchronized (mLock) {
+ mAntennaConnected = connected;
+ fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected));
+ }
+ });
+ }
+
+ @Override
+ public void onConfigFlagUpdated(int flag, boolean value) {
+ fireLater(() -> {
+ // TODO(b/243853343): implement config flag update method in
+ // android.hardware.radio.ITunerCallback
+ });
+ }
+
+ @Override
+ public void onParametersUpdated(VendorKeyValue[] parameters) {
+ fireLater(() -> {
+ synchronized (mLock) {
+ Map<String, String> cparam =
+ ConversionUtils.vendorInfoFromHalVendorKeyValues(parameters);
+ fanoutAidlCallbackLocked(cb -> cb.onParametersUpdated(cparam));
+ }
+ });
+ }
+ };
+
+ @VisibleForTesting
+ RadioModule(IBroadcastRadio service,
+ RadioManager.ModuleProperties properties, Object lock) {
+ mProperties = Objects.requireNonNull(properties, "properties cannot be null");
+ mService = Objects.requireNonNull(service, "service cannot be null");
+ mLock = Objects.requireNonNull(lock, "lock cannot be null");
+ mHandler = new Handler(Looper.getMainLooper());
+ mLogger = new RadioLogger(TAG, RADIO_EVENT_LOGGER_QUEUE_SIZE);
+ }
+
+ @Nullable
+ public static RadioModule tryLoadingModule(int moduleId, String moduleName,
+ IBinder serviceBinder, Object lock) {
+ try {
+ Slogf.i(TAG, "Try loading module for module id = %d, module name = %s",
+ moduleId, moduleName);
+ IBroadcastRadio service = IBroadcastRadio.Stub
+ .asInterface(serviceBinder);
+ if (service == null) {
+ Slogf.w(TAG, "Module %s is null", moduleName);
+ return null;
+ }
+
+ AmFmRegionConfig amfmConfig;
+ try {
+ amfmConfig = service.getAmFmRegionConfig(/* full= */ false);
+ } catch (RuntimeException ex) {
+ Slogf.i(TAG, "Module %s does not has AMFM config", moduleName);
+ amfmConfig = null;
+ }
+
+ DabTableEntry[] dabConfig;
+ try {
+ dabConfig = service.getDabRegionConfig();
+ } catch (RuntimeException ex) {
+ Slogf.i(TAG, "Module %s does not has DAB config", moduleName);
+ dabConfig = null;
+ }
+
+ RadioManager.ModuleProperties prop = ConversionUtils.propertiesFromHalProperties(
+ moduleId, moduleName, service.getProperties(), amfmConfig, dabConfig);
+
+ return new RadioModule(service, prop, lock);
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, ex, "Failed to load module %s", moduleName);
+ return null;
+ }
+ }
+
+ public IBroadcastRadio getService() {
+ return mService;
+ }
+
+ void setInternalHalCallback() throws RemoteException {
+ synchronized (mLock) {
+ mService.setTunerCallback(mHalTunerCallback);
+ }
+ }
+
+ public TunerSession openSession(android.hardware.radio.ITunerCallback userCb)
+ throws RemoteException {
+ mLogger.logRadioEvent("Open TunerSession");
+ TunerSession tunerSession;
+ Boolean antennaConnected;
+ RadioManager.ProgramInfo currentProgramInfo;
+ synchronized (mLock) {
+ tunerSession = new TunerSession(this, mService, userCb, mLock);
+ mAidlTunerSessions.add(tunerSession);
+ antennaConnected = mAntennaConnected;
+ currentProgramInfo = mCurrentProgramInfo;
+ }
+ // Propagate state to new client.
+ // Note: These callbacks are invoked while holding mLock to prevent race conditions
+ // with new callbacks from the HAL.
+ if (antennaConnected != null) {
+ userCb.onAntennaState(antennaConnected);
+ }
+ if (currentProgramInfo != null) {
+ userCb.onCurrentProgramInfoChanged(currentProgramInfo);
+ }
+
+ return tunerSession;
+ }
+
+ public void closeSessions(int error) {
+ mLogger.logRadioEvent("Close TunerSessions %d", error);
+ // TunerSession.close() must be called without mAidlTunerSessions locked because
+ // it can call onTunerSessionClosed(). Therefore, the contents of mAidlTunerSessions
+ // are copied into a local array here.
+ TunerSession[] tunerSessions;
+ synchronized (mLock) {
+ tunerSessions = new TunerSession[mAidlTunerSessions.size()];
+ mAidlTunerSessions.toArray(tunerSessions);
+ mAidlTunerSessions.clear();
+ }
+
+ for (TunerSession tunerSession : tunerSessions) {
+ try {
+ tunerSession.close(error);
+ } catch (Exception e) {
+ Slogf.e(TAG, "Failed to close TunerSession %s: %s", tunerSession, e);
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private android.hardware.radio.ProgramList.Filter
+ buildUnionOfTunerSessionFiltersLocked() {
+ Set<Integer> idTypes = null;
+ Set<android.hardware.radio.ProgramSelector.Identifier> ids = null;
+ boolean includeCategories = false;
+ boolean excludeModifications = true;
+
+ for (int i = 0; i < mAidlTunerSessions.size(); i++) {
+ android.hardware.radio.ProgramList.Filter filter =
+ mAidlTunerSessions.valueAt(i).getProgramListFilter();
+ if (filter == null) {
+ continue;
+ }
+
+ if (idTypes == null) {
+ idTypes = new ArraySet<>(filter.getIdentifierTypes());
+ ids = new ArraySet<>(filter.getIdentifiers());
+ includeCategories = filter.areCategoriesIncluded();
+ excludeModifications = filter.areModificationsExcluded();
+ continue;
+ }
+ if (!idTypes.isEmpty()) {
+ if (filter.getIdentifierTypes().isEmpty()) {
+ idTypes.clear();
+ } else {
+ idTypes.addAll(filter.getIdentifierTypes());
+ }
+ }
+
+ if (!ids.isEmpty()) {
+ if (filter.getIdentifiers().isEmpty()) {
+ ids.clear();
+ } else {
+ ids.addAll(filter.getIdentifiers());
+ }
+ }
+
+ includeCategories |= filter.areCategoriesIncluded();
+ excludeModifications &= filter.areModificationsExcluded();
+ }
+
+ return idTypes == null ? null : new android.hardware.radio.ProgramList.Filter(idTypes, ids,
+ includeCategories, excludeModifications);
+ }
+
+ void onTunerSessionProgramListFilterChanged(@Nullable TunerSession session) {
+ synchronized (mLock) {
+ onTunerSessionProgramListFilterChangedLocked(session);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void onTunerSessionProgramListFilterChangedLocked(@Nullable TunerSession session) {
+ android.hardware.radio.ProgramList.Filter newFilter =
+ buildUnionOfTunerSessionFiltersLocked();
+ if (newFilter == null) {
+ // If there are no AIDL clients remaining, we can stop updates from the HAL as well.
+ if (mUnionOfAidlProgramFilters == null) {
+ return;
+ }
+ mUnionOfAidlProgramFilters = null;
+ try {
+ mService.stopProgramListUpdates();
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, ex, "mHalTunerSession.stopProgramListUpdates() failed");
+ }
+ return;
+ }
+
+ synchronized (mLock) {
+ // If the HAL filter doesn't change, we can immediately send an update to the AIDL
+ // client.
+ if (newFilter.equals(mUnionOfAidlProgramFilters)) {
+ if (session != null) {
+ session.updateProgramInfoFromHalCache(mProgramInfoCache);
+ }
+ return;
+ }
+
+ // Otherwise, update the HAL's filter, and AIDL clients will be updated when
+ // mHalTunerCallback.onProgramListUpdated() is called.
+ mUnionOfAidlProgramFilters = newFilter;
+ try {
+ mService.startProgramListUpdates(
+ ConversionUtils.filterToHalProgramFilter(newFilter));
+ } catch (RuntimeException ex) {
+ throw ConversionUtils.throwOnError(ex, /* action= */ "Start Program ListUpdates");
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, ex, "mHalTunerSession.startProgramListUpdates() failed");
+ }
+ }
+ }
+
+ void onTunerSessionClosed(TunerSession tunerSession) {
+ synchronized (mLock) {
+ onTunerSessionsClosedLocked(tunerSession);
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void onTunerSessionsClosedLocked(TunerSession... tunerSessions) {
+ for (TunerSession tunerSession : tunerSessions) {
+ mAidlTunerSessions.remove(tunerSession);
+ }
+ onTunerSessionProgramListFilterChanged(null);
+ }
+
+ // add to mHandler queue
+ private void fireLater(Runnable r) {
+ mHandler.post(() -> r.run());
+ }
+
+ interface AidlCallbackRunnable {
+ void run(android.hardware.radio.ITunerCallback callback) throws RemoteException;
+ }
+
+ // Invokes runnable with each TunerSession currently open.
+ void fanoutAidlCallback(AidlCallbackRunnable runnable) {
+ fireLater(() -> {
+ synchronized (mLock) {
+ fanoutAidlCallbackLocked(runnable);
+ }
+ });
+ }
+
+ @GuardedBy("mLock")
+ private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
+ List<TunerSession> deadSessions = null;
+ for (int i = 0; i < mAidlTunerSessions.size(); i++) {
+ try {
+ runnable.run(mAidlTunerSessions.valueAt(i).mCallback);
+ } catch (DeadObjectException ex) {
+ // The other side died without calling close(), so just purge it from our records.
+ Slogf.e(TAG, "Removing dead TunerSession");
+ if (deadSessions == null) {
+ deadSessions = new ArrayList<>();
+ }
+ deadSessions.add(mAidlTunerSessions.valueAt(i));
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, ex, "Failed to invoke ITunerCallback");
+ }
+ }
+ if (deadSessions != null) {
+ onTunerSessionsClosedLocked(deadSessions.toArray(
+ new TunerSession[deadSessions.size()]));
+ }
+ }
+
+ public android.hardware.radio.ICloseHandle addAnnouncementListener(
+ android.hardware.radio.IAnnouncementListener listener,
+ int[] enabledTypes) throws RemoteException {
+ mLogger.logRadioEvent("Add AnnouncementListener");
+ byte[] enabledList = new byte[enabledTypes.length];
+ for (int index = 0; index < enabledList.length; index++) {
+ enabledList[index] = (byte) enabledTypes[index];
+ }
+
+ final ICloseHandle[] hwCloseHandle = {null};
+ IAnnouncementListener hwListener = new IAnnouncementListener.Stub() {
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
+
+ public String getInterfaceHash() {
+ return this.HASH;
+ }
+
+ public void onListUpdated(Announcement[] hwAnnouncements)
+ throws RemoteException {
+ List<android.hardware.radio.Announcement> announcements =
+ new ArrayList<>(hwAnnouncements.length);
+ for (int i = 0; i < hwAnnouncements.length; i++) {
+ announcements.add(
+ ConversionUtils.announcementFromHalAnnouncement(hwAnnouncements[i]));
+ }
+ listener.onListUpdated(announcements);
+ }
+ };
+
+ synchronized (mLock) {
+ try {
+ hwCloseHandle[0] = mService.registerAnnouncementListener(hwListener, enabledList);
+ } catch (RuntimeException ex) {
+ throw ConversionUtils.throwOnError(ex, /* action= */ "AnnouncementListener");
+ }
+ }
+
+ return new android.hardware.radio.ICloseHandle.Stub() {
+ public void close() {
+ try {
+ hwCloseHandle[0].close();
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, ex, "Failed closing announcement listener");
+ }
+ hwCloseHandle[0] = null;
+ }
+ };
+ }
+
+ Bitmap getImage(int id) {
+ mLogger.logRadioEvent("Get image for id = %d", id);
+ if (id == 0) throw new IllegalArgumentException("Image ID is missing");
+
+ byte[] rawImage;
+ synchronized (mLock) {
+ try {
+ rawImage = mService.getImage(id);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ if (rawImage == null || rawImage.length == 0) return null;
+
+ return BitmapFactory.decodeByteArray(rawImage, 0, rawImage.length);
+ }
+
+ void dumpInfo(IndentingPrintWriter pw) {
+ pw.printf("RadioModule\n");
+
+ pw.increaseIndent();
+ synchronized (mLock) {
+ pw.printf("BroadcastRadioServiceImpl: %s\n", mService);
+ pw.printf("Properties: %s\n", mProperties);
+ pw.printf("Antenna state: ");
+ if (mAntennaConnected == null) {
+ pw.printf("undetermined\n");
+ } else {
+ pw.printf("%s\n", mAntennaConnected ? "connected" : "not connected");
+ }
+ pw.printf("current ProgramInfo: %s\n", mCurrentProgramInfo);
+ pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
+ pw.printf("Union of AIDL ProgramFilters: %s\n", mUnionOfAidlProgramFilters);
+ pw.printf("AIDL TunerSessions [%d]:\n", mAidlTunerSessions.size());
+
+ pw.increaseIndent();
+ for (int i = 0; i < mAidlTunerSessions.size(); i++) {
+ mAidlTunerSessions.valueAt(i).dumpInfo(pw);
+ }
+ pw.decreaseIndent();
+ }
+ pw.printf("Radio module events:\n");
+
+ pw.increaseIndent();
+ mLogger.dump(pw);
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
new file mode 100644
index 000000000000..7c26a8741a03
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/aidl/TunerSession.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.broadcastradio.aidl;
+
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.hardware.broadcastradio.ConfigFlag;
+import android.hardware.broadcastradio.IBroadcastRadio;
+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.os.RemoteException;
+import android.util.ArraySet;
+import android.util.IndentingPrintWriter;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.utils.Slogf;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+final class TunerSession extends ITuner.Stub {
+ private static final String TAG = "BcRadioAidlSrv.session";
+ private static final int TUNER_EVENT_LOGGER_QUEUE_SIZE = 25;
+
+ private final Object mLock;
+
+ private final RadioLogger mLogger;
+ private final RadioModule mModule;
+ final android.hardware.radio.ITunerCallback mCallback;
+ private final IBroadcastRadio mService;
+
+ @GuardedBy("mLock")
+ private boolean mIsClosed;
+ @GuardedBy("mLock")
+ private boolean mIsMuted;
+ @GuardedBy("mLock")
+ private ProgramInfoCache mProgramInfoCache;
+
+ // necessary only for older APIs compatibility
+ @GuardedBy("mLock")
+ private RadioManager.BandConfig mPlaceHolderConfig;
+
+ TunerSession(RadioModule radioModule, IBroadcastRadio service,
+ android.hardware.radio.ITunerCallback callback,
+ Object lock) {
+ mModule = Objects.requireNonNull(radioModule, "radioModule cannot be null");
+ mService = Objects.requireNonNull(service, "service cannot be null");
+ mCallback = Objects.requireNonNull(callback, "callback cannot be null");
+ mLock = Objects.requireNonNull(lock, "lock cannot be null");
+ mLogger = new RadioLogger(TAG, TUNER_EVENT_LOGGER_QUEUE_SIZE);
+ }
+
+ @Override
+ public void close() {
+ mLogger.logRadioEvent("Close tuner session");
+ close(null);
+ }
+
+ /**
+ * Closes the TunerSession. If error is non-null, the client's onError() callback is invoked
+ * first with the specified error, see {@link
+ * android.hardware.radio.RadioTuner.Callback#onError}.
+ *
+ * @param error Error to send to client before session is closed. If null, there is no error
+ * when closing the session.
+ */
+ public void close(@Nullable Integer error) {
+ if (error == null) {
+ mLogger.logRadioEvent("Close tuner session on error null");
+ } else {
+ mLogger.logRadioEvent("Close tuner session on error %d", error);
+ }
+ synchronized (mLock) {
+ if (mIsClosed) return;
+ if (error != null) {
+ try {
+ mCallback.onError(error);
+ } catch (RemoteException ex) {
+ Slogf.w(TAG, ex, "mCallback.onError(%s) failed", error);
+ }
+ }
+ mIsClosed = true;
+ mModule.onTunerSessionClosed(this);
+ }
+ }
+
+ @Override
+ public boolean isClosed() {
+ synchronized (mLock) {
+ return mIsClosed;
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void checkNotClosedLocked() {
+ if (mIsClosed) {
+ throw new IllegalStateException("Tuner is closed, no further operations are allowed");
+ }
+ }
+
+ @Override
+ public void setConfiguration(RadioManager.BandConfig config) {
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ mPlaceHolderConfig = Objects.requireNonNull(config, "config cannot be null");
+ }
+ Slogf.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL AIDL");
+ mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config));
+ }
+
+ @Override
+ public RadioManager.BandConfig getConfiguration() {
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ return mPlaceHolderConfig;
+ }
+ }
+
+ @Override
+ public void setMuted(boolean mute) {
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ if (mIsMuted == mute) return;
+ mIsMuted = mute;
+ }
+ Slogf.w(TAG, "Mute %b via RadioService is not implemented - please handle it via app",
+ mute);
+ }
+
+ @Override
+ public boolean isMuted() {
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ return mIsMuted;
+ }
+ }
+
+ @Override
+ public void step(boolean directionDown, boolean skipSubChannel) throws RemoteException {
+ mLogger.logRadioEvent("Step with direction %s, skipSubChannel? %s",
+ directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ try {
+ mService.step(!directionDown);
+ } catch (RuntimeException ex) {
+ throw ConversionUtils.throwOnError(ex, /* action= */ "step");
+ }
+ }
+ }
+
+ @Override
+ public void scan(boolean directionDown, boolean skipSubChannel) throws RemoteException {
+ mLogger.logRadioEvent("Scan with direction %s, skipSubChannel? %s",
+ directionDown ? "down" : "up", skipSubChannel ? "yes" : "no");
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ try {
+ mService.seek(!directionDown, skipSubChannel);
+ } catch (RuntimeException ex) {
+ throw ConversionUtils.throwOnError(ex, /* action= */ "seek");
+ }
+ }
+ }
+
+ @Override
+ public void tune(ProgramSelector selector) throws RemoteException {
+ mLogger.logRadioEvent("Tune with selector %s", selector);
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ try {
+ mService.tune(ConversionUtils.programSelectorToHalProgramSelector(selector));
+ } catch (RuntimeException ex) {
+ throw ConversionUtils.throwOnError(ex, /* action= */ "tune");
+ }
+ }
+ }
+
+ @Override
+ public void cancel() {
+ Slogf.i(TAG, "Cancel");
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ try {
+ mService.cancel();
+ } catch (RemoteException ex) {
+ Slogf.e(TAG, "Failed to cancel tuner session");
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ @Override
+ public void cancelAnnouncement() {
+ // TODO(b/244485175): deperacte cancelAnnouncement
+ Slogf.i(TAG, "Announcements control doesn't involve cancelling at the HAL level in AIDL");
+ }
+
+ @Override
+ public Bitmap getImage(int id) {
+ mLogger.logRadioEvent("Get image for %d", id);
+ return mModule.getImage(id);
+ }
+
+ @Override
+ public boolean startBackgroundScan() {
+ Slogf.i(TAG, "Explicit background scan trigger is not supported with HAL AIDL");
+ mModule.fanoutAidlCallback(ITunerCallback::onBackgroundScanComplete);
+ return true;
+ }
+
+ @Override
+ public void startProgramListUpdates(ProgramList.Filter filter) throws RemoteException {
+ mLogger.logRadioEvent("Start programList updates %s", filter);
+ // If the AIDL client provides a null filter, it wants all updates, so use the most broad
+ // filter.
+ if (filter == null) {
+ filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
+ /* includeCategories= */ true,
+ /* excludeModifications= */ false);
+ }
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ mProgramInfoCache = new ProgramInfoCache(filter);
+ }
+ // Note: RadioModule.onTunerSessionProgramListFilterChanged() must be called without mLock
+ // held since it can call getProgramListFilter() and onHalProgramInfoUpdated().
+ mModule.onTunerSessionProgramListFilterChanged(this);
+ }
+
+ ProgramList.Filter getProgramListFilter() {
+ synchronized (mLock) {
+ return mProgramInfoCache == null ? null : mProgramInfoCache.getFilter();
+ }
+ }
+
+ void onMergedProgramListUpdateFromHal(ProgramList.Chunk mergedChunk) {
+ List<ProgramList.Chunk> clientUpdateChunks;
+ synchronized (mLock) {
+ if (mProgramInfoCache == null) {
+ return;
+ }
+ clientUpdateChunks = mProgramInfoCache.filterAndApplyChunk(mergedChunk);
+ }
+ dispatchClientUpdateChunks(clientUpdateChunks);
+ }
+
+ void updateProgramInfoFromHalCache(ProgramInfoCache halCache) {
+ List<ProgramList.Chunk> clientUpdateChunks;
+ synchronized (mLock) {
+ if (mProgramInfoCache == null) {
+ return;
+ }
+ clientUpdateChunks = mProgramInfoCache.filterAndUpdateFromInternal(
+ halCache, /* purge = */ true);
+ }
+ dispatchClientUpdateChunks(clientUpdateChunks);
+ }
+
+ private void dispatchClientUpdateChunks(@Nullable List<ProgramList.Chunk> chunks) {
+ if (chunks == null) {
+ return;
+ }
+ for (int i = 0; i < chunks.size(); i++) {
+ try {
+ mCallback.onProgramListUpdated(chunks.get(i));
+ } catch (RemoteException ex) {
+ Slogf.w(TAG, ex, "mCallback.onProgramListUpdated() failed");
+ }
+ }
+ }
+
+ @Override
+ public void stopProgramListUpdates() throws RemoteException {
+ mLogger.logRadioEvent("Stop programList updates");
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ mProgramInfoCache = null;
+ }
+ // Note: RadioModule.onTunerSessionProgramListFilterChanged() must be called without mLock
+ // held since it can call getProgramListFilter() and onHalProgramInfoUpdated().
+ mModule.onTunerSessionProgramListFilterChanged(this);
+ }
+
+ @Override
+ public boolean isConfigFlagSupported(int flag) {
+ try {
+ isConfigFlagSet(flag);
+ return true;
+ } catch (IllegalStateException | UnsupportedOperationException ex) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean isConfigFlagSet(int flag) {
+ mLogger.logRadioEvent("is ConfigFlag %s set? ", ConfigFlag.$.toString(flag));
+ synchronized (mLock) {
+ checkNotClosedLocked();
+
+ try {
+ return mService.isConfigFlagSet(flag);
+ } catch (RuntimeException ex) {
+ throw ConversionUtils.throwOnError(ex, /* action= */ "isConfigFlagSet");
+ } catch (RemoteException ex) {
+ throw new RuntimeException("Failed to check flag " + ConfigFlag.$.toString(flag),
+ ex);
+ }
+ }
+ }
+
+ @Override
+ public void setConfigFlag(int flag, boolean value) throws RemoteException {
+ mLogger.logRadioEvent("set ConfigFlag %s to %b ",
+ ConfigFlag.$.toString(flag), value);
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ try {
+ mService.setConfigFlag(flag, value);
+ } catch (RuntimeException ex) {
+ throw ConversionUtils.throwOnError(ex, /* action= */ "setConfigFlag");
+ }
+ }
+ }
+
+ @Override
+ public Map<String, String> setParameters(Map<String, String> parameters) {
+ mLogger.logRadioEvent("Set parameters ");
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ try {
+ return ConversionUtils.vendorInfoFromHalVendorKeyValues(mService.setParameters(
+ ConversionUtils.vendorInfoToHalVendorKeyValues(parameters)));
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ @Override
+ public Map<String, String> getParameters(List<String> keys) {
+ mLogger.logRadioEvent("Get parameters ");
+ synchronized (mLock) {
+ checkNotClosedLocked();
+ try {
+ return ConversionUtils.vendorInfoFromHalVendorKeyValues(
+ mService.getParameters(keys.toArray(new String[0])));
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ void dumpInfo(IndentingPrintWriter pw) {
+ pw.printf("TunerSession\n");
+
+ pw.increaseIndent();
+ synchronized (mLock) {
+ pw.printf("Is session closed? %s\n", mIsClosed ? "Yes" : "No");
+ pw.printf("Is muted? %s\n", mIsMuted ? "Yes" : "No");
+ pw.printf("ProgramInfoCache: %s\n", mProgramInfoCache);
+ pw.printf("Config: %s\n", mPlaceHolderConfig);
+ }
+ pw.printf("Tuner session events:\n");
+
+ pw.increaseIndent();
+ mLogger.dump(pw);
+ pw.decreaseIndent();
+
+ pw.decreaseIndent();
+ }
+}
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/Utils.java b/services/core/java/com/android/server/broadcastradio/aidl/Utils.java
new file mode 100644
index 000000000000..b6bea5b18e28
--- /dev/null
+++ b/services/core/java/com/android/server/broadcastradio/aidl/Utils.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.broadcastradio.aidl;
+
+final class Utils {
+
+ private Utils() {
+ throw new UnsupportedOperationException("Utils class is noninstantiable");
+ }
+
+ public enum FrequencyBand {
+ UNKNOWN,
+ FM,
+ AM_LW,
+ AM_MW,
+ AM_SW,
+ }
+
+ static FrequencyBand getBand(int freq) {
+ // keep in sync with hardware/interfaces/broadcastradio/common/utilsaidl/Utils.cpp
+ if (freq < 30) return FrequencyBand.UNKNOWN;
+ if (freq < 500) return FrequencyBand.AM_LW;
+ if (freq < 1705) return FrequencyBand.AM_MW;
+ if (freq < 30000) return FrequencyBand.AM_SW;
+ if (freq < 60000) return FrequencyBand.UNKNOWN;
+ if (freq < 110000) return FrequencyBand.FM;
+ return FrequencyBand.UNKNOWN;
+ }
+
+}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 431be88bd892..6a53978175af 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -99,6 +99,7 @@ import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.FileUtils;
+import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
@@ -2784,26 +2785,18 @@ public class Vpn {
// When restricted to test networks, select any network with TRANSPORT_TEST. Since the
// creator of the profile and the test network creator both have MANAGE_TEST_NETWORKS,
// this is considered safe.
- final NetworkRequest req;
if (mProfile.isRestrictedToTestNetworks()) {
- req = new NetworkRequest.Builder()
+ final NetworkRequest req = new NetworkRequest.Builder()
.clearCapabilities()
.addTransportType(NetworkCapabilities.TRANSPORT_TEST)
.addCapability(NET_CAPABILITY_NOT_VPN)
.build();
+ mConnectivityManager.requestNetwork(req, mNetworkCallback);
} else {
- // Basically, the request here is referring to the default request which is defined
- // in ConnectivityService. Ideally, ConnectivityManager should provide an new API
- // which can provide the status of physical network even though there is a virtual
- // network. b/147280869 is used for tracking the new API.
- // TODO: Use the new API to register default physical network.
- req = new NetworkRequest.Builder()
- .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
- .build();
+ mConnectivityManager.registerSystemDefaultNetworkCallback(mNetworkCallback,
+ new Handler(mLooper));
}
-
- mConnectivityManager.requestNetwork(req, mNetworkCallback);
}
private boolean isActiveNetwork(@Nullable Network network) {
@@ -3217,41 +3210,44 @@ public class Vpn {
return;
}
- if (mSession != null && mMobikeEnabled) {
- Log.d(
- TAG,
- "IKE Session has mobility. Delay handleSessionLost for losing network "
- + network
- + " on session with token "
- + mCurrentToken);
-
- final int token = mCurrentToken;
- // Delay the teardown in case a new network will be available soon. For example,
- // during handover between two WiFi networks, Android will disconnect from the
- // first WiFi and then connects to the second WiFi.
- mScheduledHandleNetworkLostFuture =
- mExecutor.schedule(
- () -> {
- if (isActiveToken(token)) {
- handleSessionLost(null /* exception */, network);
- } else {
- Log.d(
- TAG,
- "Scheduled handleSessionLost fired for "
- + "obsolete token "
- + token);
+ Log.d(TAG, "Schedule a delay handleSessionLost for losing network "
+ + network
+ + " on session with token "
+ + mCurrentToken);
+
+ final int token = mCurrentToken;
+ // Delay the teardown in case a new network will be available soon. For example,
+ // during handover between two WiFi networks, Android will disconnect from the
+ // first WiFi and then connects to the second WiFi.
+ mScheduledHandleNetworkLostFuture =
+ mExecutor.schedule(
+ () -> {
+ if (isActiveToken(token)) {
+ handleSessionLost(new IkeNetworkLostException(network),
+ network);
+
+ synchronized (Vpn.this) {
+ // Ignore stale runner.
+ if (mVpnRunner != this) return;
+
+ updateState(DetailedState.DISCONNECTED,
+ "Network lost");
}
+ } else {
+ Log.d(
+ TAG,
+ "Scheduled handleSessionLost fired for "
+ + "obsolete token "
+ + token);
+ }
+
+ // Reset mScheduledHandleNetworkLostFuture since it's
+ // already run on executor thread.
+ mScheduledHandleNetworkLostFuture = null;
+ },
+ NETWORK_LOST_TIMEOUT_MS,
+ TimeUnit.MILLISECONDS);
- // Reset mScheduledHandleNetworkLostFuture since it's
- // already run on executor thread.
- mScheduledHandleNetworkLostFuture = null;
- },
- NETWORK_LOST_TIMEOUT_MS,
- TimeUnit.MILLISECONDS);
- } else {
- Log.d(TAG, "Call handleSessionLost for losing network " + network);
- handleSessionLost(null /* exception */, network);
- }
}
private void cancelHandleNetworkLostTimeout() {
@@ -4192,8 +4188,6 @@ public class Vpn {
*/
@NonNull
public synchronized List<String> getAppExclusionList(@NonNull String packageName) {
- enforceNotRestrictedUser();
-
final long oldId = Binder.clearCallingIdentity();
try {
final byte[] bytes = getVpnProfileStore().get(getVpnAppExcludedForPackage(packageName));
diff --git a/services/core/java/com/android/server/content/SyncStorageEngine.java b/services/core/java/com/android/server/content/SyncStorageEngine.java
index 2c6257f7cba6..5c679b873603 100644
--- a/services/core/java/com/android/server/content/SyncStorageEngine.java
+++ b/services/core/java/com/android/server/content/SyncStorageEngine.java
@@ -2083,7 +2083,7 @@ public class SyncStorageEngine {
try (FileInputStream in = mStatusFile.openRead()) {
readStatusInfoLocked(in);
}
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Unable to read status info file.", e);
}
}
@@ -2483,7 +2483,7 @@ public class SyncStorageEngine {
try (FileInputStream in = mStatisticsFile.openRead()) {
readDayStatsLocked(in);
}
- } catch (IOException e) {
+ } catch (Exception e) {
Slog.e(TAG, "Unable to read day stats file.", e);
}
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 8e00ccf68fb1..44c8e18a22cf 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -21,8 +21,11 @@ import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STA
import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
import static com.android.server.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
+import static com.android.server.devicestate.OverrideRequestController.STATUS_UNKNOWN;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -106,6 +109,8 @@ public final class DeviceStateManagerService extends SystemService {
private final BinderService mBinderService;
@NonNull
private final OverrideRequestController mOverrideRequestController;
+ @NonNull
+ private final DeviceStateProviderListener mDeviceStateProviderListener;
@VisibleForTesting
@NonNull
public ActivityTaskManagerInternal mActivityTaskManagerInternal;
@@ -139,6 +144,12 @@ public final class DeviceStateManagerService extends SystemService {
@NonNull
private Optional<OverrideRequest> mActiveOverride = Optional.empty();
+ // The current active base state override request. When set the device state specified here will
+ // replace the value in mBaseState.
+ @GuardedBy("mLock")
+ @NonNull
+ private Optional<OverrideRequest> mActiveBaseStateOverride = Optional.empty();
+
// List of processes registered to receive notifications about changes to device state and
// request status indexed by process id.
@GuardedBy("mLock")
@@ -177,7 +188,8 @@ public final class DeviceStateManagerService extends SystemService {
mOverrideRequestController = new OverrideRequestController(
this::onOverrideRequestStatusChangedLocked);
mDeviceStatePolicy = policy;
- mDeviceStatePolicy.getDeviceStateProvider().setListener(new DeviceStateProviderListener());
+ mDeviceStateProviderListener = new DeviceStateProviderListener();
+ mDeviceStatePolicy.getDeviceStateProvider().setListener(mDeviceStateProviderListener);
mBinderService = new BinderService();
mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
}
@@ -257,6 +269,21 @@ public final class DeviceStateManagerService extends SystemService {
}
}
+ /**
+ * Returns the current override base state, or {@link Optional#empty()} if no override state is
+ * requested. If an override base state is present, the returned state will be the same as
+ * the base state returned from {@link #getBaseState()}.
+ */
+ @NonNull
+ Optional<DeviceState> getOverrideBaseState() {
+ synchronized (mLock) {
+ if (mActiveBaseStateOverride.isPresent()) {
+ return getStateLocked(mActiveBaseStateOverride.get().getRequestedState());
+ }
+ return Optional.empty();
+ }
+ }
+
/** Returns the list of currently supported device states. */
DeviceState[] getSupportedStates() {
synchronized (mLock) {
@@ -366,6 +393,7 @@ public final class DeviceStateManagerService extends SystemService {
}
final DeviceState baseState = baseStateOptional.get();
+
if (mBaseState.isPresent() && mBaseState.get().equals(baseState)) {
// Base state hasn't changed. Nothing to do.
return;
@@ -375,7 +403,7 @@ public final class DeviceStateManagerService extends SystemService {
if (baseState.hasFlag(FLAG_CANCEL_OVERRIDE_REQUESTS)) {
mOverrideRequestController.cancelOverrideRequest();
}
- mOverrideRequestController.handleBaseStateChanged();
+ mOverrideRequestController.handleBaseStateChanged(identifier);
updatePendingStateLocked();
if (!mPendingState.isPresent()) {
@@ -528,16 +556,41 @@ public final class DeviceStateManagerService extends SystemService {
}
}
+ @GuardedBy("mLock")
private void onOverrideRequestStatusChangedLocked(@NonNull OverrideRequest request,
@OverrideRequestController.RequestStatus int status) {
- if (status == STATUS_ACTIVE) {
- mActiveOverride = Optional.of(request);
- } else if (status == STATUS_CANCELED) {
- if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
- mActiveOverride = Optional.empty();
+ if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_BASE_STATE) {
+ switch (status) {
+ case STATUS_ACTIVE:
+ enableBaseStateRequestLocked(request);
+ return;
+ case STATUS_CANCELED:
+ if (mActiveBaseStateOverride.isPresent()
+ && mActiveBaseStateOverride.get() == request) {
+ mActiveBaseStateOverride = Optional.empty();
+ }
+ break;
+ case STATUS_UNKNOWN: // same as default
+ default:
+ throw new IllegalArgumentException("Unknown request status: " + status);
+ }
+ } else if (request.getRequestType() == OVERRIDE_REQUEST_TYPE_EMULATED_STATE) {
+ switch (status) {
+ case STATUS_ACTIVE:
+ mActiveOverride = Optional.of(request);
+ break;
+ case STATUS_CANCELED:
+ if (mActiveOverride.isPresent() && mActiveOverride.get() == request) {
+ mActiveOverride = Optional.empty();
+ }
+ break;
+ case STATUS_UNKNOWN: // same as default
+ default:
+ throw new IllegalArgumentException("Unknown request status: " + status);
}
} else {
- throw new IllegalArgumentException("Unknown request status: " + status);
+ throw new IllegalArgumentException(
+ "Unknown OverrideRest type: " + request.getRequestType());
}
boolean updatedPendingState = updatePendingStateLocked();
@@ -564,6 +617,18 @@ public final class DeviceStateManagerService extends SystemService {
mHandler.post(this::notifyPolicyIfNeeded);
}
+ /**
+ * Sets the new base state of the device and notifies the process that made the base state
+ * override request that the request is now active.
+ */
+ @GuardedBy("mLock")
+ private void enableBaseStateRequestLocked(OverrideRequest request) {
+ setBaseState(request.getRequestedState());
+ mActiveBaseStateOverride = Optional.of(request);
+ ProcessRecord processRecord = mProcessRecords.get(request.getPid());
+ processRecord.notifyRequestActiveAsync(request.getToken());
+ }
+
private void registerProcess(int pid, IDeviceStateManagerCallback callback) {
synchronized (mLock) {
if (mProcessRecords.contains(pid)) {
@@ -606,7 +671,8 @@ public final class DeviceStateManagerService extends SystemService {
+ " has no registered callback.");
}
- if (mOverrideRequestController.hasRequest(token)) {
+ if (mOverrideRequestController.hasRequest(token,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE)) {
throw new IllegalStateException("Request has already been made for the supplied"
+ " token: " + token);
}
@@ -617,7 +683,8 @@ public final class DeviceStateManagerService extends SystemService {
+ " is not supported.");
}
- OverrideRequest request = new OverrideRequest(token, callingPid, state, flags);
+ OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
mOverrideRequestController.addRequest(request);
}
}
@@ -633,6 +700,44 @@ public final class DeviceStateManagerService extends SystemService {
}
}
+ private void requestBaseStateOverrideInternal(int state, int flags, int callingPid,
+ @NonNull IBinder token) {
+ synchronized (mLock) {
+ final Optional<DeviceState> deviceState = getStateLocked(state);
+ if (!deviceState.isPresent()) {
+ throw new IllegalArgumentException("Requested state: " + state
+ + " is not supported.");
+ }
+
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+
+ if (mOverrideRequestController.hasRequest(token,
+ OVERRIDE_REQUEST_TYPE_BASE_STATE)) {
+ throw new IllegalStateException("Request has already been made for the supplied"
+ + " token: " + token);
+ }
+
+ OverrideRequest request = new OverrideRequest(token, callingPid, state, flags,
+ OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ mOverrideRequestController.addBaseStateRequest(request);
+ }
+ }
+
+ private void cancelBaseStateOverrideInternal(int callingPid) {
+ synchronized (mLock) {
+ final ProcessRecord processRecord = mProcessRecords.get(callingPid);
+ if (processRecord == null) {
+ throw new IllegalStateException("Process " + callingPid
+ + " has no registered callback.");
+ }
+ setBaseState(mDeviceStateProviderListener.mCurrentBaseState);
+ }
+ }
+
private void dumpInternal(PrintWriter pw) {
pw.println("DEVICE STATE MANAGER (dumpsys device_state)");
@@ -729,6 +834,8 @@ public final class DeviceStateManagerService extends SystemService {
}
private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
+ @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int mCurrentBaseState;
+
@Override
public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates) {
if (newDeviceStates.length == 0) {
@@ -743,7 +850,7 @@ public final class DeviceStateManagerService extends SystemService {
if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) {
throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
-
+ mCurrentBaseState = identifier;
setBaseState(identifier);
}
}
@@ -895,6 +1002,38 @@ public final class DeviceStateManagerService extends SystemService {
}
@Override // Binder call
+ public void requestBaseStateOverride(IBinder token, int state, int flags) {
+ final int callingPid = Binder.getCallingPid();
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to control base state of device.");
+
+ if (token == null) {
+ throw new IllegalArgumentException("Request token must not be null.");
+ }
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ requestBaseStateOverrideInternal(state, flags, callingPid, token);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
+ public void cancelBaseStateOverride() {
+ final int callingPid = Binder.getCallingPid();
+ getContext().enforceCallingOrSelfPermission(CONTROL_DEVICE_STATE,
+ "Permission required to control base state of device.");
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ cancelBaseStateOverrideInternal(callingPid);
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+ }
+
+ @Override // Binder call
public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
String[] args, ShellCallback callback, ResultReceiver result) {
new DeviceStateManagerShellCommand(DeviceStateManagerService.this)
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
index 659ee75de453..8c6068d89296 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerShellCommand.java
@@ -16,11 +16,8 @@
package com.android.server.devicestate;
-import static android.Manifest.permission.CONTROL_DEVICE_STATE;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.Context;
import android.hardware.devicestate.DeviceStateManager;
import android.hardware.devicestate.DeviceStateRequest;
import android.os.Binder;
@@ -39,6 +36,8 @@ import java.util.stream.Collectors;
public class DeviceStateManagerShellCommand extends ShellCommand {
@Nullable
private static DeviceStateRequest sLastRequest;
+ @Nullable
+ private static DeviceStateRequest sLastBaseStateRequest;
private final DeviceStateManagerService mService;
private final DeviceStateManager mClient;
@@ -58,6 +57,8 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
switch (cmd) {
case "state":
return runState(pw);
+ case "base-state":
+ return runBaseState(pw);
case "print-state":
return runPrintState(pw);
case "print-states":
@@ -89,10 +90,6 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
return 0;
}
- final Context context = mService.getContext();
- context.enforceCallingOrSelfPermission(
- CONTROL_DEVICE_STATE,
- "Permission required to request device state.");
final long callingIdentity = Binder.clearCallingIdentity();
try {
if ("reset".equals(nextArg)) {
@@ -127,6 +124,47 @@ public class DeviceStateManagerShellCommand extends ShellCommand {
return 0;
}
+ private int runBaseState(PrintWriter pw) {
+ final String nextArg = getNextArg();
+ if (nextArg == null) {
+ printAllStates(pw);
+ return 0;
+ }
+
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ if ("reset".equals(nextArg)) {
+ if (sLastBaseStateRequest != null) {
+ mClient.cancelBaseStateOverride();
+ sLastBaseStateRequest = null;
+ }
+ } else {
+ int requestedState = Integer.parseInt(nextArg);
+ DeviceStateRequest request = DeviceStateRequest.newBuilder(requestedState).build();
+
+ mClient.requestBaseStateOverride(request, null, null);
+
+ sLastBaseStateRequest = request;
+ }
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: requested state should be an integer");
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println("Error: " + e.getMessage());
+ getErrPrintWriter().println("-------------------");
+ getErrPrintWriter().println("Run:");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println(" print-states");
+ getErrPrintWriter().println("");
+ getErrPrintWriter().println("to get the list of currently supported device states");
+ return -1;
+ } finally {
+ Binder.restoreCallingIdentity(callingIdentity);
+ }
+
+ return 0;
+ }
+
private int runPrintState(PrintWriter pw) {
Optional<DeviceState> deviceState = mService.getCommittedState();
if (deviceState.isPresent()) {
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequest.java b/services/core/java/com/android/server/devicestate/OverrideRequest.java
index 35a4c844c710..325e384cb8ad 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequest.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequest.java
@@ -16,9 +16,13 @@
package com.android.server.devicestate;
+import android.annotation.IntDef;
import android.hardware.devicestate.DeviceStateRequest;
import android.os.IBinder;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* A request to override the state managed by {@link DeviceStateManagerService}.
*
@@ -30,13 +34,47 @@ final class OverrideRequest {
private final int mRequestedState;
@DeviceStateRequest.RequestFlags
private final int mFlags;
+ @OverrideRequestType
+ private final int mRequestType;
+
+ /**
+ * Denotes that the request is meant to override the emulated state of the device. This will
+ * not change the base (physical) state of the device.
+ *
+ * This request type should be used if you are looking to emulate a device state for a feature
+ * but want the system to be aware of the physical state of the device.
+ */
+ public static final int OVERRIDE_REQUEST_TYPE_EMULATED_STATE = 0;
+
+ /**
+ * Denotes that the request is meant to override the base (physical) state of the device.
+ * Overriding the base state may not change the emulated state of the device if there is also an
+ * override request active for that property.
+ *
+ * This request type should only be used for testing, where you want to simulate the physical
+ * state of the device changing.
+ */
+ public static final int OVERRIDE_REQUEST_TYPE_BASE_STATE = 1;
+
+ /**
+ * Flags for signifying the type of {@link OverrideRequest}.
+ *
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "REQUEST_TYPE_" }, value = {
+ OVERRIDE_REQUEST_TYPE_BASE_STATE,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface OverrideRequestType {}
OverrideRequest(IBinder token, int pid, int requestedState,
- @DeviceStateRequest.RequestFlags int flags) {
+ @DeviceStateRequest.RequestFlags int flags, @OverrideRequestType int requestType) {
mToken = token;
mPid = pid;
mRequestedState = requestedState;
mFlags = flags;
+ mRequestType = requestType;
}
IBinder getToken() {
@@ -55,4 +93,9 @@ final class OverrideRequest {
int getFlags() {
return mFlags;
}
+
+ @OverrideRequestType
+ int getRequestType() {
+ return mRequestType;
+ }
}
diff --git a/services/core/java/com/android/server/devicestate/OverrideRequestController.java b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
index 01f5a09323cf..e39c7324d7bd 100644
--- a/services/core/java/com/android/server/devicestate/OverrideRequestController.java
+++ b/services/core/java/com/android/server/devicestate/OverrideRequestController.java
@@ -75,6 +75,8 @@ final class OverrideRequestController {
// Handle to the current override request, null if none.
private OverrideRequest mRequest;
+ // Handle to the current base state override request, null if none.
+ private OverrideRequest mBaseStateRequest;
private boolean mStickyRequestsAllowed;
// The current request has outlived their process.
@@ -111,13 +113,23 @@ final class OverrideRequestController {
}
}
+ void addBaseStateRequest(@NonNull OverrideRequest request) {
+ OverrideRequest previousRequest = mBaseStateRequest;
+ mBaseStateRequest = request;
+ mListener.onStatusChanged(request, STATUS_ACTIVE);
+
+ if (previousRequest != null) {
+ cancelRequestLocked(previousRequest);
+ }
+ }
+
/**
* Cancels the request with the specified {@code token} and notifies the listener of all changes
* to request status as a result of this operation.
*/
void cancelRequest(@NonNull OverrideRequest request) {
// Either don't have a current request or attempting to cancel an already cancelled request
- if (!hasRequest(request.getToken())) {
+ if (!hasRequest(request.getToken(), request.getRequestType())) {
return;
}
cancelCurrentRequestLocked();
@@ -144,11 +156,24 @@ final class OverrideRequestController {
}
/**
+ * Cancels the current base state override request, this could be due to the physical
+ * configuration of the device changing.
+ */
+ void cancelBaseStateOverrideRequest() {
+ cancelCurrentBaseStateRequestLocked();
+ }
+
+ /**
* Returns {@code true} if this controller is current managing a request with the specified
* {@code token}, {@code false} otherwise.
*/
- boolean hasRequest(@NonNull IBinder token) {
- return mRequest != null && token == mRequest.getToken();
+ boolean hasRequest(@NonNull IBinder token,
+ @OverrideRequest.OverrideRequestType int requestType) {
+ if (requestType == OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE) {
+ return mBaseStateRequest != null && token == mBaseStateRequest.getToken();
+ } else {
+ return mRequest != null && token == mRequest.getToken();
+ }
}
/**
@@ -157,11 +182,11 @@ final class OverrideRequestController {
* operation.
*/
void handleProcessDied(int pid) {
- if (mRequest == null) {
- return;
+ if (mBaseStateRequest != null && mBaseStateRequest.getPid() == pid) {
+ cancelCurrentBaseStateRequestLocked();
}
- if (mRequest.getPid() == pid) {
+ if (mRequest != null && mRequest.getPid() == pid) {
if (mStickyRequestsAllowed) {
// Do not cancel the requests now because sticky requests are allowed. These
// requests will be cancelled on a call to cancelStickyRequests().
@@ -176,7 +201,10 @@ final class OverrideRequestController {
* Notifies the controller that the base state has changed. The controller will notify the
* listener of all changes to request status as a result of this change.
*/
- void handleBaseStateChanged() {
+ void handleBaseStateChanged(int state) {
+ if (mBaseStateRequest != null && state != mBaseStateRequest.getRequestedState()) {
+ cancelBaseStateOverrideRequest();
+ }
if (mRequest == null) {
return;
}
@@ -192,11 +220,12 @@ final class OverrideRequestController {
* notify the listener of all changes to request status as a result of this change.
*/
void handleNewSupportedStates(int[] newSupportedStates) {
- if (mRequest == null) {
- return;
+ if (mBaseStateRequest != null && !contains(newSupportedStates,
+ mBaseStateRequest.getRequestedState())) {
+ cancelCurrentBaseStateRequestLocked();
}
- if (!contains(newSupportedStates, mRequest.getRequestedState())) {
+ if (mRequest != null && !contains(newSupportedStates, mRequest.getRequestedState())) {
cancelCurrentRequestLocked();
}
}
@@ -228,10 +257,23 @@ final class OverrideRequestController {
return;
}
mStickyRequest = false;
- mListener.onStatusChanged(mRequest, STATUS_CANCELED);
+ cancelRequestLocked(mRequest);
mRequest = null;
}
+ /**
+ * Handles cancelling {@code mBaseStateRequest}.
+ * Notifies the listener of the canceled status as well.
+ */
+ private void cancelCurrentBaseStateRequestLocked() {
+ if (mBaseStateRequest == null) {
+ Slog.w(TAG, "Attempted to cancel a null OverrideRequest");
+ return;
+ }
+ cancelRequestLocked(mBaseStateRequest);
+ mBaseStateRequest = null;
+ }
+
private static boolean contains(int[] array, int value) {
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 6145a91cf7cd..b3aee22547e5 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -131,6 +131,8 @@ class AutomaticBrightnessController {
// Configuration object for determining thresholds to change brightness dynamically
private final HysteresisLevels mAmbientBrightnessThresholds;
private final HysteresisLevels mScreenBrightnessThresholds;
+ private final HysteresisLevels mAmbientBrightnessThresholdsIdle;
+ private final HysteresisLevels mScreenBrightnessThresholdsIdle;
private boolean mLoggingEnabled;
@@ -242,7 +244,9 @@ class AutomaticBrightnessController {
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds, Context context,
+ HysteresisLevels screenBrightnessThresholds,
+ HysteresisLevels ambientBrightnessThresholdsIdle,
+ HysteresisLevels screenBrightnessThresholdsIdle, Context context,
HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
int ambientLightHorizonLong) {
@@ -251,7 +255,8 @@ class AutomaticBrightnessController {
lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
- ambientBrightnessThresholds, screenBrightnessThresholds, context,
+ ambientBrightnessThresholds, screenBrightnessThresholds,
+ ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context,
hbmController, brightnessThrottler, idleModeBrightnessMapper,
ambientLightHorizonShort, ambientLightHorizonLong
);
@@ -265,7 +270,9 @@ class AutomaticBrightnessController {
float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
- HysteresisLevels screenBrightnessThresholds, Context context,
+ HysteresisLevels screenBrightnessThresholds,
+ HysteresisLevels ambientBrightnessThresholdsIdle,
+ HysteresisLevels screenBrightnessThresholdsIdle, Context context,
HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
int ambientLightHorizonLong) {
@@ -289,7 +296,9 @@ class AutomaticBrightnessController {
mAmbientLightHorizonShort = ambientLightHorizonShort;
mWeightingIntercept = ambientLightHorizonLong;
mAmbientBrightnessThresholds = ambientBrightnessThresholds;
+ mAmbientBrightnessThresholdsIdle = ambientBrightnessThresholdsIdle;
mScreenBrightnessThresholds = screenBrightnessThresholds;
+ mScreenBrightnessThresholdsIdle = screenBrightnessThresholdsIdle;
mShortTermModelValid = true;
mShortTermModelAnchor = -1;
mHandler = new AutomaticBrightnessHandler(looper);
@@ -586,6 +595,8 @@ class AutomaticBrightnessController {
pw.println();
mAmbientBrightnessThresholds.dump(pw);
mScreenBrightnessThresholds.dump(pw);
+ mScreenBrightnessThresholdsIdle.dump(pw);
+ mScreenBrightnessThresholdsIdle.dump(pw);
}
private String configStateToString(int state) {
@@ -680,8 +691,17 @@ class AutomaticBrightnessController {
lux = 0;
}
mAmbientLux = lux;
- mAmbientBrighteningThreshold = mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
- mAmbientDarkeningThreshold = mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
+ if (isInIdleMode()) {
+ mAmbientBrighteningThreshold =
+ mAmbientBrightnessThresholdsIdle.getBrighteningThreshold(lux);
+ mAmbientDarkeningThreshold =
+ mAmbientBrightnessThresholdsIdle.getDarkeningThreshold(lux);
+ } else {
+ mAmbientBrighteningThreshold =
+ mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
+ mAmbientDarkeningThreshold =
+ mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
+ }
mHbmController.onAmbientLuxChange(mAmbientLux);
// If the short term model was invalidated and the change is drastic enough, reset it.
@@ -903,10 +923,20 @@ class AutomaticBrightnessController {
mPreThresholdBrightness = mScreenAutoBrightness;
}
mScreenAutoBrightness = newScreenAutoBrightness;
- mScreenBrighteningThreshold = clampScreenBrightness(
- mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
- mScreenDarkeningThreshold = clampScreenBrightness(
- mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
+ if (isInIdleMode()) {
+ mScreenBrighteningThreshold = clampScreenBrightness(
+ mScreenBrightnessThresholdsIdle.getBrighteningThreshold(
+ newScreenAutoBrightness));
+ mScreenDarkeningThreshold = clampScreenBrightness(
+ mScreenBrightnessThresholdsIdle.getDarkeningThreshold(
+ newScreenAutoBrightness));
+ } else {
+ mScreenBrighteningThreshold = clampScreenBrightness(
+ mScreenBrightnessThresholds.getBrighteningThreshold(
+ newScreenAutoBrightness));
+ mScreenDarkeningThreshold = clampScreenBrightness(
+ mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
+ }
if (sendUpdate) {
mCallbacks.updateBrightness();
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index cb04ddfd636d..372bc8ad94a1 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -31,7 +31,6 @@ import android.opengl.EGLDisplay;
import android.opengl.EGLSurface;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
-import android.os.IBinder;
import android.util.Slog;
import android.view.Display;
import android.view.DisplayInfo;
@@ -39,6 +38,7 @@ import android.view.Surface;
import android.view.Surface.OutOfResourcesException;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.window.ScreenCapture;
import com.android.server.LocalServices;
import com.android.server.policy.WindowManagerPolicy;
@@ -169,19 +169,11 @@ final class ColorFade {
mDisplayWidth = displayInfo.getNaturalWidth();
mDisplayHeight = displayInfo.getNaturalHeight();
- final IBinder token = SurfaceControl.getInternalDisplayToken();
- if (token == null) {
- Slog.e(TAG,
- "Failed to take screenshot because internal display is disconnected");
- return false;
- }
- final boolean isWideColor = SurfaceControl.getDynamicDisplayInfo(token).activeColorMode
- == Display.COLOR_MODE_DISPLAY_P3;
-
+ final boolean isWideColor = displayInfo.colorMode == Display.COLOR_MODE_DISPLAY_P3;
// Set mPrepared here so if initialization fails, resources can be cleaned up.
mPrepared = true;
- final SurfaceControl.ScreenshotHardwareBuffer hardwareBuffer = captureScreen();
+ final ScreenCapture.ScreenshotHardwareBuffer hardwareBuffer = captureScreen();
if (hardwareBuffer == null) {
dismiss();
return false;
@@ -508,7 +500,7 @@ final class ColorFade {
}
private boolean setScreenshotTextureAndSetViewport(
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer) {
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer) {
if (!attachEglContext()) {
return false;
}
@@ -559,8 +551,8 @@ final class ColorFade {
}
}
- private SurfaceControl.ScreenshotHardwareBuffer captureScreen() {
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ private ScreenCapture.ScreenshotHardwareBuffer captureScreen() {
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
mDisplayManagerInternal.systemScreenshot(mDisplayId);
if (screenshotBuffer == null) {
Slog.e(TAG, "Failed to take screenshot. Buffer is null");
diff --git a/services/core/java/com/android/server/display/DisplayControl.java b/services/core/java/com/android/server/display/DisplayControl.java
index e9640cf6b4d5..a060f076d4fb 100644
--- a/services/core/java/com/android/server/display/DisplayControl.java
+++ b/services/core/java/com/android/server/display/DisplayControl.java
@@ -16,6 +16,9 @@
package com.android.server.display;
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.os.IBinder;
import java.util.Objects;
@@ -26,6 +29,7 @@ import java.util.Objects;
public class DisplayControl {
private static native IBinder nativeCreateDisplay(String name, boolean secure);
private static native void nativeDestroyDisplay(IBinder displayToken);
+ private static native void nativeOverrideHdrTypes(IBinder displayToken, int[] modes);
/**
* Create a display in SurfaceFlinger.
@@ -52,4 +56,11 @@ public class DisplayControl {
nativeDestroyDisplay(displayToken);
}
+ /**
+ * Overrides HDR modes for a display device.
+ */
+ @RequiresPermission(Manifest.permission.ACCESS_SURFACE_FLINGER)
+ public static void overrideHdrTypes(@NonNull IBinder displayToken, @NonNull int[] modes) {
+ nativeOverrideHdrTypes(displayToken, modes);
+ }
}
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 12b2f4773387..416518613568 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -188,8 +188,8 @@ import javax.xml.datatype.DatatypeConfigurationException;
* <ambientLightHorizonLong>10001</ambientLightHorizonLong>
* <ambientLightHorizonShort>2001</ambientLightHorizonShort>
*
- * <displayBrightnessChangeThresholds>
- * <brighteningThresholds>
+ * <displayBrightnessChangeThresholds> // Thresholds for screen changes
+ * <brighteningThresholds> // Thresholds for active mode brightness changes.
* <minimum>0.001</minimum> // Minimum change needed in screen brightness to brighten.
* </brighteningThresholds>
* <darkeningThresholds>
@@ -197,8 +197,8 @@ import javax.xml.datatype.DatatypeConfigurationException;
* </darkeningThresholds>
* </displayBrightnessChangeThresholds>
*
- * <ambientBrightnessChangeThresholds>
- * <brighteningThresholds>
+ * <ambientBrightnessChangeThresholds> // Thresholds for lux changes
+ * <brighteningThresholds> // Thresholds for active mode brightness changes.
* <minimum>0.003</minimum> // Minimum change needed in ambient brightness to brighten.
* </brighteningThresholds>
* <darkeningThresholds>
@@ -206,6 +206,24 @@ import javax.xml.datatype.DatatypeConfigurationException;
* </darkeningThresholds>
* </ambientBrightnessChangeThresholds>
*
+ * <displayBrightnessChangeThresholdsIdle> // Thresholds for screen changes in idle mode
+ * <brighteningThresholds> // Thresholds for idle mode brightness changes.
+ * <minimum>0.001</minimum> // Minimum change needed in screen brightness to brighten.
+ * </brighteningThresholds>
+ * <darkeningThresholds>
+ * <minimum>0.002</minimum> // Minimum change needed in screen brightness to darken.
+ * </darkeningThresholds>
+ * </displayBrightnessChangeThresholdsIdle>
+ *
+ * <ambientBrightnessChangeThresholdsIdle> // Thresholds for lux changes in idle mode
+ * <brighteningThresholds> // Thresholds for idle mode brightness changes.
+ * <minimum>0.003</minimum> // Minimum change needed in ambient brightness to brighten.
+ * </brighteningThresholds>
+ * <darkeningThresholds>
+ * <minimum>0.004</minimum> // Minimum change needed in ambient brightness to darken.
+ * </darkeningThresholds>
+ * </ambientBrightnessChangeThresholdsIdle>
+ *
* </displayConfiguration>
* }
* </pre>
@@ -319,9 +337,13 @@ public class DisplayDeviceConfig {
private int mAmbientHorizonLong = AMBIENT_LIGHT_LONG_HORIZON_MILLIS;
private int mAmbientHorizonShort = AMBIENT_LIGHT_SHORT_HORIZON_MILLIS;
private float mScreenBrighteningMinThreshold = 0.0f; // Retain behaviour as though there is
- private float mScreenDarkeningMinThreshold = 0.0f; // no minimum threshold for change in
- private float mAmbientLuxBrighteningMinThreshold = 0.0f; // screen brightness or ambient
- private float mAmbientLuxDarkeningMinThreshold = 0.0f; // brightness.
+ private float mScreenBrighteningMinThresholdIdle = 0.0f; // no minimum threshold for change in
+ private float mScreenDarkeningMinThreshold = 0.0f; // screen brightness or ambient
+ private float mScreenDarkeningMinThresholdIdle = 0.0f; // brightness.
+ private float mAmbientLuxBrighteningMinThreshold = 0.0f;
+ private float mAmbientLuxBrighteningMinThresholdIdle = 0.0f;
+ private float mAmbientLuxDarkeningMinThreshold = 0.0f;
+ private float mAmbientLuxDarkeningMinThresholdIdle = 0.0f;
private Spline mBrightnessToBacklightSpline;
private Spline mBacklightToBrightnessSpline;
private Spline mBacklightToNitsSpline;
@@ -625,22 +647,76 @@ public class DisplayDeviceConfig {
return mAmbientHorizonShort;
}
+ /**
+ * The minimum value for the screen brightness increase to actually occur.
+ * @return float value in brightness scale of 0 - 1.
+ */
public float getScreenBrighteningMinThreshold() {
return mScreenBrighteningMinThreshold;
}
+ /**
+ * The minimum value for the screen brightness decrease to actually occur.
+ * @return float value in brightness scale of 0 - 1.
+ */
public float getScreenDarkeningMinThreshold() {
return mScreenDarkeningMinThreshold;
}
+ /**
+ * The minimum value for the screen brightness increase to actually occur while in idle screen
+ * brightness mode.
+ * @return float value in brightness scale of 0 - 1.
+ */
+ public float getScreenBrighteningMinThresholdIdle() {
+ return mScreenBrighteningMinThresholdIdle;
+ }
+
+ /**
+ * The minimum value for the screen brightness decrease to actually occur while in idle screen
+ * brightness mode.
+ * @return float value in brightness scale of 0 - 1.
+ */
+ public float getScreenDarkeningMinThresholdIdle() {
+ return mScreenDarkeningMinThresholdIdle;
+ }
+
+ /**
+ * The minimum value for the ambient lux increase for a screen brightness change to actually
+ * occur.
+ * @return float value in brightness scale of 0 - 1.
+ */
public float getAmbientLuxBrighteningMinThreshold() {
return mAmbientLuxBrighteningMinThreshold;
}
+ /**
+ * The minimum value for the ambient lux decrease for a screen brightness change to actually
+ * occur.
+ * @return float value in brightness scale of 0 - 1.
+ */
public float getAmbientLuxDarkeningMinThreshold() {
return mAmbientLuxDarkeningMinThreshold;
}
+ /**
+ * The minimum value for the ambient lux increase for a screen brightness change to actually
+ * occur while in idle screen brightness mode.
+ * @return float value in brightness scale of 0 - 1.
+ */
+ public float getAmbientLuxBrighteningMinThresholdIdle() {
+ return mAmbientLuxBrighteningMinThresholdIdle;
+ }
+
+ /**
+ * The minimum value for the ambient lux decrease for a screen brightness change to actually
+ * occur while in idle screen brightness mode.
+ * @return float value in brightness scale of 0 - 1.
+ */
+ public float getAmbientLuxDarkeningMinThresholdIdle() {
+ return mAmbientLuxDarkeningMinThresholdIdle;
+ }
+
SensorData getAmbientLightSensor() {
return mAmbientLightSensor;
}
@@ -745,9 +821,14 @@ public class DisplayDeviceConfig {
+ ", mAmbientHorizonLong=" + mAmbientHorizonLong
+ ", mAmbientHorizonShort=" + mAmbientHorizonShort
+ ", mScreenDarkeningMinThreshold=" + mScreenDarkeningMinThreshold
+ + ", mScreenDarkeningMinThresholdIdle=" + mScreenDarkeningMinThresholdIdle
+ ", mScreenBrighteningMinThreshold=" + mScreenBrighteningMinThreshold
+ + ", mScreenBrighteningMinThresholdIdle=" + mScreenBrighteningMinThresholdIdle
+ ", mAmbientLuxDarkeningMinThreshold=" + mAmbientLuxDarkeningMinThreshold
+ + ", mAmbientLuxDarkeningMinThresholdIdle=" + mAmbientLuxDarkeningMinThresholdIdle
+ ", mAmbientLuxBrighteningMinThreshold=" + mAmbientLuxBrighteningMinThreshold
+ + ", mAmbientLuxBrighteningMinThresholdIdle="
+ + mAmbientLuxBrighteningMinThresholdIdle
+ ", mAmbientLightSensor=" + mAmbientLightSensor
+ ", mProximitySensor=" + mProximitySensor
+ ", mRefreshRateLimitations= " + Arrays.toString(mRefreshRateLimitations.toArray())
@@ -1376,24 +1457,34 @@ public class DisplayDeviceConfig {
private void loadBrightnessChangeThresholds(DisplayConfiguration config) {
Thresholds displayBrightnessThresholds = config.getDisplayBrightnessChangeThresholds();
Thresholds ambientBrightnessThresholds = config.getAmbientBrightnessChangeThresholds();
+ Thresholds displayBrightnessThresholdsIdle =
+ config.getDisplayBrightnessChangeThresholdsIdle();
+ Thresholds ambientBrightnessThresholdsIdle =
+ config.getAmbientBrightnessChangeThresholdsIdle();
+ loadDisplayBrightnessThresholds(displayBrightnessThresholds);
+ loadAmbientBrightnessThresholds(ambientBrightnessThresholds);
+ loadIdleDisplayBrightnessThresholds(displayBrightnessThresholdsIdle);
+ loadIdleAmbientBrightnessThresholds(ambientBrightnessThresholdsIdle);
+ }
+
+ private void loadDisplayBrightnessThresholds(Thresholds displayBrightnessThresholds) {
if (displayBrightnessThresholds != null) {
BrightnessThresholds brighteningScreen =
displayBrightnessThresholds.getBrighteningThresholds();
BrightnessThresholds darkeningScreen =
displayBrightnessThresholds.getDarkeningThresholds();
- final BigDecimal screenBrighteningThreshold = brighteningScreen.getMinimum();
- final BigDecimal screenDarkeningThreshold = darkeningScreen.getMinimum();
-
- if (screenBrighteningThreshold != null) {
- mScreenBrighteningMinThreshold = screenBrighteningThreshold.floatValue();
+ if (brighteningScreen != null && brighteningScreen.getMinimum() != null) {
+ mScreenBrighteningMinThreshold = brighteningScreen.getMinimum().floatValue();
}
- if (screenDarkeningThreshold != null) {
- mScreenDarkeningMinThreshold = screenDarkeningThreshold.floatValue();
+ if (darkeningScreen != null && darkeningScreen.getMinimum() != null) {
+ mScreenDarkeningMinThreshold = darkeningScreen.getMinimum().floatValue();
}
}
+ }
+ private void loadAmbientBrightnessThresholds(Thresholds ambientBrightnessThresholds) {
if (ambientBrightnessThresholds != null) {
BrightnessThresholds brighteningAmbientLux =
ambientBrightnessThresholds.getBrighteningThresholds();
@@ -1412,6 +1503,44 @@ public class DisplayDeviceConfig {
}
}
+ private void loadIdleDisplayBrightnessThresholds(Thresholds idleDisplayBrightnessThresholds) {
+ if (idleDisplayBrightnessThresholds != null) {
+ BrightnessThresholds brighteningScreenIdle =
+ idleDisplayBrightnessThresholds.getBrighteningThresholds();
+ BrightnessThresholds darkeningScreenIdle =
+ idleDisplayBrightnessThresholds.getDarkeningThresholds();
+
+ if (brighteningScreenIdle != null
+ && brighteningScreenIdle.getMinimum() != null) {
+ mScreenBrighteningMinThresholdIdle =
+ brighteningScreenIdle.getMinimum().floatValue();
+ }
+ if (darkeningScreenIdle != null && darkeningScreenIdle.getMinimum() != null) {
+ mScreenDarkeningMinThresholdIdle =
+ darkeningScreenIdle.getMinimum().floatValue();
+ }
+ }
+ }
+
+ private void loadIdleAmbientBrightnessThresholds(Thresholds idleAmbientBrightnessThresholds) {
+ if (idleAmbientBrightnessThresholds != null) {
+ BrightnessThresholds brighteningAmbientLuxIdle =
+ idleAmbientBrightnessThresholds.getBrighteningThresholds();
+ BrightnessThresholds darkeningAmbientLuxIdle =
+ idleAmbientBrightnessThresholds.getDarkeningThresholds();
+
+ if (brighteningAmbientLuxIdle != null
+ && brighteningAmbientLuxIdle.getMinimum() != null) {
+ mAmbientLuxBrighteningMinThresholdIdle =
+ brighteningAmbientLuxIdle.getMinimum().floatValue();
+ }
+ if (darkeningAmbientLuxIdle != null && darkeningAmbientLuxIdle.getMinimum() != null) {
+ mAmbientLuxDarkeningMinThresholdIdle =
+ darkeningAmbientLuxIdle.getMinimum().floatValue();
+ }
+ }
+ }
+
private boolean thermalStatusIsValid(ThermalStatus value) {
if (value == null) {
return false;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index e53ac379536b..e851f03c92ef 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -35,6 +35,7 @@ import static android.hardware.display.DisplayManagerGlobal.DisplayEvent;
import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
+import static android.os.Process.ROOT_UID;
import android.Manifest;
import android.annotation.NonNull;
@@ -79,7 +80,6 @@ import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
import android.hardware.display.WifiDisplayStatus;
import android.hardware.graphics.common.DisplayDecorationSupport;
-import android.hardware.input.InputManagerInternal;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionManager;
import android.net.Uri;
@@ -118,6 +118,7 @@ import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
import android.window.DisplayWindowPolicyController;
+import android.window.ScreenCapture;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -132,6 +133,7 @@ import com.android.server.UiThread;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayDeviceConfig.SensorData;
import com.android.server.display.utils.SensorUtils;
+import com.android.server.input.InputManagerInternal;
import com.android.server.wm.SurfaceAnimationThread;
import com.android.server.wm.WindowManagerInternal;
@@ -200,6 +202,8 @@ public final class DisplayManagerService extends SystemService {
private static final String FORCE_WIFI_DISPLAY_ENABLE = "persist.debug.wfd.enable";
private static final String PROP_DEFAULT_DISPLAY_TOP_INSET = "persist.sys.displayinset.top";
+ private static final String PROP_USE_NEW_DISPLAY_POWER_CONTROLLER =
+ "persist.sys.use_new_display_power_controller";
private static final long WAIT_FOR_DEFAULT_DISPLAY_TIMEOUT = 10000;
// This value needs to be in sync with the threshold
// in RefreshRateConfigs::getFrameRateDivisor.
@@ -274,7 +278,7 @@ public final class DisplayManagerService extends SystemService {
new CopyOnWriteArrayList<>();
/** All {@link DisplayPowerController}s indexed by {@link LogicalDisplay} ID. */
- private final SparseArray<DisplayPowerController> mDisplayPowerControllers =
+ private final SparseArray<DisplayPowerControllerInterface> mDisplayPowerControllers =
new SparseArray<>();
/** {@link DisplayBlanker} used by all {@link DisplayPowerController}s. */
@@ -497,7 +501,6 @@ public final class DisplayManagerService extends SystemService {
ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
mWideColorSpace = colorSpaces[1];
mAllowNonNativeRefreshRateOverride = mInjector.getAllowNonNativeRefreshRateOverride();
-
mSystemReady = false;
}
@@ -577,7 +580,7 @@ public final class DisplayManagerService extends SystemService {
if (logicalDisplay.getDisplayInfoLocked().type != Display.TYPE_INTERNAL) {
return;
}
- final DisplayPowerController dpc = mDisplayPowerControllers.get(
+ final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(
logicalDisplay.getDisplayIdLocked());
if (dpc == null) {
return;
@@ -1170,6 +1173,10 @@ public final class DisplayManagerService extends SystemService {
}
private boolean validatePackageName(int uid, String packageName) {
+ // Root doesn't have a package name.
+ if (uid == ROOT_UID) {
+ return true;
+ }
if (packageName != null) {
String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
if (packageNames != null) {
@@ -1557,7 +1564,7 @@ public final class DisplayManagerService extends SystemService {
scheduleTraversalLocked(false);
mPersistentDataStore.saveIfNeeded();
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.onDisplayChanged();
}
@@ -1575,7 +1582,8 @@ public final class DisplayManagerService extends SystemService {
private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
- final DisplayPowerController dpc = mDisplayPowerControllers.removeReturnOld(displayId);
+ final DisplayPowerControllerInterface dpc =
+ mDisplayPowerControllers.removeReturnOld(displayId);
if (dpc != null) {
dpc.stop();
}
@@ -1604,7 +1612,7 @@ public final class DisplayManagerService extends SystemService {
mHandler.post(work);
}
final int displayId = display.getDisplayIdLocked();
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.onDisplayChanged();
}
@@ -1615,7 +1623,7 @@ public final class DisplayManagerService extends SystemService {
private void handleLogicalDisplayDeviceStateTransitionLocked(@NonNull LogicalDisplay display) {
final int displayId = display.getDisplayIdLocked();
- final DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.onDeviceStateTransition();
}
@@ -1852,14 +1860,14 @@ public final class DisplayManagerService extends SystemService {
if (userId != mCurrentUserId) {
return;
}
- DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
+ DisplayPowerControllerInterface dpc = getDpcFromUniqueIdLocked(uniqueId);
if (dpc != null) {
dpc.setBrightnessConfiguration(c);
}
}
}
- private DisplayPowerController getDpcFromUniqueIdLocked(String uniqueId) {
+ private DisplayPowerControllerInterface getDpcFromUniqueIdLocked(String uniqueId) {
final DisplayDevice displayDevice = mDisplayDeviceRepo.getByUniqueIdLocked(uniqueId);
final LogicalDisplay logicalDisplay = mLogicalDisplayMapper.getDisplayLocked(displayDevice);
if (logicalDisplay != null) {
@@ -1900,7 +1908,7 @@ public final class DisplayManagerService extends SystemService {
final BrightnessConfiguration config =
getBrightnessConfigForDisplayWithPdsFallbackLocked(uniqueId, userSerial);
if (config != null) {
- final DisplayPowerController dpc = mDisplayPowerControllers.get(
+ final DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(
logicalDisplay.getDisplayIdLocked());
if (dpc != null) {
dpc.setBrightnessConfiguration(config);
@@ -2049,8 +2057,8 @@ public final class DisplayManagerService extends SystemService {
return null;
}
- private SurfaceControl.ScreenshotHardwareBuffer systemScreenshotInternal(int displayId) {
- final SurfaceControl.DisplayCaptureArgs captureArgs;
+ private ScreenCapture.ScreenshotHardwareBuffer systemScreenshotInternal(int displayId) {
+ final ScreenCapture.DisplayCaptureArgs captureArgs;
synchronized (mSyncRoot) {
final IBinder token = getDisplayToken(displayId);
if (token == null) {
@@ -2062,27 +2070,27 @@ public final class DisplayManagerService extends SystemService {
}
final DisplayInfo displayInfo = logicalDisplay.getDisplayInfoLocked();
- captureArgs = new SurfaceControl.DisplayCaptureArgs.Builder(token)
+ captureArgs = new ScreenCapture.DisplayCaptureArgs.Builder(token)
.setSize(displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight())
.setUseIdentityTransform(true)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
.build();
}
- return SurfaceControl.captureDisplay(captureArgs);
+ return ScreenCapture.captureDisplay(captureArgs);
}
- private SurfaceControl.ScreenshotHardwareBuffer userScreenshotInternal(int displayId) {
+ private ScreenCapture.ScreenshotHardwareBuffer userScreenshotInternal(int displayId) {
synchronized (mSyncRoot) {
final IBinder token = getDisplayToken(displayId);
if (token == null) {
return null;
}
- final SurfaceControl.DisplayCaptureArgs captureArgs =
- new SurfaceControl.DisplayCaptureArgs.Builder(token)
+ final ScreenCapture.DisplayCaptureArgs captureArgs =
+ new ScreenCapture.DisplayCaptureArgs.Builder(token)
.build();
- return SurfaceControl.captureDisplay(captureArgs);
+ return ScreenCapture.captureDisplay(captureArgs);
}
}
@@ -2132,8 +2140,8 @@ public final class DisplayManagerService extends SystemService {
void setAutoBrightnessLoggingEnabled(boolean enabled) {
synchronized (mSyncRoot) {
- final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
- Display.DEFAULT_DISPLAY);
+ final DisplayPowerControllerInterface displayPowerController =
+ mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setAutoBrightnessLoggingEnabled(enabled);
}
@@ -2142,8 +2150,8 @@ public final class DisplayManagerService extends SystemService {
void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
synchronized (mSyncRoot) {
- final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
- Display.DEFAULT_DISPLAY);
+ final DisplayPowerControllerInterface displayPowerController =
+ mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setDisplayWhiteBalanceLoggingEnabled(enabled);
}
@@ -2170,8 +2178,8 @@ public final class DisplayManagerService extends SystemService {
void setAmbientColorTemperatureOverride(float cct) {
synchronized (mSyncRoot) {
- final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
- Display.DEFAULT_DISPLAY);
+ final DisplayPowerControllerInterface displayPowerController =
+ mDisplayPowerControllers.get(Display.DEFAULT_DISPLAY);
if (displayPowerController != null) {
displayPowerController.setAmbientColorTemperatureOverride(cct);
}
@@ -2180,8 +2188,8 @@ public final class DisplayManagerService extends SystemService {
void setDockedAndIdleEnabled(boolean enabled, int displayId) {
synchronized (mSyncRoot) {
- final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
- displayId);
+ final DisplayPowerControllerInterface displayPowerController =
+ mDisplayPowerControllers.get(displayId);
if (displayPowerController != null) {
displayPowerController.setAutomaticScreenBrightnessMode(enabled);
}
@@ -2554,10 +2562,19 @@ public final class DisplayManagerService extends SystemService {
final BrightnessSetting brightnessSetting = new BrightnessSetting(mPersistentDataStore,
display, mSyncRoot);
- final DisplayPowerController displayPowerController = new DisplayPowerController(
- mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
- mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
- () -> handleBrightnessChange(display));
+ final DisplayPowerControllerInterface displayPowerController;
+
+ if (SystemProperties.getInt(PROP_USE_NEW_DISPLAY_POWER_CONTROLLER, 0) == 1) {
+ displayPowerController = new DisplayPowerController2(
+ mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+ mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+ () -> handleBrightnessChange(display));
+ } else {
+ displayPowerController = new DisplayPowerController(
+ mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
+ mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
+ () -> handleBrightnessChange(display));
+ }
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
}
@@ -2983,6 +3000,19 @@ public final class DisplayManagerService extends SystemService {
}
}
+ @Override
+ public void overrideHdrTypes(int displayId, int[] modes) {
+ IBinder displayToken;
+ synchronized (mSyncRoot) {
+ displayToken = getDisplayToken(displayId);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("Invalid display: " + displayId);
+ }
+ }
+
+ DisplayControl.overrideHdrTypes(displayToken, modes);
+ }
+
@Override // Binder call
public void setAreUserDisabledHdrTypesAllowed(boolean areUserDisabledHdrTypesAllowed) {
mContext.enforceCallingOrSelfPermission(
@@ -3212,7 +3242,7 @@ public final class DisplayManagerService extends SystemService {
uniqueId, userSerial);
if (config == null) {
// Get default configuration
- DisplayPowerController dpc = getDpcFromUniqueIdLocked(uniqueId);
+ DisplayPowerControllerInterface dpc = getDpcFromUniqueIdLocked(uniqueId);
if (dpc != null) {
config = dpc.getDefaultBrightnessConfiguration();
}
@@ -3263,7 +3293,7 @@ public final class DisplayManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
return dpc.getBrightnessInfo();
}
@@ -3310,7 +3340,7 @@ public final class DisplayManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
dpc.setBrightness(brightness);
}
@@ -3330,7 +3360,7 @@ public final class DisplayManagerService extends SystemService {
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
- DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+ DisplayPowerControllerInterface dpc = mDisplayPowerControllers.get(displayId);
if (dpc != null) {
brightness = dpc.getScreenBrightnessSetting();
}
@@ -3476,6 +3506,17 @@ public final class DisplayManagerService extends SystemService {
Binder.restoreCallingIdentity(token);
}
}
+
+ @Override
+ public void setDisplayIdToMirror(IBinder token, int displayId) {
+ synchronized (mSyncRoot) {
+ final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId);
+ if (mVirtualDisplayAdapter != null) {
+ mVirtualDisplayAdapter.setDisplayIdToMirror(token,
+ display == null ? Display.INVALID_DISPLAY : displayId);
+ }
+ }
+ }
}
private static boolean isValidBrightness(float brightness) {
@@ -3534,7 +3575,7 @@ public final class DisplayManagerService extends SystemService {
id).getPrimaryDisplayDeviceLocked();
final int flags = displayDevice.getDisplayDeviceInfoLocked().flags;
if ((flags & DisplayDeviceInfo.FLAG_NEVER_BLANK) == 0) {
- final DisplayPowerController displayPowerController =
+ final DisplayPowerControllerInterface displayPowerController =
mDisplayPowerControllers.get(id);
ready &= displayPowerController.requestPowerState(request,
waitForNegativeProximity);
@@ -3564,12 +3605,12 @@ public final class DisplayManagerService extends SystemService {
}
@Override
- public SurfaceControl.ScreenshotHardwareBuffer systemScreenshot(int displayId) {
+ public ScreenCapture.ScreenshotHardwareBuffer systemScreenshot(int displayId) {
return systemScreenshotInternal(displayId);
}
@Override
- public SurfaceControl.ScreenshotHardwareBuffer userScreenshot(int displayId) {
+ public ScreenCapture.ScreenshotHardwareBuffer userScreenshot(int displayId) {
return userScreenshotInternal(displayId);
}
@@ -3856,6 +3897,19 @@ public final class DisplayManagerService extends SystemService {
return displayIdToMirror;
}
}
+
+ @Override
+ public SurfaceControl.DisplayPrimaries getDisplayNativePrimaries(int displayId) {
+ IBinder displayToken;
+ synchronized (mSyncRoot) {
+ displayToken = getDisplayToken(displayId);
+ if (displayToken == null) {
+ throw new IllegalArgumentException("Invalid displayId=" + displayId);
+ }
+ }
+
+ return SurfaceControl.getDisplayNativePrimaries(displayToken);
+ }
}
class DesiredDisplayModeSpecsObserver
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 247635017539..8d3e040bf4e6 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -104,7 +104,7 @@ import java.io.PrintWriter;
* slower by changing the "animator duration scale" option in Development Settings.
*/
final class DisplayPowerController implements AutomaticBrightnessController.Callbacks,
- DisplayWhiteBalanceController.Callbacks {
+ DisplayWhiteBalanceController.Callbacks, DisplayPowerControllerInterface {
private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
@@ -682,6 +682,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
/**
* Returns true if the proximity sensor screen-off function is available.
*/
+ @Override
public boolean isProximitySensorAvailable() {
return mProximitySensor != null;
}
@@ -693,6 +694,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
* @param includePackage if false will null out the package name in events
*/
@Nullable
+ @Override
public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
@UserIdInt int userId, boolean includePackage) {
if (mBrightnessTracker == null) {
@@ -701,6 +703,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
return mBrightnessTracker.getEvents(userId, includePackage);
}
+ @Override
public void onSwitchUser(@UserIdInt int newUserId) {
handleSettingsChange(true /* userSwitch */);
if (mBrightnessTracker != null) {
@@ -709,6 +712,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
@Nullable
+ @Override
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@UserIdInt int userId) {
if (mBrightnessTracker == null) {
@@ -720,6 +724,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
/**
* Persist the brightness slider events and ambient brightness stats to disk.
*/
+ @Override
public void persistBrightnessTrackerState() {
if (mBrightnessTracker != null) {
mBrightnessTracker.persistBrightnessTrackerState();
@@ -781,6 +786,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
+ @Override
public BrightnessConfiguration getDefaultBrightnessConfiguration() {
if (mAutomaticBrightnessController == null) {
return null;
@@ -792,8 +798,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
* Notified when the display is changed. We use this to apply any changes that might be needed
* when displays get swapped on foldable devices. For example, different brightness properties
* of each display need to be properly reflected in AutomaticBrightnessController.
+ *
+ * Make sure DisplayManagerService.mSyncRoot is held when this is called
*/
- @GuardedBy("DisplayManagerService.mSyncRoot")
+ @Override
public void onDisplayChanged() {
final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
if (device == null) {
@@ -823,6 +831,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
* This process involves turning off some displays so we need updatePowerState() to run and
* calculate the new state.
*/
+ @Override
public void onDeviceStateTransition() {
sendUpdatePowerState();
}
@@ -833,6 +842,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
* This method should be called when the DisplayPowerController is no longer in use; i.e. when
* the {@link #mDisplayId display} has been removed.
*/
+ @Override
public void stop() {
synchronized (mLock) {
mStopped = true;
@@ -979,8 +989,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
com.android.internal.R.array.config_screenBrighteningThresholds);
int[] screenDarkeningThresholds = resources.getIntArray(
com.android.internal.R.array.config_screenDarkeningThresholds);
- int[] screenThresholdLevels = resources.getIntArray(
- com.android.internal.R.array.config_screenThresholdLevels);
+ float[] screenThresholdLevels = BrightnessMappingStrategy.getFloatArray(resources
+ .obtainTypedArray(com.android.internal.R.array.config_screenThresholdLevels));
float screenDarkeningMinThreshold =
mDisplayDeviceConfig.getScreenDarkeningMinThreshold();
float screenBrighteningMinThreshold =
@@ -989,6 +999,25 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
screenDarkeningMinThreshold, screenBrighteningMinThreshold);
+ // Idle screen thresholds
+ float screenDarkeningMinThresholdIdle =
+ mDisplayDeviceConfig.getScreenDarkeningMinThresholdIdle();
+ float screenBrighteningMinThresholdIdle =
+ mDisplayDeviceConfig.getScreenBrighteningMinThresholdIdle();
+ HysteresisLevels screenBrightnessThresholdsIdle = new HysteresisLevels(
+ screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
+ screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle);
+
+ // Idle ambient thresholds
+ float ambientDarkeningMinThresholdIdle =
+ mDisplayDeviceConfig.getAmbientLuxDarkeningMinThresholdIdle();
+ float ambientBrighteningMinThresholdIdle =
+ mDisplayDeviceConfig.getAmbientLuxBrighteningMinThresholdIdle();
+ HysteresisLevels ambientBrightnessThresholdsIdle = new HysteresisLevels(
+ ambientBrighteningThresholds, ambientDarkeningThresholds,
+ ambientThresholdLevels, ambientDarkeningMinThresholdIdle,
+ ambientBrighteningMinThresholdIdle);
+
long brighteningLightDebounce = mDisplayDeviceConfig
.getAutoBrightnessBrighteningLightDebounce();
long darkeningLightDebounce = mDisplayDeviceConfig
@@ -1024,7 +1053,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
- ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
+ ambientBrightnessThresholds, screenBrightnessThresholds,
+ ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper,
mDisplayDeviceConfig.getAmbientHorizonShort(),
mDisplayDeviceConfig.getAmbientHorizonLong());
@@ -1063,6 +1093,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
+ @Override
public void setAutomaticScreenBrightnessMode(boolean isIdle) {
if (mAutomaticBrightnessController != null) {
if (isIdle) {
@@ -1567,9 +1598,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
// TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
// done in HighBrightnessModeController.
if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
- && ((mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
- || (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
- == 0)) {
+ && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
+ && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
+ == 0) {
// We want to scale HDR brightness level with the SDR level
animateValue = mHbmController.getHdrBrightnessValue();
}
@@ -1766,27 +1797,32 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
* Ignores the proximity sensor until the sensor state changes, but only if the sensor is
* currently enabled and forcing the screen to be dark.
*/
+ @Override
public void ignoreProximitySensorUntilChanged() {
mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY);
}
+ @Override
public void setBrightnessConfiguration(BrightnessConfiguration c) {
Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c);
msg.sendToTarget();
}
+ @Override
public void setTemporaryBrightness(float brightness) {
Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
Float.floatToIntBits(brightness), 0 /*unused*/);
msg.sendToTarget();
}
+ @Override
public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT,
Float.floatToIntBits(adjustment), 0 /*unused*/);
msg.sendToTarget();
}
+ @Override
public BrightnessInfo getBrightnessInfo() {
synchronized (mCachedBrightnessInfo) {
return new BrightnessInfo(
@@ -1861,7 +1897,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
},
() -> {
- sendUpdatePowerStateLocked();
+ sendUpdatePowerState();
postBrightnessChangeRunnable();
// TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
if (mAutomaticBrightnessController != null) {
@@ -1877,7 +1913,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
return new BrightnessThrottler(mHandler, data,
() -> {
- sendUpdatePowerStateLocked();
+ sendUpdatePowerState();
postBrightnessChangeRunnable();
}, mUniqueDisplayId);
}
@@ -2347,7 +2383,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
}
- float getScreenBrightnessSetting() {
+ @Override
+ public float getScreenBrightnessSetting() {
float brightness = mBrightnessSetting.getBrightness();
if (Float.isNaN(brightness)) {
brightness = mScreenBrightnessDefault;
@@ -2362,7 +2399,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
return clampScreenBrightnessForVr(brightnessFloat);
}
- void setBrightness(float brightnessValue) {
+ @Override
+ public void setBrightness(float brightnessValue) {
// Update the setting, which will eventually call back into DPC to have us actually update
// the display with the new value.
mBrightnessSetting.setBrightness(brightnessValue);
@@ -2511,6 +2549,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
};
+ @Override
public void dump(final PrintWriter pw) {
synchronized (mLock) {
pw.println();
@@ -2919,7 +2958,8 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
}
}
- void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ @Override
+ public void setAutoBrightnessLoggingEnabled(boolean enabled) {
if (mAutomaticBrightnessController != null) {
mAutomaticBrightnessController.setLoggingEnabled(enabled);
}
@@ -2930,14 +2970,16 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call
sendUpdatePowerState();
}
- void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+ @Override
+ public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.setLoggingEnabled(enabled);
mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled);
}
}
- void setAmbientColorTemperatureOverride(float cct) {
+ @Override
+ public void setAmbientColorTemperatureOverride(float cct) {
if (mDisplayWhiteBalanceController != null) {
mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct);
// The ambient color temperature override is only applied when the ambient color
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
new file mode 100644
index 000000000000..c734095a4743
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -0,0 +1,3071 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.animation.Animator;
+import android.animation.ObjectAnimator;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.pm.ParceledListSlice;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.metrics.LogMaker;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.FloatProperty;
+import android.util.Log;
+import android.util.MathUtils;
+import android.util.MutableFloat;
+import android.util.MutableInt;
+import android.util.Slog;
+import android.util.TimeUtils;
+import android.view.Display;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IBatteryStats;
+import com.android.internal.display.BrightnessSynchronizer;
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.internal.util.RingBuffer;
+import com.android.server.LocalServices;
+import com.android.server.am.BatteryStatsService;
+import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.display.brightness.BrightnessEvent;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
+import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
+import com.android.server.display.utils.SensorUtils;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings;
+import com.android.server.policy.WindowManagerPolicy;
+
+import java.io.PrintWriter;
+
+/**
+ * Controls the power state of the display.
+ *
+ * Handles the proximity sensor, light sensor, and animations between states
+ * including the screen off animation.
+ *
+ * This component acts independently of the rest of the power manager service.
+ * In particular, it does not share any state and it only communicates
+ * via asynchronous callbacks to inform the power manager that something has
+ * changed.
+ *
+ * Everything this class does internally is serialized on its handler although
+ * it may be accessed by other threads from the outside.
+ *
+ * Note that the power manager service guarantees that it will hold a suspend
+ * blocker as long as the display is not ready. So most of the work done here
+ * does not need to worry about holding a suspend blocker unless it happens
+ * independently of the display ready signal.
+ *
+ * For debugging, you can make the color fade and brightness animations run
+ * slower by changing the "animator duration scale" option in Development Settings.
+ */
+final class DisplayPowerController2 implements AutomaticBrightnessController.Callbacks,
+ DisplayWhiteBalanceController.Callbacks, DisplayPowerControllerInterface {
+ private static final String SCREEN_ON_BLOCKED_TRACE_NAME = "Screen on blocked";
+ private static final String SCREEN_OFF_BLOCKED_TRACE_NAME = "Screen off blocked";
+
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT = false;
+
+ // If true, uses the color fade on animation.
+ // We might want to turn this off if we cannot get a guarantee that the screen
+ // actually turns on and starts showing new content after the call to set the
+ // screen state returns. Playing the animation can also be somewhat slow.
+ private static final boolean USE_COLOR_FADE_ON_ANIMATION = false;
+
+ private static final float SCREEN_ANIMATION_RATE_MINIMUM = 0.0f;
+
+ private static final int COLOR_FADE_ON_ANIMATION_DURATION_MILLIS = 250;
+ private static final int COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS = 400;
+
+ private static final int MSG_UPDATE_POWER_STATE = 1;
+ private static final int MSG_PROXIMITY_SENSOR_DEBOUNCED = 2;
+ private static final int MSG_SCREEN_ON_UNBLOCKED = 3;
+ private static final int MSG_SCREEN_OFF_UNBLOCKED = 4;
+ private static final int MSG_CONFIGURE_BRIGHTNESS = 5;
+ private static final int MSG_SET_TEMPORARY_BRIGHTNESS = 6;
+ private static final int MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT = 7;
+ private static final int MSG_IGNORE_PROXIMITY = 8;
+ private static final int MSG_STOP = 9;
+ private static final int MSG_UPDATE_BRIGHTNESS = 10;
+ private static final int MSG_UPDATE_RBC = 11;
+ private static final int MSG_BRIGHTNESS_RAMP_DONE = 12;
+ private static final int MSG_STATSD_HBM_BRIGHTNESS = 13;
+
+ private static final int PROXIMITY_UNKNOWN = -1;
+ private static final int PROXIMITY_NEGATIVE = 0;
+ private static final int PROXIMITY_POSITIVE = 1;
+
+ // Proximity sensor debounce delay in milliseconds for positive or negative transitions.
+ private static final int PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY = 0;
+ private static final int PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY = 250;
+
+ private static final int BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS = 500;
+
+ // Trigger proximity if distance is less than 5 cm.
+ private static final float TYPICAL_PROXIMITY_THRESHOLD = 5.0f;
+
+ // State machine constants for tracking initial brightness ramp skipping when enabled.
+ private static final int RAMP_STATE_SKIP_NONE = 0;
+ private static final int RAMP_STATE_SKIP_INITIAL = 1;
+ private static final int RAMP_STATE_SKIP_AUTOBRIGHT = 2;
+
+ private static final int REPORTED_TO_POLICY_UNREPORTED = -1;
+ private static final int REPORTED_TO_POLICY_SCREEN_OFF = 0;
+ private static final int REPORTED_TO_POLICY_SCREEN_TURNING_ON = 1;
+ private static final int REPORTED_TO_POLICY_SCREEN_ON = 2;
+ private static final int REPORTED_TO_POLICY_SCREEN_TURNING_OFF = 3;
+
+ private static final int RINGBUFFER_MAX = 100;
+
+ private final String mTag;
+
+ private final Object mLock = new Object();
+
+ private final Context mContext;
+
+ // Our handler.
+ private final DisplayControllerHandler mHandler;
+
+ // Asynchronous callbacks into the power manager service.
+ // Only invoked from the handler thread while no locks are held.
+ private final DisplayPowerCallbacks mCallbacks;
+
+ // Battery stats.
+ @Nullable
+ private final IBatteryStats mBatteryStats;
+
+ // The sensor manager.
+ private final SensorManager mSensorManager;
+
+ // The window manager policy.
+ private final WindowManagerPolicy mWindowManagerPolicy;
+
+ // The display blanker.
+ private final DisplayBlanker mBlanker;
+
+ // The LogicalDisplay tied to this DisplayPowerController2.
+ private final LogicalDisplay mLogicalDisplay;
+
+ // The ID of the LogicalDisplay tied to this DisplayPowerController2.
+ private final int mDisplayId;
+
+ // The unique ID of the primary display device currently tied to this logical display
+ private String mUniqueDisplayId;
+
+ // Tracker for brightness changes.
+ @Nullable
+ private final BrightnessTracker mBrightnessTracker;
+
+ // Tracker for brightness settings changes.
+ private final SettingsObserver mSettingsObserver;
+
+ // The proximity sensor, or null if not available or needed.
+ private Sensor mProximitySensor;
+
+ // The doze screen brightness.
+ private final float mScreenBrightnessDozeConfig;
+
+ // The dim screen brightness.
+ private final float mScreenBrightnessDimConfig;
+
+ // The minimum dim amount to use if the screen brightness is already below
+ // mScreenBrightnessDimConfig.
+ private final float mScreenBrightnessMinimumDimAmount;
+
+ private final float mScreenBrightnessDefault;
+
+ // The minimum allowed brightness while in VR.
+ private final float mScreenBrightnessForVrRangeMinimum;
+
+ // The maximum allowed brightness while in VR.
+ private final float mScreenBrightnessForVrRangeMaximum;
+
+ // The default screen brightness for VR.
+ private final float mScreenBrightnessForVrDefault;
+
+ // True if auto-brightness should be used.
+ private boolean mUseSoftwareAutoBrightnessConfig;
+
+ // True if should use light sensor to automatically determine doze screen brightness.
+ private final boolean mAllowAutoBrightnessWhileDozingConfig;
+
+ // Whether or not the color fade on screen on / off is enabled.
+ private final boolean mColorFadeEnabled;
+
+ @GuardedBy("mCachedBrightnessInfo")
+ private final CachedBrightnessInfo mCachedBrightnessInfo = new CachedBrightnessInfo();
+
+ private DisplayDevice mDisplayDevice;
+
+ // True if we should fade the screen while turning it off, false if we should play
+ // a stylish color fade animation instead.
+ private final boolean mColorFadeFadesConfig;
+
+ // True if we need to fake a transition to off when coming out of a doze state.
+ // Some display hardware will blank itself when coming out of doze in order to hide
+ // artifacts. For these displays we fake a transition into OFF so that policy can appropriately
+ // blank itself and begin an appropriate power on animation.
+ private final boolean mDisplayBlanksAfterDozeConfig;
+
+ // True if there are only buckets of brightness values when the display is in the doze state,
+ // rather than a full range of values. If this is true, then we'll avoid animating the screen
+ // brightness since it'd likely be multiple jarring brightness transitions instead of just one
+ // to reach the final state.
+ private final boolean mBrightnessBucketsInDozeConfig;
+
+ private final Clock mClock;
+ private final Injector mInjector;
+
+ // Maximum time a ramp animation can take.
+ private long mBrightnessRampIncreaseMaxTimeMillis;
+ private long mBrightnessRampDecreaseMaxTimeMillis;
+
+ // The pending power request.
+ // Initially null until the first call to requestPowerState.
+ @GuardedBy("mLock")
+ private DisplayPowerRequest mPendingRequestLocked;
+
+ // True if a request has been made to wait for the proximity sensor to go negative.
+ @GuardedBy("mLock")
+ private boolean mPendingWaitForNegativeProximityLocked;
+
+ // True if the pending power request or wait for negative proximity flag
+ // has been changed since the last update occurred.
+ @GuardedBy("mLock")
+ private boolean mPendingRequestChangedLocked;
+
+ // Set to true when the important parts of the pending power request have been applied.
+ // The important parts are mainly the screen state. Brightness changes may occur
+ // concurrently.
+ @GuardedBy("mLock")
+ private boolean mDisplayReadyLocked;
+
+ // Set to true if a power state update is required.
+ @GuardedBy("mLock")
+ private boolean mPendingUpdatePowerStateLocked;
+
+ /* The following state must only be accessed by the handler thread. */
+
+ // The currently requested power state.
+ // The power controller will progressively update its internal state to match
+ // the requested power state. Initially null until the first update.
+ private DisplayPowerRequest mPowerRequest;
+
+ // The current power state.
+ // Must only be accessed on the handler thread.
+ private DisplayPowerState mPowerState;
+
+ // True if the device should wait for negative proximity sensor before
+ // waking up the screen. This is set to false as soon as a negative
+ // proximity sensor measurement is observed or when the device is forced to
+ // go to sleep by the user. While true, the screen remains off.
+ private boolean mWaitingForNegativeProximity;
+
+ // True if the device should not take into account the proximity sensor
+ // until either the proximity sensor state changes, or there is no longer a
+ // request to listen to proximity sensor.
+ private boolean mIgnoreProximityUntilChanged;
+
+ // The actual proximity sensor threshold value.
+ private float mProximityThreshold;
+
+ // Set to true if the proximity sensor listener has been registered
+ // with the sensor manager.
+ private boolean mProximitySensorEnabled;
+
+ // The debounced proximity sensor state.
+ private int mProximity = PROXIMITY_UNKNOWN;
+
+ // The raw non-debounced proximity sensor state.
+ private int mPendingProximity = PROXIMITY_UNKNOWN;
+ private long mPendingProximityDebounceTime = -1; // -1 if fully debounced
+
+ // True if the screen was turned off because of the proximity sensor.
+ // When the screen turns on again, we report user activity to the power manager.
+ private boolean mScreenOffBecauseOfProximity;
+
+ // The currently active screen on unblocker. This field is non-null whenever
+ // we are waiting for a callback to release it and unblock the screen.
+ private ScreenOnUnblocker mPendingScreenOnUnblocker;
+ private ScreenOffUnblocker mPendingScreenOffUnblocker;
+
+ // True if we were in the process of turning off the screen.
+ // This allows us to recover more gracefully from situations where we abort
+ // turning off the screen.
+ private boolean mPendingScreenOff;
+
+ // True if we have unfinished business and are holding a suspend blocker.
+ private boolean mUnfinishedBusiness;
+
+ // The elapsed real time when the screen on was blocked.
+ private long mScreenOnBlockStartRealTime;
+ private long mScreenOffBlockStartRealTime;
+
+ // Screen state we reported to policy. Must be one of REPORTED_TO_POLICY_* fields.
+ private int mReportedScreenStateToPolicy = REPORTED_TO_POLICY_UNREPORTED;
+
+ // If the last recorded screen state was dozing or not.
+ private boolean mDozing;
+
+ // Remembers whether certain kinds of brightness adjustments
+ // were recently applied so that we can decide how to transition.
+ private boolean mAppliedAutoBrightness;
+ private boolean mAppliedDimming;
+ private boolean mAppliedLowPower;
+ private boolean mAppliedScreenBrightnessOverride;
+ private boolean mAppliedTemporaryBrightness;
+ private boolean mAppliedTemporaryAutoBrightnessAdjustment;
+ private boolean mAppliedBrightnessBoost;
+ private boolean mAppliedThrottling;
+
+ // Reason for which the brightness was last changed. See {@link BrightnessReason} for more
+ // information.
+ // At the time of this writing, this value is changed within updatePowerState() only, which is
+ // limited to the thread used by DisplayControllerHandler.
+ private final BrightnessReason mBrightnessReason = new BrightnessReason();
+ private final BrightnessReason mBrightnessReasonTemp = new BrightnessReason();
+
+ // Brightness animation ramp rates in brightness units per second
+ private float mBrightnessRampRateFastDecrease;
+ private float mBrightnessRampRateFastIncrease;
+ private float mBrightnessRampRateSlowDecrease;
+ private float mBrightnessRampRateSlowIncrease;
+
+ // Report HBM brightness change to StatsD
+ private int mDisplayStatsId;
+ private float mLastStatsBrightness = PowerManager.BRIGHTNESS_MIN;
+
+ // Whether or not to skip the initial brightness ramps into STATE_ON.
+ private final boolean mSkipScreenOnBrightnessRamp;
+
+ // Display white balance components.
+ @Nullable
+ private final DisplayWhiteBalanceSettings mDisplayWhiteBalanceSettings;
+ @Nullable
+ private final DisplayWhiteBalanceController mDisplayWhiteBalanceController;
+
+ @Nullable
+ private final ColorDisplayServiceInternal mCdsi;
+ private float[] mNitsRange;
+
+ private final HighBrightnessModeController mHbmController;
+
+ private final BrightnessThrottler mBrightnessThrottler;
+
+ private final BrightnessSetting mBrightnessSetting;
+
+ private final Runnable mOnBrightnessChangeRunnable;
+
+ private final BrightnessEvent mLastBrightnessEvent;
+ private final BrightnessEvent mTempBrightnessEvent;
+
+ // Keeps a record of brightness changes for dumpsys.
+ private RingBuffer<BrightnessEvent> mBrightnessEventRingBuffer;
+
+ // A record of state for skipping brightness ramps.
+ private int mSkipRampState = RAMP_STATE_SKIP_NONE;
+
+ // The first autobrightness value set when entering RAMP_STATE_SKIP_INITIAL.
+ private float mInitialAutoBrightness;
+
+ // The controller for the automatic brightness level.
+ @Nullable
+ private AutomaticBrightnessController mAutomaticBrightnessController;
+
+ private Sensor mLightSensor;
+
+ // The mappers between ambient lux, display backlight values, and display brightness.
+ // We will switch between the idle mapper and active mapper in AutomaticBrightnessController.
+ // Mapper used for active (normal) screen brightness mode
+ @Nullable
+ private BrightnessMappingStrategy mInteractiveModeBrightnessMapper;
+ // Mapper used for idle screen brightness mode
+ @Nullable
+ private BrightnessMappingStrategy mIdleModeBrightnessMapper;
+
+ // The current brightness configuration.
+ @Nullable
+ private BrightnessConfiguration mBrightnessConfiguration;
+
+ // The last brightness that was set by the user and not temporary. Set to
+ // PowerManager.BRIGHTNESS_INVALID_FLOAT when a brightness has yet to be recorded.
+ private float mLastUserSetScreenBrightness = Float.NaN;
+
+ // The screen brightness setting has changed but not taken effect yet. If this is different
+ // from the current screen brightness setting then this is coming from something other than us
+ // and should be considered a user interaction.
+ private float mPendingScreenBrightnessSetting;
+
+ // The last observed screen brightness setting, either set by us or by the settings app on
+ // behalf of the user.
+ private float mCurrentScreenBrightnessSetting;
+
+ // The temporary screen brightness. Typically set when a user is interacting with the
+ // brightness slider but hasn't settled on a choice yet. Set to
+ // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary brightness set.
+ private float mTemporaryScreenBrightness;
+
+ // The current screen brightness while in VR mode.
+ private float mScreenBrightnessForVr;
+
+ // The last auto brightness adjustment that was set by the user and not temporary. Set to
+ // Float.NaN when an auto-brightness adjustment hasn't been recorded yet.
+ private float mAutoBrightnessAdjustment;
+
+ // The pending auto brightness adjustment that will take effect on the next power state update.
+ private float mPendingAutoBrightnessAdjustment;
+
+ // The temporary auto brightness adjustment. Typically set when a user is interacting with the
+ // adjustment slider but hasn't settled on a choice yet. Set to
+ // PowerManager.BRIGHTNESS_INVALID_FLOAT when there's no temporary adjustment set.
+ private float mTemporaryAutoBrightnessAdjustment;
+
+ private boolean mIsRbcActive;
+
+ // Whether there's a callback to tell listeners the display has changed scheduled to run. When
+ // true it implies a wakelock is being held to guarantee the update happens before we collapse
+ // into suspend and so needs to be cleaned up if the thread is exiting.
+ // Should only be accessed on the Handler thread.
+ private boolean mOnStateChangedPending;
+
+ // Count of proximity messages currently on this DPC's Handler. Used to keep track of how many
+ // suspend blocker acquisitions are pending when shutting down this DPC.
+ // Should only be accessed on the Handler thread.
+ private int mOnProximityPositiveMessages;
+ private int mOnProximityNegativeMessages;
+
+ // Animators.
+ private ObjectAnimator mColorFadeOnAnimator;
+ private ObjectAnimator mColorFadeOffAnimator;
+ private DualRampAnimator<DisplayPowerState> mScreenBrightnessRampAnimator;
+ private BrightnessSetting.BrightnessSettingListener mBrightnessSettingListener;
+
+ // True if this DisplayPowerController2 has been stopped and should no longer be running.
+ private boolean mStopped;
+
+ private DisplayDeviceConfig mDisplayDeviceConfig;
+
+ // Identifiers for suspend blocker acuisition requests
+ private final String mSuspendBlockerIdUnfinishedBusiness;
+ private final String mSuspendBlockerIdOnStateChanged;
+ private final String mSuspendBlockerIdProxPositive;
+ private final String mSuspendBlockerIdProxNegative;
+ private final String mSuspendBlockerIdProxDebounce;
+
+ /**
+ * Creates the display power controller.
+ */
+ DisplayPowerController2(Context context, Injector injector,
+ DisplayPowerCallbacks callbacks, Handler handler,
+ SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay,
+ BrightnessTracker brightnessTracker, BrightnessSetting brightnessSetting,
+ Runnable onBrightnessChangeRunnable) {
+
+ mInjector = injector != null ? injector : new Injector();
+ mClock = mInjector.getClock();
+ mLogicalDisplay = logicalDisplay;
+ mDisplayId = mLogicalDisplay.getDisplayIdLocked();
+ mTag = "DisplayPowerController2[" + mDisplayId + "]";
+ mSuspendBlockerIdUnfinishedBusiness = getSuspendBlockerUnfinishedBusinessId(mDisplayId);
+ mSuspendBlockerIdOnStateChanged = getSuspendBlockerOnStateChangedId(mDisplayId);
+ mSuspendBlockerIdProxPositive = getSuspendBlockerProxPositiveId(mDisplayId);
+ mSuspendBlockerIdProxNegative = getSuspendBlockerProxNegativeId(mDisplayId);
+ mSuspendBlockerIdProxDebounce = getSuspendBlockerProxDebounceId(mDisplayId);
+
+ mDisplayDevice = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+ mUniqueDisplayId = logicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+ mDisplayStatsId = mUniqueDisplayId.hashCode();
+ mHandler = new DisplayControllerHandler(handler.getLooper());
+ mLastBrightnessEvent = new BrightnessEvent(mDisplayId);
+ mTempBrightnessEvent = new BrightnessEvent(mDisplayId);
+
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ mBatteryStats = BatteryStatsService.getService();
+ } else {
+ mBatteryStats = null;
+ }
+
+ mSettingsObserver = new SettingsObserver(mHandler);
+ mCallbacks = callbacks;
+ mSensorManager = sensorManager;
+ mWindowManagerPolicy = LocalServices.getService(WindowManagerPolicy.class);
+ mBlanker = blanker;
+ mContext = context;
+ mBrightnessTracker = brightnessTracker;
+ // TODO: b/186428377 update brightness setting when display changes
+ mBrightnessSetting = brightnessSetting;
+ mOnBrightnessChangeRunnable = onBrightnessChangeRunnable;
+
+ PowerManager pm = context.getSystemService(PowerManager.class);
+
+ final Resources resources = context.getResources();
+
+ // DOZE AND DIM SETTINGS
+ mScreenBrightnessDozeConfig = clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DOZE));
+ mScreenBrightnessDimConfig = clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DIM));
+ mScreenBrightnessMinimumDimAmount = resources.getFloat(
+ R.dimen.config_screenBrightnessMinimumDimAmountFloat);
+
+
+ // NORMAL SCREEN SETTINGS
+ mScreenBrightnessDefault = clampAbsoluteBrightness(
+ mLogicalDisplay.getDisplayInfoLocked().brightnessDefault);
+
+ // VR SETTINGS
+ mScreenBrightnessForVrDefault = clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR));
+ mScreenBrightnessForVrRangeMaximum = clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM_VR));
+ mScreenBrightnessForVrRangeMinimum = clampAbsoluteBrightness(
+ pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR));
+
+ // Check the setting, but also verify that it is the default display. Only the default
+ // display has an automatic brightness controller running.
+ // TODO: b/179021925 - Fix to work with multiple displays
+ mUseSoftwareAutoBrightnessConfig = resources.getBoolean(
+ R.bool.config_automatic_brightness_available)
+ && mDisplayId == Display.DEFAULT_DISPLAY;
+
+ mAllowAutoBrightnessWhileDozingConfig = resources.getBoolean(
+ R.bool.config_allowAutoBrightnessWhileDozing);
+
+ mDisplayDeviceConfig = logicalDisplay.getPrimaryDisplayDeviceLocked()
+ .getDisplayDeviceConfig();
+
+ loadBrightnessRampRates();
+ mSkipScreenOnBrightnessRamp = resources.getBoolean(
+ R.bool.config_skipScreenOnBrightnessRamp);
+
+ mHbmController = createHbmControllerLocked();
+
+ mBrightnessThrottler = createBrightnessThrottlerLocked();
+
+ // Seed the cached brightness
+ saveBrightnessInfo(getScreenBrightnessSetting());
+
+ DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null;
+ DisplayWhiteBalanceController displayWhiteBalanceController = null;
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ try {
+ displayWhiteBalanceSettings = new DisplayWhiteBalanceSettings(mContext, mHandler);
+ displayWhiteBalanceController = DisplayWhiteBalanceFactory.create(mHandler,
+ mSensorManager, resources);
+ displayWhiteBalanceSettings.setCallbacks(this);
+ displayWhiteBalanceController.setCallbacks(this);
+ } catch (Exception e) {
+ Slog.e(mTag, "failed to set up display white-balance: " + e);
+ }
+ }
+ mDisplayWhiteBalanceSettings = displayWhiteBalanceSettings;
+ mDisplayWhiteBalanceController = displayWhiteBalanceController;
+
+ loadNitsRange(resources);
+
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ mCdsi = LocalServices.getService(ColorDisplayServiceInternal.class);
+ boolean active = mCdsi.setReduceBrightColorsListener(new ReduceBrightColorsListener() {
+ @Override
+ public void onReduceBrightColorsActivationChanged(boolean activated,
+ boolean userInitiated) {
+ applyReduceBrightColorsSplineAdjustment();
+
+ }
+
+ @Override
+ public void onReduceBrightColorsStrengthChanged(int strength) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ });
+ if (active) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ } else {
+ mCdsi = null;
+ }
+
+ setUpAutoBrightness(resources, handler);
+
+ mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic();
+ mColorFadeFadesConfig = resources.getBoolean(
+ R.bool.config_animateScreenLights);
+
+ mDisplayBlanksAfterDozeConfig = resources.getBoolean(
+ R.bool.config_displayBlanksAfterDoze);
+
+ mBrightnessBucketsInDozeConfig = resources.getBoolean(
+ R.bool.config_displayBrightnessBucketsInDoze);
+
+ loadProximitySensor();
+
+ mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
+ mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
+ mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
+ mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
+ }
+
+ private void applyReduceBrightColorsSplineAdjustment() {
+ mHandler.obtainMessage(MSG_UPDATE_RBC).sendToTarget();
+ sendUpdatePowerState();
+ }
+
+ private void handleRbcChanged() {
+ if (mAutomaticBrightnessController == null) {
+ return;
+ }
+ if ((!mAutomaticBrightnessController.isInIdleMode()
+ && mInteractiveModeBrightnessMapper == null)
+ || (mAutomaticBrightnessController.isInIdleMode()
+ && mIdleModeBrightnessMapper == null)) {
+ Log.w(mTag, "No brightness mapping available to recalculate splines for this mode");
+ return;
+ }
+
+ float[] adjustedNits = new float[mNitsRange.length];
+ for (int i = 0; i < mNitsRange.length; i++) {
+ adjustedNits[i] = mCdsi.getReduceBrightColorsAdjustedBrightnessNits(mNitsRange[i]);
+ }
+ mIsRbcActive = mCdsi.isReduceBrightColorsActivated();
+ mAutomaticBrightnessController.recalculateSplines(mIsRbcActive, adjustedNits);
+ }
+
+ /**
+ * Returns true if the proximity sensor screen-off function is available.
+ */
+ @Override
+ public boolean isProximitySensorAvailable() {
+ return mProximitySensor != null;
+ }
+
+ /**
+ * Get the {@link BrightnessChangeEvent}s for the specified user.
+ *
+ * @param userId userId to fetch data for
+ * @param includePackage if false will null out the package name in events
+ */
+ @Nullable
+ @Override
+ public ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(
+ @UserIdInt int userId, boolean includePackage) {
+ if (mBrightnessTracker == null) {
+ return null;
+ }
+ return mBrightnessTracker.getEvents(userId, includePackage);
+ }
+
+ @Override
+ public void onSwitchUser(@UserIdInt int newUserId) {
+ handleSettingsChange(true /* userSwitch */);
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.onSwitchUser(newUserId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
+ @UserIdInt int userId) {
+ if (mBrightnessTracker == null) {
+ return null;
+ }
+ return mBrightnessTracker.getAmbientBrightnessStats(userId);
+ }
+
+ /**
+ * Persist the brightness slider events and ambient brightness stats to disk.
+ */
+ @Override
+ public void persistBrightnessTrackerState() {
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.persistBrightnessTrackerState();
+ }
+ }
+
+ /**
+ * Requests a new power state.
+ * The controller makes a copy of the provided object and then
+ * begins adjusting the power state to match what was requested.
+ *
+ * @param request The requested power state.
+ * @param waitForNegativeProximity If true, issues a request to wait for
+ * negative proximity before turning the screen back on,
+ * assuming the screen
+ * was turned off by the proximity sensor.
+ * @return True if display is ready, false if there are important changes that must
+ * be made asynchronously (such as turning the screen on), in which case the caller
+ * should grab a wake lock, watch for {@link DisplayPowerCallbacks#onStateChanged()}
+ * then try the request again later until the state converges.
+ */
+ public boolean requestPowerState(DisplayPowerRequest request,
+ boolean waitForNegativeProximity) {
+ if (DEBUG) {
+ Slog.d(mTag, "requestPowerState: "
+ + request + ", waitForNegativeProximity=" + waitForNegativeProximity);
+ }
+
+ synchronized (mLock) {
+ if (mStopped) {
+ return true;
+ }
+
+ boolean changed = false;
+
+ if (waitForNegativeProximity
+ && !mPendingWaitForNegativeProximityLocked) {
+ mPendingWaitForNegativeProximityLocked = true;
+ changed = true;
+ }
+
+ if (mPendingRequestLocked == null) {
+ mPendingRequestLocked = new DisplayPowerRequest(request);
+ changed = true;
+ } else if (!mPendingRequestLocked.equals(request)) {
+ mPendingRequestLocked.copyFrom(request);
+ changed = true;
+ }
+
+ if (changed) {
+ mDisplayReadyLocked = false;
+ if (!mPendingRequestChangedLocked) {
+ mPendingRequestChangedLocked = true;
+ sendUpdatePowerStateLocked();
+ }
+ }
+
+ return mDisplayReadyLocked;
+ }
+ }
+
+ @Override
+ public BrightnessConfiguration getDefaultBrightnessConfiguration() {
+ if (mAutomaticBrightnessController == null) {
+ return null;
+ }
+ return mAutomaticBrightnessController.getDefaultConfig();
+ }
+
+ /**
+ * Notified when the display is changed. We use this to apply any changes that might be needed
+ * when displays get swapped on foldable devices. For example, different brightness properties
+ * of each display need to be properly reflected in AutomaticBrightnessController.
+ *
+ * Make sure DisplayManagerService.mSyncRoot lock is held when this is called
+ */
+ @Override
+ public void onDisplayChanged() {
+ final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+ if (device == null) {
+ Slog.wtf(mTag, "Display Device is null in DisplayPowerController2 for display: "
+ + mLogicalDisplay.getDisplayIdLocked());
+ return;
+ }
+
+ final String uniqueId = device.getUniqueId();
+ final DisplayDeviceConfig config = device.getDisplayDeviceConfig();
+ final IBinder token = device.getDisplayTokenLocked();
+ final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ mHandler.post(() -> {
+ if (mDisplayDevice != device) {
+ mDisplayDevice = device;
+ mUniqueDisplayId = uniqueId;
+ mDisplayStatsId = mUniqueDisplayId.hashCode();
+ mDisplayDeviceConfig = config;
+ loadFromDisplayDeviceConfig(token, info);
+ updatePowerState();
+ }
+ });
+ }
+
+ /**
+ * Called when the displays are preparing to transition from one device state to another.
+ * This process involves turning off some displays so we need updatePowerState() to run and
+ * calculate the new state.
+ */
+ @Override
+ public void onDeviceStateTransition() {
+ sendUpdatePowerState();
+ }
+
+ /**
+ * Unregisters all listeners and interrupts all running threads; halting future work.
+ *
+ * This method should be called when the DisplayPowerController2 is no longer in use; i.e. when
+ * the {@link #mDisplayId display} has been removed.
+ */
+ @Override
+ public void stop() {
+ synchronized (mLock) {
+ mStopped = true;
+ Message msg = mHandler.obtainMessage(MSG_STOP);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setEnabled(false);
+ }
+
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.stop();
+ }
+
+ if (mBrightnessSetting != null) {
+ mBrightnessSetting.unregisterListener(mBrightnessSettingListener);
+ }
+
+ mContext.getContentResolver().unregisterContentObserver(mSettingsObserver);
+ }
+ }
+
+ private void loadFromDisplayDeviceConfig(IBinder token, DisplayDeviceInfo info) {
+ // All properties that depend on the associated DisplayDevice and the DDC must be
+ // updated here.
+ loadBrightnessRampRates();
+ loadProximitySensor();
+ loadNitsRange(mContext.getResources());
+ setUpAutoBrightness(mContext.getResources(), mHandler);
+ reloadReduceBrightColours();
+ if (mScreenBrightnessRampAnimator != null) {
+ mScreenBrightnessRampAnimator.setAnimationTimeLimits(
+ mBrightnessRampIncreaseMaxTimeMillis,
+ mBrightnessRampDecreaseMaxTimeMillis);
+ }
+ mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
+ mDisplayDeviceConfig.getHighBrightnessModeData(),
+ new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
+ @Override
+ public float getHdrBrightnessFromSdr(float sdrBrightness) {
+ return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
+ }
+ });
+ mBrightnessThrottler.resetThrottlingData(
+ mDisplayDeviceConfig.getBrightnessThrottlingData(), mUniqueDisplayId);
+ }
+
+ private void sendUpdatePowerState() {
+ synchronized (mLock) {
+ sendUpdatePowerStateLocked();
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void sendUpdatePowerStateLocked() {
+ if (!mStopped && !mPendingUpdatePowerStateLocked) {
+ mPendingUpdatePowerStateLocked = true;
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_POWER_STATE);
+ mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
+ }
+ }
+
+ private void initialize(int displayState) {
+ mPowerState = mInjector.getDisplayPowerState(mBlanker,
+ mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState);
+
+ if (mColorFadeEnabled) {
+ mColorFadeOnAnimator = ObjectAnimator.ofFloat(
+ mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 0.0f, 1.0f);
+ mColorFadeOnAnimator.setDuration(COLOR_FADE_ON_ANIMATION_DURATION_MILLIS);
+ mColorFadeOnAnimator.addListener(mAnimatorListener);
+
+ mColorFadeOffAnimator = ObjectAnimator.ofFloat(
+ mPowerState, DisplayPowerState.COLOR_FADE_LEVEL, 1.0f, 0.0f);
+ mColorFadeOffAnimator.setDuration(COLOR_FADE_OFF_ANIMATION_DURATION_MILLIS);
+ mColorFadeOffAnimator.addListener(mAnimatorListener);
+ }
+
+ mScreenBrightnessRampAnimator = mInjector.getDualRampAnimator(mPowerState,
+ DisplayPowerState.SCREEN_BRIGHTNESS_FLOAT,
+ DisplayPowerState.SCREEN_SDR_BRIGHTNESS_FLOAT);
+ mScreenBrightnessRampAnimator.setAnimationTimeLimits(
+ mBrightnessRampIncreaseMaxTimeMillis,
+ mBrightnessRampDecreaseMaxTimeMillis);
+ mScreenBrightnessRampAnimator.setListener(mRampAnimatorListener);
+
+ noteScreenState(mPowerState.getScreenState());
+ noteScreenBrightness(mPowerState.getScreenBrightness());
+
+ // Initialize all of the brightness tracking state
+ final float brightness = convertToNits(mPowerState.getScreenBrightness());
+ if (brightness >= PowerManager.BRIGHTNESS_MIN) {
+ mBrightnessTracker.start(brightness);
+ }
+ mBrightnessSettingListener = brightnessValue -> {
+ Message msg = mHandler.obtainMessage(MSG_UPDATE_BRIGHTNESS, brightnessValue);
+ mHandler.sendMessage(msg);
+ };
+
+ mBrightnessSetting.registerListener(mBrightnessSettingListener);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT),
+ false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
+ mContext.getContentResolver().registerContentObserver(
+ Settings.System.getUriFor(Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ),
+ false /*notifyForDescendants*/, mSettingsObserver, UserHandle.USER_ALL);
+ }
+
+ private void setUpAutoBrightness(Resources resources, Handler handler) {
+ if (!mUseSoftwareAutoBrightnessConfig) {
+ return;
+ }
+
+ final boolean isIdleScreenBrightnessEnabled = resources.getBoolean(
+ R.bool.config_enableIdleScreenBrightnessMode);
+ mInteractiveModeBrightnessMapper = BrightnessMappingStrategy.create(resources,
+ mDisplayDeviceConfig, mDisplayWhiteBalanceController);
+ if (isIdleScreenBrightnessEnabled) {
+ mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources,
+ mDisplayDeviceConfig, mDisplayWhiteBalanceController);
+ }
+
+ if (mInteractiveModeBrightnessMapper != null) {
+ final float dozeScaleFactor = resources.getFraction(
+ R.fraction.config_screenAutoBrightnessDozeScaleFactor,
+ 1, 1);
+
+ int[] ambientBrighteningThresholds = resources.getIntArray(
+ R.array.config_ambientBrighteningThresholds);
+ int[] ambientDarkeningThresholds = resources.getIntArray(
+ R.array.config_ambientDarkeningThresholds);
+ int[] ambientThresholdLevels = resources.getIntArray(
+ R.array.config_ambientThresholdLevels);
+ float ambientDarkeningMinThreshold =
+ mDisplayDeviceConfig.getAmbientLuxDarkeningMinThreshold();
+ float ambientBrighteningMinThreshold =
+ mDisplayDeviceConfig.getAmbientLuxBrighteningMinThreshold();
+ HysteresisLevels ambientBrightnessThresholds = new HysteresisLevels(
+ ambientBrighteningThresholds, ambientDarkeningThresholds,
+ ambientThresholdLevels, ambientDarkeningMinThreshold,
+ ambientBrighteningMinThreshold);
+
+ int[] screenBrighteningThresholds = resources.getIntArray(
+ R.array.config_screenBrighteningThresholds);
+ int[] screenDarkeningThresholds = resources.getIntArray(
+ R.array.config_screenDarkeningThresholds);
+ int[] screenThresholdLevels = resources.getIntArray(
+ R.array.config_screenThresholdLevels);
+ float screenDarkeningMinThreshold =
+ mDisplayDeviceConfig.getScreenDarkeningMinThreshold();
+ float screenBrighteningMinThreshold =
+ mDisplayDeviceConfig.getScreenBrighteningMinThreshold();
+ HysteresisLevels screenBrightnessThresholds = new HysteresisLevels(
+ screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
+ screenDarkeningMinThreshold, screenBrighteningMinThreshold);
+
+ // Idle screen thresholds
+ float screenDarkeningMinThresholdIdle =
+ mDisplayDeviceConfig.getScreenDarkeningMinThresholdIdle();
+ float screenBrighteningMinThresholdIdle =
+ mDisplayDeviceConfig.getScreenBrighteningMinThresholdIdle();
+ HysteresisLevels screenBrightnessThresholdsIdle = new HysteresisLevels(
+ screenBrighteningThresholds, screenDarkeningThresholds, screenThresholdLevels,
+ screenDarkeningMinThresholdIdle, screenBrighteningMinThresholdIdle);
+
+ // Idle ambient thresholds
+ float ambientDarkeningMinThresholdIdle =
+ mDisplayDeviceConfig.getAmbientLuxDarkeningMinThresholdIdle();
+ float ambientBrighteningMinThresholdIdle =
+ mDisplayDeviceConfig.getAmbientLuxBrighteningMinThresholdIdle();
+ HysteresisLevels ambientBrightnessThresholdsIdle = new HysteresisLevels(
+ ambientBrighteningThresholds, ambientDarkeningThresholds,
+ ambientThresholdLevels, ambientDarkeningMinThresholdIdle,
+ ambientBrighteningMinThresholdIdle);
+
+ long brighteningLightDebounce = mDisplayDeviceConfig
+ .getAutoBrightnessBrighteningLightDebounce();
+ long darkeningLightDebounce = mDisplayDeviceConfig
+ .getAutoBrightnessDarkeningLightDebounce();
+ boolean autoBrightnessResetAmbientLuxAfterWarmUp = resources.getBoolean(
+ R.bool.config_autoBrightnessResetAmbientLuxAfterWarmUp);
+
+ int lightSensorWarmUpTimeConfig = resources.getInteger(
+ R.integer.config_lightSensorWarmupTime);
+ int lightSensorRate = resources.getInteger(
+ R.integer.config_autoBrightnessLightSensorRate);
+ int initialLightSensorRate = resources.getInteger(
+ R.integer.config_autoBrightnessInitialLightSensorRate);
+ if (initialLightSensorRate == -1) {
+ initialLightSensorRate = lightSensorRate;
+ } else if (initialLightSensorRate > lightSensorRate) {
+ Slog.w(mTag, "Expected config_autoBrightnessInitialLightSensorRate ("
+ + initialLightSensorRate + ") to be less than or equal to "
+ + "config_autoBrightnessLightSensorRate (" + lightSensorRate + ").");
+ }
+
+ loadAmbientLightSensor();
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.setLightSensor(mLightSensor);
+ }
+
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.stop();
+ }
+ mAutomaticBrightnessController = new AutomaticBrightnessController(this,
+ handler.getLooper(), mSensorManager, mLightSensor,
+ mInteractiveModeBrightnessMapper, lightSensorWarmUpTimeConfig,
+ PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, dozeScaleFactor,
+ lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
+ darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
+ ambientBrightnessThresholds, screenBrightnessThresholds,
+ ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, mContext,
+ mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper,
+ mDisplayDeviceConfig.getAmbientHorizonShort(),
+ mDisplayDeviceConfig.getAmbientHorizonLong());
+
+ mBrightnessEventRingBuffer =
+ new RingBuffer<>(BrightnessEvent.class, RINGBUFFER_MAX);
+ } else {
+ mUseSoftwareAutoBrightnessConfig = false;
+ }
+ }
+
+ private void loadBrightnessRampRates() {
+ mBrightnessRampRateFastDecrease = mDisplayDeviceConfig.getBrightnessRampFastDecrease();
+ mBrightnessRampRateFastIncrease = mDisplayDeviceConfig.getBrightnessRampFastIncrease();
+ mBrightnessRampRateSlowDecrease = mDisplayDeviceConfig.getBrightnessRampSlowDecrease();
+ mBrightnessRampRateSlowIncrease = mDisplayDeviceConfig.getBrightnessRampSlowIncrease();
+ mBrightnessRampDecreaseMaxTimeMillis =
+ mDisplayDeviceConfig.getBrightnessRampDecreaseMaxMillis();
+ mBrightnessRampIncreaseMaxTimeMillis =
+ mDisplayDeviceConfig.getBrightnessRampIncreaseMaxMillis();
+ }
+
+ private void loadNitsRange(Resources resources) {
+ if (mDisplayDeviceConfig != null && mDisplayDeviceConfig.getNits() != null) {
+ mNitsRange = mDisplayDeviceConfig.getNits();
+ } else {
+ Slog.w(mTag, "Screen brightness nits configuration is unavailable; falling back");
+ mNitsRange = BrightnessMappingStrategy.getFloatArray(resources
+ .obtainTypedArray(R.array.config_screenBrightnessNits));
+ }
+ }
+
+ private void reloadReduceBrightColours() {
+ if (mCdsi != null && mCdsi.isReduceBrightColorsActivated()) {
+ applyReduceBrightColorsSplineAdjustment();
+ }
+ }
+
+ @Override
+ public void setAutomaticScreenBrightnessMode(boolean isIdle) {
+ if (mAutomaticBrightnessController != null) {
+ if (isIdle) {
+ mAutomaticBrightnessController.switchToIdleMode();
+ } else {
+ mAutomaticBrightnessController.switchToInteractiveScreenBrightnessMode();
+ }
+ }
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setStrongModeEnabled(isIdle);
+ }
+ }
+
+ private final Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ sendUpdatePowerState();
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ }
+ };
+
+ private final RampAnimator.Listener mRampAnimatorListener = new RampAnimator.Listener() {
+ @Override
+ public void onAnimationEnd() {
+ sendUpdatePowerState();
+ Message msg = mHandler.obtainMessage(MSG_BRIGHTNESS_RAMP_DONE);
+ mHandler.sendMessage(msg);
+ }
+ };
+
+ /** Clean up all resources that are accessed via the {@link #mHandler} thread. */
+ private void cleanupHandlerThreadAfterStop() {
+ setProximitySensorEnabled(false);
+ mHbmController.stop();
+ mBrightnessThrottler.stop();
+ mHandler.removeCallbacksAndMessages(null);
+
+ // Release any outstanding wakelocks we're still holding because of pending messages.
+ if (mUnfinishedBusiness) {
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
+ mUnfinishedBusiness = false;
+ }
+ if (mOnStateChangedPending) {
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdOnStateChanged);
+ mOnStateChangedPending = false;
+ }
+ for (int i = 0; i < mOnProximityPositiveMessages; i++) {
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxPositive);
+ }
+ mOnProximityPositiveMessages = 0;
+ for (int i = 0; i < mOnProximityNegativeMessages; i++) {
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxNegative);
+ }
+ mOnProximityNegativeMessages = 0;
+
+ final float brightness = mPowerState != null
+ ? mPowerState.getScreenBrightness()
+ : PowerManager.BRIGHTNESS_MIN;
+ reportStats(brightness);
+
+ if (mPowerState != null) {
+ mPowerState.stop();
+ mPowerState = null;
+ }
+ }
+
+ private void updatePowerState() {
+ if (DEBUG) {
+ Trace.beginSection("DisplayPowerController#updatePowerState");
+ }
+ updatePowerStateInternal();
+ if (DEBUG) {
+ Trace.endSection();
+ }
+ }
+
+ private void updatePowerStateInternal() {
+ // Update the power state request.
+ final boolean mustNotify;
+ final int previousPolicy;
+ boolean mustInitialize = false;
+ int brightnessAdjustmentFlags = 0;
+ mBrightnessReasonTemp.set(null);
+ mTempBrightnessEvent.reset();
+ synchronized (mLock) {
+ if (mStopped) {
+ return;
+ }
+ mPendingUpdatePowerStateLocked = false;
+ if (mPendingRequestLocked == null) {
+ return; // wait until first actual power request
+ }
+
+ if (mPowerRequest == null) {
+ mPowerRequest = new DisplayPowerRequest(mPendingRequestLocked);
+ updatePendingProximityRequestsLocked();
+ mPendingRequestChangedLocked = false;
+ mustInitialize = true;
+ // Assume we're on and bright until told otherwise, since that's the state we turn
+ // on in.
+ previousPolicy = DisplayPowerRequest.POLICY_BRIGHT;
+ } else if (mPendingRequestChangedLocked) {
+ previousPolicy = mPowerRequest.policy;
+ mPowerRequest.copyFrom(mPendingRequestLocked);
+ updatePendingProximityRequestsLocked();
+ mPendingRequestChangedLocked = false;
+ mDisplayReadyLocked = false;
+ } else {
+ previousPolicy = mPowerRequest.policy;
+ }
+
+ mustNotify = !mDisplayReadyLocked;
+ }
+
+ // Compute the basic display state using the policy.
+ // We might override this below based on other factors.
+ // Initialise brightness as invalid.
+ int state;
+ float brightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ boolean performScreenOffTransition = false;
+ switch (mPowerRequest.policy) {
+ case DisplayPowerRequest.POLICY_OFF:
+ state = Display.STATE_OFF;
+ performScreenOffTransition = true;
+ break;
+ case DisplayPowerRequest.POLICY_DOZE:
+ if (mPowerRequest.dozeScreenState != Display.STATE_UNKNOWN) {
+ state = mPowerRequest.dozeScreenState;
+ } else {
+ state = Display.STATE_DOZE;
+ }
+ if (!mAllowAutoBrightnessWhileDozingConfig) {
+ brightnessState = mPowerRequest.dozeScreenBrightness;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE);
+ }
+ break;
+ case DisplayPowerRequest.POLICY_VR:
+ state = Display.STATE_VR;
+ break;
+ case DisplayPowerRequest.POLICY_DIM:
+ case DisplayPowerRequest.POLICY_BRIGHT:
+ default:
+ state = Display.STATE_ON;
+ break;
+ }
+ assert (state != Display.STATE_UNKNOWN);
+
+ boolean skipRampBecauseOfProximityChangeToNegative = false;
+ // Apply the proximity sensor.
+ if (mProximitySensor != null) {
+ if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
+ // At this point the policy says that the screen should be on, but we've been
+ // asked to listen to the prox sensor to adjust the display state, so lets make
+ // sure the sensor is on.
+ setProximitySensorEnabled(true);
+ if (!mScreenOffBecauseOfProximity
+ && mProximity == PROXIMITY_POSITIVE
+ && !mIgnoreProximityUntilChanged) {
+ // Prox sensor already reporting "near" so we should turn off the screen.
+ // Also checked that we aren't currently set to ignore the proximity sensor
+ // temporarily.
+ mScreenOffBecauseOfProximity = true;
+ sendOnProximityPositiveWithWakelock();
+ }
+ } else if (mWaitingForNegativeProximity
+ && mScreenOffBecauseOfProximity
+ && mProximity == PROXIMITY_POSITIVE
+ && state != Display.STATE_OFF) {
+ // The policy says that we should have the screen on, but it's off due to the prox
+ // and we've been asked to wait until the screen is far from the user to turn it
+ // back on. Let keep the prox sensor on so we can tell when it's far again.
+ setProximitySensorEnabled(true);
+ } else {
+ // We haven't been asked to use the prox sensor and we're not waiting on the screen
+ // to turn back on...so lets shut down the prox sensor.
+ setProximitySensorEnabled(false);
+ mWaitingForNegativeProximity = false;
+ }
+
+ if (mScreenOffBecauseOfProximity
+ && (mProximity != PROXIMITY_POSITIVE || mIgnoreProximityUntilChanged)) {
+ // The screen *was* off due to prox being near, but now it's "far" so lets turn
+ // the screen back on. Also turn it back on if we've been asked to ignore the
+ // prox sensor temporarily.
+ mScreenOffBecauseOfProximity = false;
+ skipRampBecauseOfProximityChangeToNegative = true;
+ sendOnProximityNegativeWithWakelock();
+ }
+ } else {
+ mWaitingForNegativeProximity = false;
+ mIgnoreProximityUntilChanged = false;
+ }
+
+ if (!mLogicalDisplay.isEnabled()
+ || mLogicalDisplay.getPhase() == LogicalDisplay.DISPLAY_PHASE_LAYOUT_TRANSITION
+ || mScreenOffBecauseOfProximity) {
+ state = Display.STATE_OFF;
+ }
+
+ // Initialize things the first time the power state is changed.
+ if (mustInitialize) {
+ initialize(state);
+ }
+
+ // Animate the screen state change unless already animating.
+ // The transition may be deferred, so after this point we will use the
+ // actual state instead of the desired one.
+ final int oldState = mPowerState.getScreenState();
+ animateScreenStateChange(state, performScreenOffTransition);
+ state = mPowerState.getScreenState();
+
+ if (state == Display.STATE_OFF) {
+ brightnessState = PowerManager.BRIGHTNESS_OFF_FLOAT;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_SCREEN_OFF);
+ }
+
+ // Always use the VR brightness when in the VR state.
+ if (state == Display.STATE_VR) {
+ brightnessState = mScreenBrightnessForVr;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_VR);
+ }
+
+ if ((Float.isNaN(brightnessState))
+ && isValidBrightnessValue(mPowerRequest.screenBrightnessOverride)) {
+ brightnessState = mPowerRequest.screenBrightnessOverride;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_OVERRIDE);
+ mAppliedScreenBrightnessOverride = true;
+ } else {
+ mAppliedScreenBrightnessOverride = false;
+ }
+
+ final boolean autoBrightnessEnabledInDoze =
+ mAllowAutoBrightnessWhileDozingConfig && Display.isDozeState(state);
+ final boolean autoBrightnessEnabled = mPowerRequest.useAutoBrightness
+ && (state == Display.STATE_ON || autoBrightnessEnabledInDoze)
+ && Float.isNaN(brightnessState)
+ && mAutomaticBrightnessController != null;
+ final boolean autoBrightnessDisabledDueToDisplayOff = mPowerRequest.useAutoBrightness
+ && !(state == Display.STATE_ON || autoBrightnessEnabledInDoze);
+ final int autoBrightnessState = autoBrightnessEnabled
+ ? AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED
+ : autoBrightnessDisabledDueToDisplayOff
+ ? AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE
+ : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
+
+ final boolean userSetBrightnessChanged = updateUserSetScreenBrightness();
+
+ // Use the temporary screen brightness if there isn't an override, either from
+ // WindowManager or based on the display state.
+ if (isValidBrightnessValue(mTemporaryScreenBrightness)) {
+ brightnessState = mTemporaryScreenBrightness;
+ mAppliedTemporaryBrightness = true;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_TEMPORARY);
+ } else {
+ mAppliedTemporaryBrightness = false;
+ }
+
+ final boolean autoBrightnessAdjustmentChanged = updateAutoBrightnessAdjustment();
+
+ // Use the autobrightness adjustment override if set.
+ final float autoBrightnessAdjustment;
+ if (!Float.isNaN(mTemporaryAutoBrightnessAdjustment)) {
+ autoBrightnessAdjustment = mTemporaryAutoBrightnessAdjustment;
+ brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO_TEMP;
+ mAppliedTemporaryAutoBrightnessAdjustment = true;
+ } else {
+ autoBrightnessAdjustment = mAutoBrightnessAdjustment;
+ brightnessAdjustmentFlags = BrightnessReason.ADJUSTMENT_AUTO;
+ mAppliedTemporaryAutoBrightnessAdjustment = false;
+ }
+ // Apply brightness boost.
+ // We do this here after deciding whether auto-brightness is enabled so that we don't
+ // disable the light sensor during this temporary state. That way when boost ends we will
+ // be able to resume normal auto-brightness behavior without any delay.
+ if (mPowerRequest.boostScreenBrightness
+ && brightnessState != PowerManager.BRIGHTNESS_OFF_FLOAT) {
+ brightnessState = PowerManager.BRIGHTNESS_MAX;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_BOOST);
+ mAppliedBrightnessBoost = true;
+ } else {
+ mAppliedBrightnessBoost = false;
+ }
+
+ // If the brightness is already set then it's been overridden by something other than the
+ // user, or is a temporary adjustment.
+ boolean userInitiatedChange = (Float.isNaN(brightnessState))
+ && (autoBrightnessAdjustmentChanged || userSetBrightnessChanged);
+ boolean hadUserBrightnessPoint = false;
+ // Configure auto-brightness.
+ if (mAutomaticBrightnessController != null) {
+ hadUserBrightnessPoint = mAutomaticBrightnessController.hasUserDataPoints();
+ mAutomaticBrightnessController.configure(autoBrightnessState,
+ mBrightnessConfiguration,
+ mLastUserSetScreenBrightness,
+ userSetBrightnessChanged, autoBrightnessAdjustment,
+ autoBrightnessAdjustmentChanged, mPowerRequest.policy);
+ }
+
+ if (mBrightnessTracker != null) {
+ mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration);
+ }
+
+ boolean updateScreenBrightnessSetting = false;
+
+ // Apply auto-brightness.
+ boolean slowChange = false;
+ if (Float.isNaN(brightnessState)) {
+ float newAutoBrightnessAdjustment = autoBrightnessAdjustment;
+ if (autoBrightnessEnabled) {
+ brightnessState = mAutomaticBrightnessController.getAutomaticScreenBrightness(
+ mTempBrightnessEvent);
+ newAutoBrightnessAdjustment =
+ mAutomaticBrightnessController.getAutomaticScreenBrightnessAdjustment();
+ }
+ if (isValidBrightnessValue(brightnessState)
+ || brightnessState == PowerManager.BRIGHTNESS_OFF_FLOAT) {
+ // Use current auto-brightness value and slowly adjust to changes.
+ brightnessState = clampScreenBrightness(brightnessState);
+ if (mAppliedAutoBrightness && !autoBrightnessAdjustmentChanged) {
+ slowChange = true; // slowly adapt to auto-brightness
+ }
+ updateScreenBrightnessSetting = mCurrentScreenBrightnessSetting != brightnessState;
+ mAppliedAutoBrightness = true;
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_AUTOMATIC);
+ } else {
+ mAppliedAutoBrightness = false;
+ }
+ if (autoBrightnessAdjustment != newAutoBrightnessAdjustment) {
+ // If the autobrightness controller has decided to change the adjustment value
+ // used, make sure that's reflected in settings.
+ putAutoBrightnessAdjustmentSetting(newAutoBrightnessAdjustment);
+ } else {
+ // Adjustment values resulted in no change
+ brightnessAdjustmentFlags = 0;
+ }
+ } else {
+ // Any non-auto-brightness values such as override or temporary should still be subject
+ // to clamping so that they don't go beyond the current max as specified by HBM
+ // Controller.
+ brightnessState = clampScreenBrightness(brightnessState);
+ mAppliedAutoBrightness = false;
+ brightnessAdjustmentFlags = 0;
+ }
+
+ // Use default brightness when dozing unless overridden.
+ if ((Float.isNaN(brightnessState))
+ && Display.isDozeState(state)) {
+ brightnessState = clampScreenBrightness(mScreenBrightnessDozeConfig);
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_DOZE_DEFAULT);
+ }
+
+ // Apply manual brightness.
+ if (Float.isNaN(brightnessState)) {
+ brightnessState = clampScreenBrightness(mCurrentScreenBrightnessSetting);
+ if (brightnessState != mCurrentScreenBrightnessSetting) {
+ // The manually chosen screen brightness is outside of the currently allowed
+ // range (i.e., high-brightness-mode), make sure we tell the rest of the system
+ // by updating the setting.
+ updateScreenBrightnessSetting = true;
+ }
+ mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
+ }
+
+ // Now that a desired brightness has been calculated, apply brightness throttling. The
+ // dimming and low power transformations that follow can only dim brightness further.
+ //
+ // We didn't do this earlier through brightness clamping because we need to know both
+ // unthrottled (unclamped/ideal) and throttled brightness levels for subsequent operations.
+ // Note throttling effectively changes the allowed brightness range, so, similarly to HBM,
+ // we broadcast this change through setting.
+ final float unthrottledBrightnessState = brightnessState;
+ if (mBrightnessThrottler.isThrottled()) {
+ mTempBrightnessEvent.setThermalMax(mBrightnessThrottler.getBrightnessCap());
+ brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
+ if (!mAppliedThrottling) {
+ // Brightness throttling is needed, so do so quickly.
+ // Later, when throttling is removed, we let other mechanisms decide on speed.
+ slowChange = false;
+ }
+ mAppliedThrottling = true;
+ } else if (mAppliedThrottling) {
+ mAppliedThrottling = false;
+ }
+
+ if (updateScreenBrightnessSetting) {
+ // Tell the rest of the system about the new brightness in case we had to change it
+ // for things like auto-brightness or high-brightness-mode. Note that we do this
+ // before applying the low power or dim transformations so that the slider
+ // accurately represents the full possible range, even if they range changes what
+ // it means in absolute terms.
+ updateScreenBrightnessSetting(brightnessState);
+ }
+
+ // Apply dimming by at least some minimum amount when user activity
+ // timeout is about to expire.
+ if (mPowerRequest.policy == DisplayPowerRequest.POLICY_DIM) {
+ if (brightnessState > PowerManager.BRIGHTNESS_MIN) {
+ brightnessState = Math.max(
+ Math.min(brightnessState - mScreenBrightnessMinimumDimAmount,
+ mScreenBrightnessDimConfig),
+ PowerManager.BRIGHTNESS_MIN);
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_DIMMED);
+ }
+ if (!mAppliedDimming) {
+ slowChange = false;
+ }
+ mAppliedDimming = true;
+ } else if (mAppliedDimming) {
+ slowChange = false;
+ mAppliedDimming = false;
+ }
+ // If low power mode is enabled, scale brightness by screenLowPowerBrightnessFactor
+ // as long as it is above the minimum threshold.
+ if (mPowerRequest.lowPowerMode) {
+ if (brightnessState > PowerManager.BRIGHTNESS_MIN) {
+ final float brightnessFactor =
+ Math.min(mPowerRequest.screenLowPowerBrightnessFactor, 1);
+ final float lowPowerBrightnessFloat = (brightnessState * brightnessFactor);
+ brightnessState = Math.max(lowPowerBrightnessFloat, PowerManager.BRIGHTNESS_MIN);
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_LOW_POWER);
+ }
+ if (!mAppliedLowPower) {
+ slowChange = false;
+ }
+ mAppliedLowPower = true;
+ } else if (mAppliedLowPower) {
+ slowChange = false;
+ mAppliedLowPower = false;
+ }
+
+ // The current brightness to use has been calculated at this point, and HbmController should
+ // be notified so that it can accurately calculate HDR or HBM levels. We specifically do it
+ // here instead of having HbmController listen to the brightness setting because certain
+ // brightness sources (such as an app override) are not saved to the setting, but should be
+ // reflected in HBM calculations.
+ mHbmController.onBrightnessChanged(brightnessState, unthrottledBrightnessState,
+ mBrightnessThrottler.getBrightnessMaxReason());
+
+ // Animate the screen brightness when the screen is on or dozing.
+ // Skip the animation when the screen is off or suspended or transition to/from VR.
+ boolean brightnessAdjusted = false;
+ final boolean brightnessIsTemporary =
+ mAppliedTemporaryBrightness || mAppliedTemporaryAutoBrightnessAdjustment;
+ if (!mPendingScreenOff) {
+ if (mSkipScreenOnBrightnessRamp) {
+ if (state == Display.STATE_ON) {
+ if (mSkipRampState == RAMP_STATE_SKIP_NONE && mDozing) {
+ mInitialAutoBrightness = brightnessState;
+ mSkipRampState = RAMP_STATE_SKIP_INITIAL;
+ } else if (mSkipRampState == RAMP_STATE_SKIP_INITIAL
+ && mUseSoftwareAutoBrightnessConfig
+ && !BrightnessSynchronizer.floatEquals(brightnessState,
+ mInitialAutoBrightness)) {
+ mSkipRampState = RAMP_STATE_SKIP_AUTOBRIGHT;
+ } else if (mSkipRampState == RAMP_STATE_SKIP_AUTOBRIGHT) {
+ mSkipRampState = RAMP_STATE_SKIP_NONE;
+ }
+ } else {
+ mSkipRampState = RAMP_STATE_SKIP_NONE;
+ }
+ }
+
+ final boolean wasOrWillBeInVr =
+ (state == Display.STATE_VR || oldState == Display.STATE_VR);
+ final boolean initialRampSkip = (state == Display.STATE_ON && mSkipRampState
+ != RAMP_STATE_SKIP_NONE) || skipRampBecauseOfProximityChangeToNegative;
+ // While dozing, sometimes the brightness is split into buckets. Rather than animating
+ // through the buckets, which is unlikely to be smooth in the first place, just jump
+ // right to the suggested brightness.
+ final boolean hasBrightnessBuckets =
+ Display.isDozeState(state) && mBrightnessBucketsInDozeConfig;
+ // If the color fade is totally covering the screen then we can change the backlight
+ // level without it being a noticeable jump since any actual content isn't yet visible.
+ final boolean isDisplayContentVisible =
+ mColorFadeEnabled && mPowerState.getColorFadeLevel() == 1.0f;
+ // We only want to animate the brightness if it is between 0.0f and 1.0f.
+ // brightnessState can contain the values -1.0f and NaN, which we do not want to
+ // animate to. To avoid this, we check the value first.
+ // If the brightnessState is off (-1.0f) we still want to animate to the minimum
+ // brightness (0.0f) to accommodate for LED displays, which can appear bright to the
+ // user even when the display is all black. We also clamp here in case some
+ // transformations to the brightness have pushed it outside of the currently
+ // allowed range.
+ float animateValue = clampScreenBrightness(brightnessState);
+
+ // If there are any HDR layers on the screen, we have a special brightness value that we
+ // use instead. We still preserve the calculated brightness for Standard Dynamic Range
+ // (SDR) layers, but the main brightness value will be the one for HDR.
+ float sdrAnimateValue = animateValue;
+ // TODO(b/216365040): The decision to prevent HBM for HDR in low power mode should be
+ // done in HighBrightnessModeController.
+ if (mHbmController.getHighBrightnessMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR
+ && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_DIMMED) == 0
+ && (mBrightnessReason.getModifier() & BrightnessReason.MODIFIER_LOW_POWER)
+ == 0) {
+ // We want to scale HDR brightness level with the SDR level
+ animateValue = mHbmController.getHdrBrightnessValue();
+ }
+
+ final float currentBrightness = mPowerState.getScreenBrightness();
+ final float currentSdrBrightness = mPowerState.getSdrScreenBrightness();
+ if (isValidBrightnessValue(animateValue)
+ && (animateValue != currentBrightness
+ || sdrAnimateValue != currentSdrBrightness)) {
+ if (initialRampSkip || hasBrightnessBuckets
+ || wasOrWillBeInVr || !isDisplayContentVisible || brightnessIsTemporary) {
+ animateScreenBrightness(animateValue, sdrAnimateValue,
+ SCREEN_ANIMATION_RATE_MINIMUM);
+ } else {
+ boolean isIncreasing = animateValue > currentBrightness;
+ final float rampSpeed;
+ if (isIncreasing && slowChange) {
+ rampSpeed = mBrightnessRampRateSlowIncrease;
+ } else if (isIncreasing && !slowChange) {
+ rampSpeed = mBrightnessRampRateFastIncrease;
+ } else if (!isIncreasing && slowChange) {
+ rampSpeed = mBrightnessRampRateSlowDecrease;
+ } else {
+ rampSpeed = mBrightnessRampRateFastDecrease;
+ }
+ animateScreenBrightness(animateValue, sdrAnimateValue, rampSpeed);
+ }
+ }
+
+ // Report brightness to brightnesstracker:
+ // If brightness is not temporary (ie the slider has been released)
+ // AND if we are not in idle screen brightness mode.
+ if (!brightnessIsTemporary
+ && (mAutomaticBrightnessController != null
+ && !mAutomaticBrightnessController.isInIdleMode())) {
+ if (userInitiatedChange && (mAutomaticBrightnessController == null
+ || !mAutomaticBrightnessController.hasValidAmbientLux())) {
+ // If we don't have a valid lux reading we can't report a valid
+ // slider event so notify as if the system changed the brightness.
+ userInitiatedChange = false;
+ }
+ notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
+ hadUserBrightnessPoint);
+ }
+
+ // We save the brightness info *after* the brightness setting has been changed and
+ // adjustments made so that the brightness info reflects the latest value.
+ brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting(), animateValue);
+ } else {
+ brightnessAdjusted = saveBrightnessInfo(getScreenBrightnessSetting());
+ }
+
+ // Only notify if the brightness adjustment is not temporary (i.e. slider has been released)
+ if (brightnessAdjusted && !brightnessIsTemporary) {
+ postBrightnessChangeRunnable();
+ }
+
+ // Log any changes to what is currently driving the brightness setting.
+ if (!mBrightnessReasonTemp.equals(mBrightnessReason) || brightnessAdjustmentFlags != 0) {
+ Slog.v(mTag, "Brightness [" + brightnessState + "] reason changing to: '"
+ + mBrightnessReasonTemp.toString(brightnessAdjustmentFlags)
+ + "', previous reason: '" + mBrightnessReason + "'.");
+ mBrightnessReason.set(mBrightnessReasonTemp);
+ } else if (mBrightnessReasonTemp.getReason() == BrightnessReason.REASON_MANUAL
+ && userSetBrightnessChanged) {
+ Slog.v(mTag, "Brightness [" + brightnessState + "] manual adjustment.");
+ }
+
+
+ // Log brightness events when a detail of significance has changed. Generally this is the
+ // brightness itself changing, but also includes data like HBM cap, thermal throttling
+ // brightness cap, RBC state, etc.
+ mTempBrightnessEvent.setTime(System.currentTimeMillis());
+ mTempBrightnessEvent.setBrightness(brightnessState);
+ mTempBrightnessEvent.setPhysicalDisplayId(mUniqueDisplayId);
+ mTempBrightnessEvent.setReason(mBrightnessReason);
+ mTempBrightnessEvent.setHbmMax(mHbmController.getCurrentBrightnessMax());
+ mTempBrightnessEvent.setHbmMode(mHbmController.getHighBrightnessMode());
+ mTempBrightnessEvent.setFlags(mTempBrightnessEvent.getFlags()
+ | (mIsRbcActive ? BrightnessEvent.FLAG_RBC : 0)
+ | (mPowerRequest.lowPowerMode ? BrightnessEvent.FLAG_LOW_POWER_MODE : 0));
+ mTempBrightnessEvent.setRbcStrength(mCdsi != null
+ ? mCdsi.getReduceBrightColorsStrength() : -1);
+ mTempBrightnessEvent.setPowerFactor(mPowerRequest.screenLowPowerBrightnessFactor);
+ // Temporary is what we use during slider interactions. We avoid logging those so that
+ // we don't spam logcat when the slider is being used.
+ boolean tempToTempTransition =
+ mTempBrightnessEvent.getReason().getReason() == BrightnessReason.REASON_TEMPORARY
+ && mLastBrightnessEvent.getReason().getReason()
+ == BrightnessReason.REASON_TEMPORARY;
+ if ((!mTempBrightnessEvent.equalsMainData(mLastBrightnessEvent) && !tempToTempTransition)
+ || brightnessAdjustmentFlags != 0) {
+ float lastBrightness = mLastBrightnessEvent.getBrightness();
+ mTempBrightnessEvent.setInitialBrightness(lastBrightness);
+ mTempBrightnessEvent.setFastAmbientLux(
+ mAutomaticBrightnessController == null
+ ? -1f : mAutomaticBrightnessController.getFastAmbientLux());
+ mTempBrightnessEvent.setSlowAmbientLux(
+ mAutomaticBrightnessController == null
+ ? -1f : mAutomaticBrightnessController.getSlowAmbientLux());
+ mTempBrightnessEvent.setAutomaticBrightnessEnabled(mPowerRequest.useAutoBrightness);
+ mLastBrightnessEvent.copyFrom(mTempBrightnessEvent);
+ BrightnessEvent newEvent = new BrightnessEvent(mTempBrightnessEvent);
+ // Adjustment flags (and user-set flag) only get added after the equality checks since
+ // they are transient.
+ newEvent.setAdjustmentFlags(brightnessAdjustmentFlags);
+ newEvent.setFlags(newEvent.getFlags() | (userSetBrightnessChanged
+ ? BrightnessEvent.FLAG_USER_SET : 0));
+ Slog.i(mTag, newEvent.toString(/* includeTime= */ false));
+
+ if (userSetBrightnessChanged) {
+ logManualBrightnessEvent(newEvent);
+ }
+ if (mBrightnessEventRingBuffer != null) {
+ mBrightnessEventRingBuffer.append(newEvent);
+ }
+ }
+
+ // Update display white-balance.
+ if (mDisplayWhiteBalanceController != null) {
+ if (state == Display.STATE_ON && mDisplayWhiteBalanceSettings.isEnabled()) {
+ mDisplayWhiteBalanceController.setEnabled(true);
+ mDisplayWhiteBalanceController.updateDisplayColorTemperature();
+ } else {
+ mDisplayWhiteBalanceController.setEnabled(false);
+ }
+ }
+
+ // Determine whether the display is ready for use in the newly requested state.
+ // Note that we do not wait for the brightness ramp animation to complete before
+ // reporting the display is ready because we only need to ensure the screen is in the
+ // right power state even as it continues to converge on the desired brightness.
+ final boolean ready = mPendingScreenOnUnblocker == null
+ && (!mColorFadeEnabled || (!mColorFadeOnAnimator.isStarted()
+ && !mColorFadeOffAnimator.isStarted()))
+ && mPowerState.waitUntilClean(mCleanListener);
+ final boolean finished = ready
+ && !mScreenBrightnessRampAnimator.isAnimating();
+
+ // Notify policy about screen turned on.
+ if (ready && state != Display.STATE_OFF
+ && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_ON) {
+ setReportedScreenState(REPORTED_TO_POLICY_SCREEN_ON);
+ mWindowManagerPolicy.screenTurnedOn(mDisplayId);
+ }
+
+ // Grab a wake lock if we have unfinished business.
+ if (!finished && !mUnfinishedBusiness) {
+ if (DEBUG) {
+ Slog.d(mTag, "Unfinished business...");
+ }
+ mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
+ mUnfinishedBusiness = true;
+ }
+
+ // Notify the power manager when ready.
+ if (ready && mustNotify) {
+ // Send state change.
+ synchronized (mLock) {
+ if (!mPendingRequestChangedLocked) {
+ mDisplayReadyLocked = true;
+
+ if (DEBUG) {
+ Slog.d(mTag, "Display ready!");
+ }
+ }
+ }
+ sendOnStateChangedWithWakelock();
+ }
+
+ // Release the wake lock when we have no unfinished business.
+ if (finished && mUnfinishedBusiness) {
+ if (DEBUG) {
+ Slog.d(mTag, "Finished business...");
+ }
+ mUnfinishedBusiness = false;
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdUnfinishedBusiness);
+ }
+
+ // Record if dozing for future comparison.
+ mDozing = state != Display.STATE_ON;
+
+ if (previousPolicy != mPowerRequest.policy) {
+ logDisplayPolicyChanged(mPowerRequest.policy);
+ }
+ }
+
+ @Override
+ public void updateBrightness() {
+ sendUpdatePowerState();
+ }
+
+ /**
+ * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
+ * currently enabled and forcing the screen to be dark.
+ */
+ @Override
+ public void ignoreProximitySensorUntilChanged() {
+ mHandler.sendEmptyMessage(MSG_IGNORE_PROXIMITY);
+ }
+
+ @Override
+ public void setBrightnessConfiguration(BrightnessConfiguration c) {
+ Message msg = mHandler.obtainMessage(MSG_CONFIGURE_BRIGHTNESS, c);
+ msg.sendToTarget();
+ }
+
+ @Override
+ public void setTemporaryBrightness(float brightness) {
+ Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_BRIGHTNESS,
+ Float.floatToIntBits(brightness), 0 /*unused*/);
+ msg.sendToTarget();
+ }
+
+ @Override
+ public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
+ Message msg = mHandler.obtainMessage(MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT,
+ Float.floatToIntBits(adjustment), 0 /*unused*/);
+ msg.sendToTarget();
+ }
+
+ @Override
+ public BrightnessInfo getBrightnessInfo() {
+ synchronized (mCachedBrightnessInfo) {
+ return new BrightnessInfo(
+ mCachedBrightnessInfo.brightness.value,
+ mCachedBrightnessInfo.adjustedBrightness.value,
+ mCachedBrightnessInfo.brightnessMin.value,
+ mCachedBrightnessInfo.brightnessMax.value,
+ mCachedBrightnessInfo.hbmMode.value,
+ mCachedBrightnessInfo.hbmTransitionPoint.value,
+ mCachedBrightnessInfo.brightnessMaxReason.value);
+ }
+ }
+
+ private boolean saveBrightnessInfo(float brightness) {
+ return saveBrightnessInfo(brightness, brightness);
+ }
+
+ private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) {
+ synchronized (mCachedBrightnessInfo) {
+ final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(),
+ mBrightnessThrottler.getBrightnessCap());
+ final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(),
+ mBrightnessThrottler.getBrightnessCap());
+ boolean changed = false;
+
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightness,
+ brightness);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.adjustedBrightness,
+ adjustedBrightness);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
+ minBrightness);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
+ maxBrightness);
+ changed |=
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
+ mHbmController.getHighBrightnessMode());
+ changed |=
+ mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.hbmTransitionPoint,
+ mHbmController.getTransitionPoint());
+ changed |=
+ mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.brightnessMaxReason,
+ mBrightnessThrottler.getBrightnessMaxReason());
+
+ return changed;
+ }
+ }
+
+ void postBrightnessChangeRunnable() {
+ mHandler.post(mOnBrightnessChangeRunnable);
+ }
+
+ private HighBrightnessModeController createHbmControllerLocked() {
+ final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+ final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+ final IBinder displayToken =
+ mLogicalDisplay.getPrimaryDisplayDeviceLocked().getDisplayTokenLocked();
+ final String displayUniqueId =
+ mLogicalDisplay.getPrimaryDisplayDeviceLocked().getUniqueId();
+ final DisplayDeviceConfig.HighBrightnessModeData hbmData =
+ ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
+ final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
+ return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken,
+ displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
+ new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
+ @Override
+ public float getHdrBrightnessFromSdr(float sdrBrightness) {
+ return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
+ }
+ },
+ () -> {
+ sendUpdatePowerState();
+ postBrightnessChangeRunnable();
+ // TODO(b/192258832): Switch the HBMChangeCallback to a listener pattern.
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.update();
+ }
+ }, mContext);
+ }
+
+ private BrightnessThrottler createBrightnessThrottlerLocked() {
+ final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
+ final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
+ final DisplayDeviceConfig.BrightnessThrottlingData data =
+ ddConfig != null ? ddConfig.getBrightnessThrottlingData() : null;
+ return new BrightnessThrottler(mHandler, data,
+ () -> {
+ sendUpdatePowerState();
+ postBrightnessChangeRunnable();
+ }, mUniqueDisplayId);
+ }
+
+ private void blockScreenOn() {
+ if (mPendingScreenOnUnblocker == null) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
+ mPendingScreenOnUnblocker = new ScreenOnUnblocker();
+ mScreenOnBlockStartRealTime = SystemClock.elapsedRealtime();
+ Slog.i(mTag, "Blocking screen on until initial contents have been drawn.");
+ }
+ }
+
+ private void unblockScreenOn() {
+ if (mPendingScreenOnUnblocker != null) {
+ mPendingScreenOnUnblocker = null;
+ long delay = SystemClock.elapsedRealtime() - mScreenOnBlockStartRealTime;
+ Slog.i(mTag, "Unblocked screen on after " + delay + " ms");
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
+ }
+ }
+
+ private void blockScreenOff() {
+ if (mPendingScreenOffUnblocker == null) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
+ mPendingScreenOffUnblocker = new ScreenOffUnblocker();
+ mScreenOffBlockStartRealTime = SystemClock.elapsedRealtime();
+ Slog.i(mTag, "Blocking screen off");
+ }
+ }
+
+ private void unblockScreenOff() {
+ if (mPendingScreenOffUnblocker != null) {
+ mPendingScreenOffUnblocker = null;
+ long delay = SystemClock.elapsedRealtime() - mScreenOffBlockStartRealTime;
+ Slog.i(mTag, "Unblocked screen off after " + delay + " ms");
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_POWER, SCREEN_OFF_BLOCKED_TRACE_NAME, 0);
+ }
+ }
+
+ private boolean setScreenState(int state) {
+ return setScreenState(state, false /*reportOnly*/);
+ }
+
+ private boolean setScreenState(int state, boolean reportOnly) {
+ final boolean isOff = (state == Display.STATE_OFF);
+
+ if (mPowerState.getScreenState() != state
+ || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
+ // If we are trying to turn screen off, give policy a chance to do something before we
+ // actually turn the screen off.
+ if (isOff && !mScreenOffBecauseOfProximity) {
+ if (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_ON
+ || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED) {
+ setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_OFF);
+ blockScreenOff();
+ mWindowManagerPolicy.screenTurningOff(mDisplayId, mPendingScreenOffUnblocker);
+ unblockScreenOff();
+ } else if (mPendingScreenOffUnblocker != null) {
+ // Abort doing the state change until screen off is unblocked.
+ return false;
+ }
+ }
+
+ if (!reportOnly && mPowerState.getScreenState() != state) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "ScreenState", state);
+ // TODO(b/153319140) remove when we can get this from the above trace invocation
+ SystemProperties.set("debug.tracing.screen_state", String.valueOf(state));
+ mPowerState.setScreenState(state);
+ // Tell battery stats about the transition.
+ noteScreenState(state);
+ }
+ }
+
+ // Tell the window manager policy when the screen is turned off or on unless it's due
+ // to the proximity sensor. We temporarily block turning the screen on until the
+ // window manager is ready by leaving a black surface covering the screen.
+ // This surface is essentially the final state of the color fade animation and
+ // it is only removed once the window manager tells us that the activity has
+ // finished drawing underneath.
+ if (isOff && mReportedScreenStateToPolicy != REPORTED_TO_POLICY_SCREEN_OFF
+ && !mScreenOffBecauseOfProximity) {
+ setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
+ unblockScreenOn();
+ mWindowManagerPolicy.screenTurnedOff(mDisplayId);
+ } else if (!isOff
+ && mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_TURNING_OFF) {
+
+ // We told policy already that screen was turning off, but now we changed our minds.
+ // Complete the full state transition on -> turningOff -> off.
+ unblockScreenOff();
+ mWindowManagerPolicy.screenTurnedOff(mDisplayId);
+ setReportedScreenState(REPORTED_TO_POLICY_SCREEN_OFF);
+ }
+ if (!isOff
+ && (mReportedScreenStateToPolicy == REPORTED_TO_POLICY_SCREEN_OFF
+ || mReportedScreenStateToPolicy == REPORTED_TO_POLICY_UNREPORTED)) {
+ setReportedScreenState(REPORTED_TO_POLICY_SCREEN_TURNING_ON);
+ if (mPowerState.getColorFadeLevel() == 0.0f) {
+ blockScreenOn();
+ } else {
+ unblockScreenOn();
+ }
+ mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
+ }
+
+ // Return true if the screen isn't blocked.
+ return mPendingScreenOnUnblocker == null;
+ }
+
+ private void setReportedScreenState(int state) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "ReportedScreenStateToPolicy", state);
+ mReportedScreenStateToPolicy = state;
+ }
+
+ private void loadAmbientLightSensor() {
+ DisplayDeviceConfig.SensorData lightSensor = mDisplayDeviceConfig.getAmbientLightSensor();
+ final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
+ ? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK;
+ mLightSensor = SensorUtils.findSensor(mSensorManager, lightSensor.type, lightSensor.name,
+ fallbackType);
+ }
+
+ private void loadProximitySensor() {
+ if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) {
+ return;
+ }
+ final DisplayDeviceConfig.SensorData proxSensor =
+ mDisplayDeviceConfig.getProximitySensor();
+ final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY
+ ? Sensor.TYPE_PROXIMITY : SensorUtils.NO_FALLBACK;
+ mProximitySensor = SensorUtils.findSensor(mSensorManager, proxSensor.type, proxSensor.name,
+ fallbackType);
+ if (mProximitySensor != null) {
+ mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(),
+ TYPICAL_PROXIMITY_THRESHOLD);
+ }
+ }
+
+ private float clampScreenBrightnessForVr(float value) {
+ return MathUtils.constrain(
+ value, mScreenBrightnessForVrRangeMinimum,
+ mScreenBrightnessForVrRangeMaximum);
+ }
+
+ private float clampScreenBrightness(float value) {
+ if (Float.isNaN(value)) {
+ value = PowerManager.BRIGHTNESS_MIN;
+ }
+ return MathUtils.constrain(value,
+ mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
+ }
+
+ // Checks whether the brightness is within the valid brightness range, not including off.
+ private boolean isValidBrightnessValue(float brightness) {
+ return brightness >= PowerManager.BRIGHTNESS_MIN
+ && brightness <= PowerManager.BRIGHTNESS_MAX;
+ }
+
+ private void animateScreenBrightness(float target, float sdrTarget, float rate) {
+ if (DEBUG) {
+ Slog.d(mTag, "Animating brightness: target=" + target + ", sdrTarget=" + sdrTarget
+ + ", rate=" + rate);
+ }
+ if (mScreenBrightnessRampAnimator.animateTo(target, sdrTarget, rate)) {
+ Trace.traceCounter(Trace.TRACE_TAG_POWER, "TargetScreenBrightness", (int) target);
+ // TODO(b/153319140) remove when we can get this from the above trace invocation
+ SystemProperties.set("debug.tracing.screen_brightness", String.valueOf(target));
+ noteScreenBrightness(target);
+ }
+ }
+
+ private void animateScreenStateChange(int target, boolean performScreenOffTransition) {
+ // If there is already an animation in progress, don't interfere with it.
+ if (mColorFadeEnabled
+ && (mColorFadeOnAnimator.isStarted() || mColorFadeOffAnimator.isStarted())) {
+ if (target != Display.STATE_ON) {
+ return;
+ }
+ // If display state changed to on, proceed and stop the color fade and turn screen on.
+ mPendingScreenOff = false;
+ }
+
+ if (mDisplayBlanksAfterDozeConfig
+ && Display.isDozeState(mPowerState.getScreenState())
+ && !Display.isDozeState(target)) {
+ // Skip the screen off animation and add a black surface to hide the
+ // contents of the screen.
+ mPowerState.prepareColorFade(mContext,
+ mColorFadeFadesConfig ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP);
+ if (mColorFadeOffAnimator != null) {
+ mColorFadeOffAnimator.end();
+ }
+ // Some display hardware will blank itself on the transition between doze and non-doze
+ // but still on display states. In this case we want to report to policy that the
+ // display has turned off so it can prepare the appropriate power on animation, but we
+ // don't want to actually transition to the fully off state since that takes
+ // significantly longer to transition from.
+ setScreenState(Display.STATE_OFF, target != Display.STATE_OFF /*reportOnly*/);
+ }
+
+ // If we were in the process of turning off the screen but didn't quite
+ // finish. Then finish up now to prevent a jarring transition back
+ // to screen on if we skipped blocking screen on as usual.
+ if (mPendingScreenOff && target != Display.STATE_OFF) {
+ setScreenState(Display.STATE_OFF);
+ mPendingScreenOff = false;
+ mPowerState.dismissColorFadeResources();
+ }
+
+ if (target == Display.STATE_ON) {
+ // Want screen on. The contents of the screen may not yet
+ // be visible if the color fade has not been dismissed because
+ // its last frame of animation is solid black.
+ if (!setScreenState(Display.STATE_ON)) {
+ return; // screen on blocked
+ }
+ if (USE_COLOR_FADE_ON_ANIMATION && mColorFadeEnabled && mPowerRequest.isBrightOrDim()) {
+ // Perform screen on animation.
+ if (mPowerState.getColorFadeLevel() == 1.0f) {
+ mPowerState.dismissColorFade();
+ } else if (mPowerState.prepareColorFade(mContext,
+ mColorFadeFadesConfig
+ ? ColorFade.MODE_FADE : ColorFade.MODE_WARM_UP)) {
+ mColorFadeOnAnimator.start();
+ } else {
+ mColorFadeOnAnimator.end();
+ }
+ } else {
+ // Skip screen on animation.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
+ }
+ } else if (target == Display.STATE_VR) {
+ // Wait for brightness animation to complete beforehand when entering VR
+ // from screen on to prevent a perceptible jump because brightness may operate
+ // differently when the display is configured for dozing.
+ if (mScreenBrightnessRampAnimator.isAnimating()
+ && mPowerState.getScreenState() == Display.STATE_ON) {
+ return;
+ }
+
+ // Set screen state.
+ if (!setScreenState(Display.STATE_VR)) {
+ return; // screen on blocked
+ }
+
+ // Dismiss the black surface without fanfare.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
+ } else if (target == Display.STATE_DOZE) {
+ // Want screen dozing.
+ // Wait for brightness animation to complete beforehand when entering doze
+ // from screen on to prevent a perceptible jump because brightness may operate
+ // differently when the display is configured for dozing.
+ if (mScreenBrightnessRampAnimator.isAnimating()
+ && mPowerState.getScreenState() == Display.STATE_ON) {
+ return;
+ }
+
+ // Set screen state.
+ if (!setScreenState(Display.STATE_DOZE)) {
+ return; // screen on blocked
+ }
+
+ // Dismiss the black surface without fanfare.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
+ } else if (target == Display.STATE_DOZE_SUSPEND) {
+ // Want screen dozing and suspended.
+ // Wait for brightness animation to complete beforehand unless already
+ // suspended because we may not be able to change it after suspension.
+ if (mScreenBrightnessRampAnimator.isAnimating()
+ && mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) {
+ return;
+ }
+
+ // If not already suspending, temporarily set the state to doze until the
+ // screen on is unblocked, then suspend.
+ if (mPowerState.getScreenState() != Display.STATE_DOZE_SUSPEND) {
+ if (!setScreenState(Display.STATE_DOZE)) {
+ return; // screen on blocked
+ }
+ setScreenState(Display.STATE_DOZE_SUSPEND); // already on so can't block
+ }
+
+ // Dismiss the black surface without fanfare.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
+ } else if (target == Display.STATE_ON_SUSPEND) {
+ // Want screen full-power and suspended.
+ // Wait for brightness animation to complete beforehand unless already
+ // suspended because we may not be able to change it after suspension.
+ if (mScreenBrightnessRampAnimator.isAnimating()
+ && mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) {
+ return;
+ }
+
+ // If not already suspending, temporarily set the state to on until the
+ // screen on is unblocked, then suspend.
+ if (mPowerState.getScreenState() != Display.STATE_ON_SUSPEND) {
+ if (!setScreenState(Display.STATE_ON)) {
+ return;
+ }
+ setScreenState(Display.STATE_ON_SUSPEND);
+ }
+
+ // Dismiss the black surface without fanfare.
+ mPowerState.setColorFadeLevel(1.0f);
+ mPowerState.dismissColorFade();
+ } else {
+ // Want screen off.
+ mPendingScreenOff = true;
+ if (!mColorFadeEnabled) {
+ mPowerState.setColorFadeLevel(0.0f);
+ }
+
+ if (mPowerState.getColorFadeLevel() == 0.0f) {
+ // Turn the screen off.
+ // A black surface is already hiding the contents of the screen.
+ setScreenState(Display.STATE_OFF);
+ mPendingScreenOff = false;
+ mPowerState.dismissColorFadeResources();
+ } else if (performScreenOffTransition
+ && mPowerState.prepareColorFade(mContext,
+ mColorFadeFadesConfig
+ ? ColorFade.MODE_FADE : ColorFade.MODE_COOL_DOWN)
+ && mPowerState.getScreenState() != Display.STATE_OFF) {
+ // Perform the screen off animation.
+ mColorFadeOffAnimator.start();
+ } else {
+ // Skip the screen off animation and add a black surface to hide the
+ // contents of the screen.
+ mColorFadeOffAnimator.end();
+ }
+ }
+ }
+
+ private final Runnable mCleanListener = this::sendUpdatePowerState;
+
+ private void setProximitySensorEnabled(boolean enable) {
+ if (enable) {
+ if (!mProximitySensorEnabled) {
+ // Register the listener.
+ // Proximity sensor state already cleared initially.
+ mProximitySensorEnabled = true;
+ mIgnoreProximityUntilChanged = false;
+ mSensorManager.registerListener(mProximitySensorListener, mProximitySensor,
+ SensorManager.SENSOR_DELAY_NORMAL, mHandler);
+ }
+ } else {
+ if (mProximitySensorEnabled) {
+ // Unregister the listener.
+ // Clear the proximity sensor state for next time.
+ mProximitySensorEnabled = false;
+ mProximity = PROXIMITY_UNKNOWN;
+ mIgnoreProximityUntilChanged = false;
+ mPendingProximity = PROXIMITY_UNKNOWN;
+ mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ mSensorManager.unregisterListener(mProximitySensorListener);
+ clearPendingProximityDebounceTime(); // release wake lock (must be last)
+ }
+ }
+ }
+
+ private void handleProximitySensorEvent(long time, boolean positive) {
+ if (mProximitySensorEnabled) {
+ if (mPendingProximity == PROXIMITY_NEGATIVE && !positive) {
+ return; // no change
+ }
+ if (mPendingProximity == PROXIMITY_POSITIVE && positive) {
+ return; // no change
+ }
+
+ // Only accept a proximity sensor reading if it remains
+ // stable for the entire debounce delay. We hold a wake lock while
+ // debouncing the sensor.
+ mHandler.removeMessages(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ if (positive) {
+ mPendingProximity = PROXIMITY_POSITIVE;
+ setPendingProximityDebounceTime(
+ time + PROXIMITY_SENSOR_POSITIVE_DEBOUNCE_DELAY); // acquire wake lock
+ } else {
+ mPendingProximity = PROXIMITY_NEGATIVE;
+ setPendingProximityDebounceTime(
+ time + PROXIMITY_SENSOR_NEGATIVE_DEBOUNCE_DELAY); // acquire wake lock
+ }
+
+ // Debounce the new sensor reading.
+ debounceProximitySensor();
+ }
+ }
+
+ private void debounceProximitySensor() {
+ if (mProximitySensorEnabled
+ && mPendingProximity != PROXIMITY_UNKNOWN
+ && mPendingProximityDebounceTime >= 0) {
+ final long now = mClock.uptimeMillis();
+ if (mPendingProximityDebounceTime <= now) {
+ if (mProximity != mPendingProximity) {
+ // if the status of the sensor changed, stop ignoring.
+ mIgnoreProximityUntilChanged = false;
+ Slog.i(mTag, "No longer ignoring proximity [" + mPendingProximity + "]");
+ }
+ // Sensor reading accepted. Apply the change then release the wake lock.
+ mProximity = mPendingProximity;
+ updatePowerState();
+ clearPendingProximityDebounceTime(); // release wake lock (must be last)
+ } else {
+ // Need to wait a little longer.
+ // Debounce again later. We continue holding a wake lock while waiting.
+ Message msg = mHandler.obtainMessage(MSG_PROXIMITY_SENSOR_DEBOUNCED);
+ mHandler.sendMessageAtTime(msg, mPendingProximityDebounceTime);
+ }
+ }
+ }
+
+ private void clearPendingProximityDebounceTime() {
+ if (mPendingProximityDebounceTime >= 0) {
+ mPendingProximityDebounceTime = -1;
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxDebounce);
+ }
+ }
+
+ private void setPendingProximityDebounceTime(long debounceTime) {
+ if (mPendingProximityDebounceTime < 0) {
+ mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxDebounce);
+ }
+ mPendingProximityDebounceTime = debounceTime;
+ }
+
+ private void sendOnStateChangedWithWakelock() {
+ if (!mOnStateChangedPending) {
+ mOnStateChangedPending = true;
+ mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdOnStateChanged);
+ mHandler.post(mOnStateChangedRunnable);
+ }
+ }
+
+ private void logDisplayPolicyChanged(int newPolicy) {
+ LogMaker log = new LogMaker(MetricsEvent.DISPLAY_POLICY);
+ log.setType(MetricsEvent.TYPE_UPDATE);
+ log.setSubtype(newPolicy);
+ MetricsLogger.action(log);
+ }
+
+ private void handleSettingsChange(boolean userSwitch) {
+ mPendingScreenBrightnessSetting = getScreenBrightnessSetting();
+ mPendingAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
+ if (userSwitch) {
+ // Don't treat user switches as user initiated change.
+ setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
+ updateAutoBrightnessAdjustment();
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.resetShortTermModel();
+ }
+ }
+ // We don't bother with a pending variable for VR screen brightness since we just
+ // immediately adapt to it.
+ mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
+ sendUpdatePowerState();
+ }
+
+ private float getAutoBrightnessAdjustmentSetting() {
+ final float adj = Settings.System.getFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, 0.0f, UserHandle.USER_CURRENT);
+ return Float.isNaN(adj) ? 0.0f : clampAutoBrightnessAdjustment(adj);
+ }
+
+ @Override
+ public float getScreenBrightnessSetting() {
+ float brightness = mBrightnessSetting.getBrightness();
+ if (Float.isNaN(brightness)) {
+ brightness = mScreenBrightnessDefault;
+ }
+ return clampAbsoluteBrightness(brightness);
+ }
+
+ private float getScreenBrightnessForVrSetting() {
+ final float brightnessFloat = Settings.System.getFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_FOR_VR_FLOAT, mScreenBrightnessForVrDefault,
+ UserHandle.USER_CURRENT);
+ return clampScreenBrightnessForVr(brightnessFloat);
+ }
+
+ @Override
+ public void setBrightness(float brightnessValue) {
+ // Update the setting, which will eventually call back into DPC to have us actually update
+ // the display with the new value.
+ mBrightnessSetting.setBrightness(brightnessValue);
+ }
+
+ private void updateScreenBrightnessSetting(float brightnessValue) {
+ if (!isValidBrightnessValue(brightnessValue)
+ || brightnessValue == mCurrentScreenBrightnessSetting) {
+ return;
+ }
+ setCurrentScreenBrightness(brightnessValue);
+ mBrightnessSetting.setBrightness(brightnessValue);
+ }
+
+ private void setCurrentScreenBrightness(float brightnessValue) {
+ if (brightnessValue != mCurrentScreenBrightnessSetting) {
+ mCurrentScreenBrightnessSetting = brightnessValue;
+ postBrightnessChangeRunnable();
+ }
+ }
+
+ private void putAutoBrightnessAdjustmentSetting(float adjustment) {
+ if (mDisplayId == Display.DEFAULT_DISPLAY) {
+ mAutoBrightnessAdjustment = adjustment;
+ Settings.System.putFloatForUser(mContext.getContentResolver(),
+ Settings.System.SCREEN_AUTO_BRIGHTNESS_ADJ, adjustment,
+ UserHandle.USER_CURRENT);
+ }
+ }
+
+ private boolean updateAutoBrightnessAdjustment() {
+ if (Float.isNaN(mPendingAutoBrightnessAdjustment)) {
+ return false;
+ }
+ if (mAutoBrightnessAdjustment == mPendingAutoBrightnessAdjustment) {
+ mPendingAutoBrightnessAdjustment = Float.NaN;
+ return false;
+ }
+ mAutoBrightnessAdjustment = mPendingAutoBrightnessAdjustment;
+ mPendingAutoBrightnessAdjustment = Float.NaN;
+ mTemporaryAutoBrightnessAdjustment = Float.NaN;
+ return true;
+ }
+
+ // We want to return true if the user has set the screen brightness.
+ // RBC on, off, and intensity changes will return false.
+ // Slider interactions whilst in RBC will return true, just as when in non-rbc.
+ private boolean updateUserSetScreenBrightness() {
+ if ((Float.isNaN(mPendingScreenBrightnessSetting)
+ || mPendingScreenBrightnessSetting < 0.0f)) {
+ return false;
+ }
+ if (mCurrentScreenBrightnessSetting == mPendingScreenBrightnessSetting) {
+ mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ return false;
+ }
+ setCurrentScreenBrightness(mPendingScreenBrightnessSetting);
+ mLastUserSetScreenBrightness = mPendingScreenBrightnessSetting;
+ mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ return true;
+ }
+
+ private void notifyBrightnessTrackerChanged(float brightness, boolean userInitiated,
+ boolean hadUserDataPoint) {
+ final float brightnessInNits = convertToNits(brightness);
+ if (mPowerRequest.useAutoBrightness && brightnessInNits >= 0.0f
+ && mAutomaticBrightnessController != null) {
+ // We only want to track changes on devices that can actually map the display backlight
+ // values into a physical brightness unit since the value provided by the API is in
+ // nits and not using the arbitrary backlight units.
+ final float powerFactor = mPowerRequest.lowPowerMode
+ ? mPowerRequest.screenLowPowerBrightnessFactor
+ : 1.0f;
+ mBrightnessTracker.notifyBrightnessChanged(brightnessInNits, userInitiated,
+ powerFactor, hadUserDataPoint,
+ mAutomaticBrightnessController.isDefaultConfig(), mUniqueDisplayId);
+ }
+ }
+
+ private float convertToNits(float brightness) {
+ if (mAutomaticBrightnessController == null) {
+ return -1f;
+ }
+ return mAutomaticBrightnessController.convertToNits(brightness);
+ }
+
+ @GuardedBy("mLock")
+ private void updatePendingProximityRequestsLocked() {
+ mWaitingForNegativeProximity |= mPendingWaitForNegativeProximityLocked;
+ mPendingWaitForNegativeProximityLocked = false;
+
+ if (mIgnoreProximityUntilChanged) {
+ // Also, lets stop waiting for negative proximity if we're ignoring it.
+ mWaitingForNegativeProximity = false;
+ }
+ }
+
+ private void ignoreProximitySensorUntilChangedInternal() {
+ if (!mIgnoreProximityUntilChanged
+ && mProximity == PROXIMITY_POSITIVE) {
+ // Only ignore if it is still reporting positive (near)
+ mIgnoreProximityUntilChanged = true;
+ Slog.i(mTag, "Ignoring proximity");
+ updatePowerState();
+ }
+ }
+
+ private final Runnable mOnStateChangedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mOnStateChangedPending = false;
+ mCallbacks.onStateChanged();
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdOnStateChanged);
+ }
+ };
+
+ private void sendOnProximityPositiveWithWakelock() {
+ mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxPositive);
+ mHandler.post(mOnProximityPositiveRunnable);
+ mOnProximityPositiveMessages++;
+ }
+
+ private final Runnable mOnProximityPositiveRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mOnProximityPositiveMessages--;
+ mCallbacks.onProximityPositive();
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxPositive);
+ }
+ };
+
+ private void sendOnProximityNegativeWithWakelock() {
+ mOnProximityNegativeMessages++;
+ mCallbacks.acquireSuspendBlocker(mSuspendBlockerIdProxNegative);
+ mHandler.post(mOnProximityNegativeRunnable);
+ }
+
+ private final Runnable mOnProximityNegativeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mOnProximityNegativeMessages--;
+ mCallbacks.onProximityNegative();
+ mCallbacks.releaseSuspendBlocker(mSuspendBlockerIdProxNegative);
+ }
+ };
+
+ @Override
+ public void dump(final PrintWriter pw) {
+ synchronized (mLock) {
+ pw.println();
+ pw.println("Display Power Controller:");
+ pw.println(" mDisplayId=" + mDisplayId);
+ pw.println(" mLightSensor=" + mLightSensor);
+
+ pw.println();
+ pw.println("Display Power Controller Locked State:");
+ pw.println(" mDisplayReadyLocked=" + mDisplayReadyLocked);
+ pw.println(" mPendingRequestLocked=" + mPendingRequestLocked);
+ pw.println(" mPendingRequestChangedLocked=" + mPendingRequestChangedLocked);
+ pw.println(" mPendingWaitForNegativeProximityLocked="
+ + mPendingWaitForNegativeProximityLocked);
+ pw.println(" mPendingUpdatePowerStateLocked=" + mPendingUpdatePowerStateLocked);
+ }
+
+ pw.println();
+ pw.println("Display Power Controller Configuration:");
+ pw.println(" mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault);
+ pw.println(" mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
+ pw.println(" mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
+ pw.println(" mScreenBrightnessForVrRangeMinimum=" + mScreenBrightnessForVrRangeMinimum);
+ pw.println(" mScreenBrightnessForVrRangeMaximum=" + mScreenBrightnessForVrRangeMaximum);
+ pw.println(" mScreenBrightnessForVrDefault=" + mScreenBrightnessForVrDefault);
+ pw.println(" mUseSoftwareAutoBrightnessConfig=" + mUseSoftwareAutoBrightnessConfig);
+ pw.println(" mAllowAutoBrightnessWhileDozingConfig="
+ + mAllowAutoBrightnessWhileDozingConfig);
+ pw.println(" mSkipScreenOnBrightnessRamp=" + mSkipScreenOnBrightnessRamp);
+ pw.println(" mColorFadeFadesConfig=" + mColorFadeFadesConfig);
+ pw.println(" mColorFadeEnabled=" + mColorFadeEnabled);
+ synchronized (mCachedBrightnessInfo) {
+ pw.println(" mCachedBrightnessInfo.brightness="
+ + mCachedBrightnessInfo.brightness.value);
+ pw.println(" mCachedBrightnessInfo.adjustedBrightness="
+ + mCachedBrightnessInfo.adjustedBrightness.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMin="
+ + mCachedBrightnessInfo.brightnessMin.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMax="
+ + mCachedBrightnessInfo.brightnessMax.value);
+ pw.println(" mCachedBrightnessInfo.hbmMode=" + mCachedBrightnessInfo.hbmMode.value);
+ pw.println(" mCachedBrightnessInfo.hbmTransitionPoint="
+ + mCachedBrightnessInfo.hbmTransitionPoint.value);
+ pw.println(" mCachedBrightnessInfo.brightnessMaxReason ="
+ + mCachedBrightnessInfo.brightnessMaxReason.value);
+ }
+ pw.println(" mDisplayBlanksAfterDozeConfig=" + mDisplayBlanksAfterDozeConfig);
+ pw.println(" mBrightnessBucketsInDozeConfig=" + mBrightnessBucketsInDozeConfig);
+
+ mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
+ }
+
+ private void dumpLocal(PrintWriter pw) {
+ pw.println();
+ pw.println("Display Power Controller Thread State:");
+ pw.println(" mPowerRequest=" + mPowerRequest);
+ pw.println(" mUnfinishedBusiness=" + mUnfinishedBusiness);
+ pw.println(" mWaitingForNegativeProximity=" + mWaitingForNegativeProximity);
+ pw.println(" mProximitySensor=" + mProximitySensor);
+ pw.println(" mProximitySensorEnabled=" + mProximitySensorEnabled);
+ pw.println(" mProximityThreshold=" + mProximityThreshold);
+ pw.println(" mProximity=" + proximityToString(mProximity));
+ pw.println(" mPendingProximity=" + proximityToString(mPendingProximity));
+ pw.println(" mPendingProximityDebounceTime="
+ + TimeUtils.formatUptime(mPendingProximityDebounceTime));
+ pw.println(" mScreenOffBecauseOfProximity=" + mScreenOffBecauseOfProximity);
+ pw.println(" mLastUserSetScreenBrightness=" + mLastUserSetScreenBrightness);
+ pw.println(" mPendingScreenBrightnessSetting="
+ + mPendingScreenBrightnessSetting);
+ pw.println(" mTemporaryScreenBrightness=" + mTemporaryScreenBrightness);
+ pw.println(" mAutoBrightnessAdjustment=" + mAutoBrightnessAdjustment);
+ pw.println(" mBrightnessReason=" + mBrightnessReason);
+ pw.println(" mTemporaryAutoBrightnessAdjustment=" + mTemporaryAutoBrightnessAdjustment);
+ pw.println(" mPendingAutoBrightnessAdjustment=" + mPendingAutoBrightnessAdjustment);
+ pw.println(" mScreenBrightnessForVrFloat=" + mScreenBrightnessForVr);
+ pw.println(" mAppliedAutoBrightness=" + mAppliedAutoBrightness);
+ pw.println(" mAppliedDimming=" + mAppliedDimming);
+ pw.println(" mAppliedLowPower=" + mAppliedLowPower);
+ pw.println(" mAppliedThrottling=" + mAppliedThrottling);
+ pw.println(" mAppliedScreenBrightnessOverride=" + mAppliedScreenBrightnessOverride);
+ pw.println(" mAppliedTemporaryBrightness=" + mAppliedTemporaryBrightness);
+ pw.println(" mAppliedTemporaryAutoBrightnessAdjustment="
+ + mAppliedTemporaryAutoBrightnessAdjustment);
+ pw.println(" mAppliedBrightnessBoost=" + mAppliedBrightnessBoost);
+ pw.println(" mDozing=" + mDozing);
+ pw.println(" mSkipRampState=" + skipRampStateToString(mSkipRampState));
+ pw.println(" mScreenOnBlockStartRealTime=" + mScreenOnBlockStartRealTime);
+ pw.println(" mScreenOffBlockStartRealTime=" + mScreenOffBlockStartRealTime);
+ pw.println(" mPendingScreenOnUnblocker=" + mPendingScreenOnUnblocker);
+ pw.println(" mPendingScreenOffUnblocker=" + mPendingScreenOffUnblocker);
+ pw.println(" mPendingScreenOff=" + mPendingScreenOff);
+ pw.println(" mReportedToPolicy="
+ + reportedToPolicyToString(mReportedScreenStateToPolicy));
+ pw.println(" mIsRbcActive=" + mIsRbcActive);
+ pw.println(" mOnStateChangePending=" + mOnStateChangedPending);
+ pw.println(" mOnProximityPositiveMessages=" + mOnProximityPositiveMessages);
+ pw.println(" mOnProximityNegativeMessages=" + mOnProximityNegativeMessages);
+
+ if (mScreenBrightnessRampAnimator != null) {
+ pw.println(" mScreenBrightnessRampAnimator.isAnimating()="
+ + mScreenBrightnessRampAnimator.isAnimating());
+ }
+
+ if (mColorFadeOnAnimator != null) {
+ pw.println(" mColorFadeOnAnimator.isStarted()="
+ + mColorFadeOnAnimator.isStarted());
+ }
+ if (mColorFadeOffAnimator != null) {
+ pw.println(" mColorFadeOffAnimator.isStarted()="
+ + mColorFadeOffAnimator.isStarted());
+ }
+
+ if (mPowerState != null) {
+ mPowerState.dump(pw);
+ }
+
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.dump(pw);
+ dumpBrightnessEvents(pw);
+ }
+
+ if (mHbmController != null) {
+ mHbmController.dump(pw);
+ }
+
+ if (mBrightnessThrottler != null) {
+ mBrightnessThrottler.dump(pw);
+ }
+
+ pw.println();
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.dump(pw);
+ mDisplayWhiteBalanceSettings.dump(pw);
+ }
+ }
+
+ private static String proximityToString(int state) {
+ switch (state) {
+ case PROXIMITY_UNKNOWN:
+ return "Unknown";
+ case PROXIMITY_NEGATIVE:
+ return "Negative";
+ case PROXIMITY_POSITIVE:
+ return "Positive";
+ default:
+ return Integer.toString(state);
+ }
+ }
+
+ private static String reportedToPolicyToString(int state) {
+ switch (state) {
+ case REPORTED_TO_POLICY_SCREEN_OFF:
+ return "REPORTED_TO_POLICY_SCREEN_OFF";
+ case REPORTED_TO_POLICY_SCREEN_TURNING_ON:
+ return "REPORTED_TO_POLICY_SCREEN_TURNING_ON";
+ case REPORTED_TO_POLICY_SCREEN_ON:
+ return "REPORTED_TO_POLICY_SCREEN_ON";
+ default:
+ return Integer.toString(state);
+ }
+ }
+
+ private static String skipRampStateToString(int state) {
+ switch (state) {
+ case RAMP_STATE_SKIP_NONE:
+ return "RAMP_STATE_SKIP_NONE";
+ case RAMP_STATE_SKIP_INITIAL:
+ return "RAMP_STATE_SKIP_INITIAL";
+ case RAMP_STATE_SKIP_AUTOBRIGHT:
+ return "RAMP_STATE_SKIP_AUTOBRIGHT";
+ default:
+ return Integer.toString(state);
+ }
+ }
+
+ private void dumpBrightnessEvents(PrintWriter pw) {
+ int size = mBrightnessEventRingBuffer.size();
+ if (size < 1) {
+ pw.println("No Automatic Brightness Adjustments");
+ return;
+ }
+
+ pw.println("Automatic Brightness Adjustments Last " + size + " Events: ");
+ BrightnessEvent[] eventArray = mBrightnessEventRingBuffer.toArray();
+ for (int i = 0; i < mBrightnessEventRingBuffer.size(); i++) {
+ pw.println(" " + eventArray[i].toString());
+ }
+ }
+
+ private static float clampAbsoluteBrightness(float value) {
+ return MathUtils.constrain(value, PowerManager.BRIGHTNESS_MIN,
+ PowerManager.BRIGHTNESS_MAX);
+ }
+
+ private static float clampAutoBrightnessAdjustment(float value) {
+ return MathUtils.constrain(value, -1.0f, 1.0f);
+ }
+
+ private void noteScreenState(int screenState) {
+ if (mBatteryStats != null) {
+ try {
+ // TODO(multi-display): make this multi-display
+ mBatteryStats.noteScreenState(screenState);
+ } catch (RemoteException e) {
+ // same process
+ }
+ }
+ }
+
+ private void noteScreenBrightness(float brightness) {
+ if (mBatteryStats != null) {
+ try {
+ // TODO(brightnessfloat): change BatteryStats to use float
+ mBatteryStats.noteScreenBrightness(BrightnessSynchronizer.brightnessFloatToInt(
+ brightness));
+ } catch (RemoteException e) {
+ // same process
+ }
+ }
+ }
+
+ private void reportStats(float brightness) {
+ if (mLastStatsBrightness == brightness) {
+ return;
+ }
+
+ float hbmTransitionPoint = PowerManager.BRIGHTNESS_MAX;
+ synchronized (mCachedBrightnessInfo) {
+ if (mCachedBrightnessInfo.hbmTransitionPoint == null) {
+ return;
+ }
+ hbmTransitionPoint = mCachedBrightnessInfo.hbmTransitionPoint.value;
+ }
+
+ final boolean aboveTransition = brightness > hbmTransitionPoint;
+ final boolean oldAboveTransition = mLastStatsBrightness > hbmTransitionPoint;
+
+ if (aboveTransition || oldAboveTransition) {
+ mLastStatsBrightness = brightness;
+ mHandler.removeMessages(MSG_STATSD_HBM_BRIGHTNESS);
+ if (aboveTransition != oldAboveTransition) {
+ // report immediately
+ logHbmBrightnessStats(brightness, mDisplayStatsId);
+ } else {
+ // delay for rate limiting
+ Message msg = mHandler.obtainMessage();
+ msg.what = MSG_STATSD_HBM_BRIGHTNESS;
+ msg.arg1 = Float.floatToIntBits(brightness);
+ msg.arg2 = mDisplayStatsId;
+ mHandler.sendMessageDelayed(msg, BRIGHTNESS_CHANGE_STATSD_REPORT_INTERVAL_MS);
+ }
+ }
+ }
+
+ private void logHbmBrightnessStats(float brightness, int displayStatsId) {
+ synchronized (mHandler) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.DISPLAY_HBM_BRIGHTNESS_CHANGED, displayStatsId, brightness);
+ }
+ }
+
+ private void logManualBrightnessEvent(BrightnessEvent event) {
+ float appliedLowPowerMode = event.isLowPowerModeSet() ? event.getPowerFactor() : -1f;
+ int appliedRbcStrength = event.isRbcEnabled() ? event.getRbcStrength() : -1;
+ float appliedHbmMaxNits =
+ event.getHbmMode() == BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF
+ ? -1f : convertToNits(event.getHbmMax());
+ // thermalCapNits set to -1 if not currently capping max brightness
+ float appliedThermalCapNits =
+ event.getThermalMax() == PowerManager.BRIGHTNESS_MAX
+ ? -1f : convertToNits(event.getThermalMax());
+
+ FrameworkStatsLog.write(FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED,
+ convertToNits(event.getInitialBrightness()),
+ convertToNits(event.getBrightness()),
+ event.getSlowAmbientLux(),
+ event.getPhysicalDisplayId(),
+ event.isShortTermModelActive(),
+ appliedLowPowerMode,
+ appliedRbcStrength,
+ appliedHbmMaxNits,
+ appliedThermalCapNits,
+ event.isAutomaticBrightnessEnabled(),
+ FrameworkStatsLog.DISPLAY_BRIGHTNESS_CHANGED__REASON__REASON_MANUAL);
+ }
+
+ private final class DisplayControllerHandler extends Handler {
+ DisplayControllerHandler(Looper looper) {
+ super(looper, null, true /*async*/);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_POWER_STATE:
+ updatePowerState();
+ break;
+
+ case MSG_PROXIMITY_SENSOR_DEBOUNCED:
+ debounceProximitySensor();
+ break;
+
+ case MSG_SCREEN_ON_UNBLOCKED:
+ if (mPendingScreenOnUnblocker == msg.obj) {
+ unblockScreenOn();
+ updatePowerState();
+ }
+ break;
+ case MSG_SCREEN_OFF_UNBLOCKED:
+ if (mPendingScreenOffUnblocker == msg.obj) {
+ unblockScreenOff();
+ updatePowerState();
+ }
+ break;
+ case MSG_CONFIGURE_BRIGHTNESS:
+ mBrightnessConfiguration = (BrightnessConfiguration) msg.obj;
+ updatePowerState();
+ break;
+
+ case MSG_SET_TEMPORARY_BRIGHTNESS:
+ // TODO: Should we have a a timeout for the temporary brightness?
+ mTemporaryScreenBrightness = Float.intBitsToFloat(msg.arg1);
+ updatePowerState();
+ break;
+
+ case MSG_SET_TEMPORARY_AUTO_BRIGHTNESS_ADJUSTMENT:
+ mTemporaryAutoBrightnessAdjustment = Float.intBitsToFloat(msg.arg1);
+ updatePowerState();
+ break;
+
+ case MSG_IGNORE_PROXIMITY:
+ ignoreProximitySensorUntilChangedInternal();
+ break;
+
+ case MSG_STOP:
+ cleanupHandlerThreadAfterStop();
+ break;
+
+ case MSG_UPDATE_BRIGHTNESS:
+ if (mStopped) {
+ return;
+ }
+ handleSettingsChange(false /*userSwitch*/);
+ break;
+
+ case MSG_UPDATE_RBC:
+ handleRbcChanged();
+ break;
+
+ case MSG_BRIGHTNESS_RAMP_DONE:
+ if (mPowerState != null) {
+ final float brightness = mPowerState.getScreenBrightness();
+ reportStats(brightness);
+ }
+ break;
+
+ case MSG_STATSD_HBM_BRIGHTNESS:
+ logHbmBrightnessStats(Float.intBitsToFloat(msg.arg1), msg.arg2);
+ break;
+ }
+ }
+ }
+
+ private final SensorEventListener mProximitySensorListener = new SensorEventListener() {
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ if (mProximitySensorEnabled) {
+ final long time = mClock.uptimeMillis();
+ final float distance = event.values[0];
+ boolean positive = distance >= 0.0f && distance < mProximityThreshold;
+ handleProximitySensorEvent(time, positive);
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ // Not used.
+ }
+ };
+
+
+ private final class SettingsObserver extends ContentObserver {
+ SettingsObserver(Handler handler) {
+ super(handler);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ handleSettingsChange(false /* userSwitch */);
+ }
+ }
+
+ private final class ScreenOnUnblocker implements WindowManagerPolicy.ScreenOnListener {
+ @Override
+ public void onScreenOn() {
+ Message msg = mHandler.obtainMessage(MSG_SCREEN_ON_UNBLOCKED, this);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ private final class ScreenOffUnblocker implements WindowManagerPolicy.ScreenOffListener {
+ @Override
+ public void onScreenOff() {
+ Message msg = mHandler.obtainMessage(MSG_SCREEN_OFF_UNBLOCKED, this);
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void setAutoBrightnessLoggingEnabled(boolean enabled) {
+ if (mAutomaticBrightnessController != null) {
+ mAutomaticBrightnessController.setLoggingEnabled(enabled);
+ }
+ }
+
+ @Override // DisplayWhiteBalanceController.Callbacks
+ public void updateWhiteBalance() {
+ sendUpdatePowerState();
+ }
+
+ @Override
+ public void setDisplayWhiteBalanceLoggingEnabled(boolean enabled) {
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setLoggingEnabled(enabled);
+ mDisplayWhiteBalanceSettings.setLoggingEnabled(enabled);
+ }
+ }
+
+ @Override
+ public void setAmbientColorTemperatureOverride(float cct) {
+ if (mDisplayWhiteBalanceController != null) {
+ mDisplayWhiteBalanceController.setAmbientColorTemperatureOverride(cct);
+ // The ambient color temperature override is only applied when the ambient color
+ // temperature changes or is updated, so it doesn't necessarily change the screen color
+ // temperature immediately. So, let's make it!
+ sendUpdatePowerState();
+ }
+ }
+
+ @VisibleForTesting
+ String getSuspendBlockerUnfinishedBusinessId(int displayId) {
+ return "[" + displayId + "]unfinished business";
+ }
+
+ String getSuspendBlockerOnStateChangedId(int displayId) {
+ return "[" + displayId + "]on state changed";
+ }
+
+ String getSuspendBlockerProxPositiveId(int displayId) {
+ return "[" + displayId + "]prox positive";
+ }
+
+ String getSuspendBlockerProxNegativeId(int displayId) {
+ return "[" + displayId + "]prox negative";
+ }
+
+ @VisibleForTesting
+ String getSuspendBlockerProxDebounceId(int displayId) {
+ return "[" + displayId + "]prox debounce";
+ }
+
+ /** Functional interface for providing time. */
+ @VisibleForTesting
+ interface Clock {
+ /**
+ * Returns current time in milliseconds since boot, not counting time spent in deep sleep.
+ */
+ long uptimeMillis();
+ }
+
+ @VisibleForTesting
+ static class Injector {
+ Clock getClock() {
+ return SystemClock::uptimeMillis;
+ }
+
+ DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+ int displayId, int displayState) {
+ return new DisplayPowerState(blanker, colorFade, displayId, displayState);
+ }
+
+ DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+ FloatProperty<DisplayPowerState> firstProperty,
+ FloatProperty<DisplayPowerState> secondProperty) {
+ return new DualRampAnimator(dps, firstProperty, secondProperty);
+ }
+ }
+
+ static class CachedBrightnessInfo {
+ public MutableFloat brightness = new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableFloat adjustedBrightness =
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableFloat brightnessMin =
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableFloat brightnessMax =
+ new MutableFloat(PowerManager.BRIGHTNESS_INVALID_FLOAT);
+ public MutableInt hbmMode = new MutableInt(BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF);
+ public MutableFloat hbmTransitionPoint =
+ new MutableFloat(HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID);
+ public MutableInt brightnessMaxReason =
+ new MutableInt(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
+
+ public boolean checkAndSetFloat(MutableFloat mf, float f) {
+ if (mf.value != f) {
+ mf.value = f;
+ return true;
+ }
+ return false;
+ }
+
+ public boolean checkAndSetInt(MutableInt mi, int i) {
+ if (mi.value != i) {
+ mi.value = i;
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
new file mode 100644
index 000000000000..6677f358557d
--- /dev/null
+++ b/services/core/java/com/android/server/display/DisplayPowerControllerInterface.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.content.pm.ParceledListSlice;
+import android.hardware.display.AmbientBrightnessDayStats;
+import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.BrightnessConfiguration;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
+
+import java.io.PrintWriter;
+
+/**
+ * An interface to manage the display's power state and brightness
+ */
+public interface DisplayPowerControllerInterface {
+
+ /**
+ * Notified when the display is changed. We use this to apply any changes that might be needed
+ * when displays get swapped on foldable devices.
+ */
+ void onDisplayChanged();
+
+ /**
+ * Unregisters all listeners and interrupts all running threads; halting future work.
+ *
+ * This method should be called when the DisplayPowerController is no longer in use; i.e. when
+ * the display has been removed.
+ */
+ void stop();
+
+ /**
+ * Used to manage the displays preparing to transition from one device state to another.
+ */
+ void onDeviceStateTransition();
+
+ /**
+ * Used to update the display's BrightnessConfiguration
+ * @param config The new BrightnessConfiguration
+ */
+ void setBrightnessConfiguration(BrightnessConfiguration config);
+
+ /**
+ * Used to set the ambient color temperature of the Display
+ * @param ambientColorTemperature The target ambientColorTemperature
+ */
+ void setAmbientColorTemperatureOverride(float ambientColorTemperature);
+
+ /**
+ * Used to decide the associated AutomaticBrightnessController's BrightnessMode
+ * @param isIdle Flag which represents if the Idle BrightnessMode is to be set
+ */
+ void setAutomaticScreenBrightnessMode(boolean isIdle);
+
+ /**
+ * Used to enable/disable the logging of the WhileBalance associated entities
+ * @param enabled Flag which represents if the logging is the be enabled
+ */
+ void setDisplayWhiteBalanceLoggingEnabled(boolean enabled);
+
+ /**
+ * Used to dump the state.
+ * @param writer The PrintWriter used to dump the state.
+ */
+ void dump(PrintWriter writer);
+
+ /**
+ * Used to get the ambient brightness stats
+ */
+ ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId);
+
+ /**
+ * Get the default brightness configuration
+ */
+ BrightnessConfiguration getDefaultBrightnessConfiguration();
+
+ /**
+ * Set the screen brightness of the associated display
+ * @param brightness The value to which the brightness is to be set
+ */
+ void setBrightness(float brightness);
+
+ /**
+ * Checks if the proximity sensor is available
+ */
+ boolean isProximitySensorAvailable();
+
+ /**
+ * Persist the brightness slider events and ambient brightness stats to disk.
+ */
+ void persistBrightnessTrackerState();
+
+ /**
+ * Ignores the proximity sensor until the sensor state changes, but only if the sensor is
+ * currently enabled and forcing the screen to be dark.
+ */
+ void ignoreProximitySensorUntilChanged();
+
+ /**
+ * Requests a new power state.
+ *
+ * @param request The requested power state.
+ * @param waitForNegativeProximity If true, issues a request to wait for
+ * negative proximity before turning the screen back on,
+ * assuming the screen was turned off by the proximity sensor.
+ * @return True if display is ready, false if there are important changes that must
+ * be made asynchronously.
+ */
+ boolean requestPowerState(DisplayManagerInternal.DisplayPowerRequest request,
+ boolean waitForNegativeProximity);
+
+ /**
+ * Sets up the temporary autobrightness adjustment when the user is yet to settle down to a
+ * value.
+ */
+ void setTemporaryAutoBrightnessAdjustment(float adjustment);
+
+ /**
+ * Gets the screen brightness setting
+ */
+ float getScreenBrightnessSetting();
+
+ /**
+ * Sets up the temporary brightness for the associated display
+ */
+ void setTemporaryBrightness(float brightness);
+
+ /**
+ * Gets the associated {@link BrightnessInfo}
+ */
+ BrightnessInfo getBrightnessInfo();
+
+ /**
+ * Get the {@link BrightnessChangeEvent}s for the specified user.
+ */
+ ParceledListSlice<BrightnessChangeEvent> getBrightnessEvents(int userId, boolean hasUsageStats);
+
+ /**
+ * Sets up the logging for the associated {@link AutomaticBrightnessController}
+ * @param enabled Flag to represent if the logging is to be enabled
+ */
+ void setAutoBrightnessLoggingEnabled(boolean enabled);
+
+ /**
+ * Handles the changes to be done to update the brightness when the user is changed
+ * @param newUserId The new userId
+ */
+ void onSwitchUser(int newUserId);
+}
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index 7a932ce6d7cf..abf8fe3d3f17 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -18,15 +18,12 @@ package com.android.server.display;
import android.util.Slog;
-import com.android.internal.annotations.VisibleForTesting;
-
import java.io.PrintWriter;
import java.util.Arrays;
/**
* A helper class for handling access to illuminance hysteresis level values.
*/
-@VisibleForTesting
public class HysteresisLevels {
private static final String TAG = "HysteresisLevels";
@@ -39,8 +36,7 @@ public class HysteresisLevels {
private final float mMinBrightening;
/**
- * Creates a {@code HysteresisLevels} object with the given equal-length
- * integer arrays.
+ * Creates a {@code HysteresisLevels} object for ambient brightness.
* @param brighteningThresholds an array of brightening hysteresis constraint constants.
* @param darkeningThresholds an array of darkening hysteresis constraint constants.
* @param thresholdLevels a monotonically increasing array of threshold levels.
@@ -62,6 +58,28 @@ public class HysteresisLevels {
}
/**
+ * Creates a {@code HysteresisLevels} object for screen brightness.
+ * @param brighteningThresholds an array of brightening hysteresis constraint constants.
+ * @param darkeningThresholds an array of darkening hysteresis constraint constants.
+ * @param thresholdLevels a monotonically increasing array of threshold levels.
+ * @param minBrighteningThreshold the minimum value for which the brightening value needs to
+ * return.
+ * @param minDarkeningThreshold the minimum value for which the darkening value needs to return.
+ */
+ HysteresisLevels(int[] brighteningThresholds, int[] darkeningThresholds,
+ float[] thresholdLevels, float minDarkeningThreshold, float minBrighteningThreshold) {
+ if (brighteningThresholds.length != darkeningThresholds.length
+ || darkeningThresholds.length != thresholdLevels.length + 1) {
+ throw new IllegalArgumentException("Mismatch between hysteresis array lengths.");
+ }
+ mBrighteningThresholds = setArrayFormat(brighteningThresholds, 1000.0f);
+ mDarkeningThresholds = setArrayFormat(darkeningThresholds, 1000.0f);
+ mThresholdLevels = constraintInRangeIfNeeded(thresholdLevels);
+ mMinDarkening = minDarkeningThreshold;
+ mMinBrightening = minBrighteningThreshold;
+ }
+
+ /**
* Return the brightening hysteresis threshold for the given value level.
*/
public float getBrighteningThreshold(float value) {
@@ -107,11 +125,42 @@ public class HysteresisLevels {
private float[] setArrayFormat(int[] configArray, float divideFactor) {
float[] levelArray = new float[configArray.length];
for (int index = 0; levelArray.length > index; ++index) {
- levelArray[index] = (float)configArray[index] / divideFactor;
+ levelArray[index] = (float) configArray[index] / divideFactor;
}
return levelArray;
}
+ /**
+ * This check is due to historical reasons, where screen thresholdLevels used to be
+ * integer values in the range of [0-255], but then was changed to be float values from [0,1].
+ * To accommodate both the possibilities, we first check if all the thresholdLevels are in [0,
+ * 1], and if not, we divide all the levels with 255 to bring them down to the same scale.
+ */
+ private float[] constraintInRangeIfNeeded(float[] thresholdLevels) {
+ if (isAllInRange(thresholdLevels, /* minValueInclusive = */ 0.0f, /* maxValueInclusive = */
+ 1.0f)) {
+ return thresholdLevels;
+ }
+
+ Slog.w(TAG, "Detected screen thresholdLevels on a deprecated brightness scale");
+ float[] thresholdLevelsScaled = new float[thresholdLevels.length];
+ for (int index = 0; thresholdLevels.length > index; ++index) {
+ thresholdLevelsScaled[index] = thresholdLevels[index] / 255.0f;
+ }
+ return thresholdLevelsScaled;
+ }
+
+ private boolean isAllInRange(float[] configArray, float minValueInclusive,
+ float maxValueInclusive) {
+ int configArraySize = configArray.length;
+ for (int index = 0; configArraySize > index; ++index) {
+ if (configArray[index] < minValueInclusive || configArray[index] > maxValueInclusive) {
+ return false;
+ }
+ }
+ return true;
+ }
+
void dump(PrintWriter pw) {
pw.println("HysteresisLevels");
pw.println(" mBrighteningThresholds=" + Arrays.toString(mBrighteningThresholds));
diff --git a/services/core/java/com/android/server/display/TEST_MAPPING b/services/core/java/com/android/server/display/TEST_MAPPING
index 66ec5c4b1525..57c2e01297e6 100644
--- a/services/core/java/com/android/server/display/TEST_MAPPING
+++ b/services/core/java/com/android/server/display/TEST_MAPPING
@@ -7,6 +7,15 @@
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
+ },
+ {
+ "name": "FrameworksServicesTests",
+ "options": [
+ {"include-filter": "com.android.server.display"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
+ ]
}
]
} \ No newline at end of file
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 38728ce106af..20b82c3e3c97 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -161,6 +161,13 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
}
}
+ void setDisplayIdToMirror(IBinder appToken, int displayId) {
+ VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
+ if (device != null) {
+ device.setDisplayIdToMirror(displayId);
+ }
+ }
+
public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
if (device != null) {
@@ -313,6 +320,15 @@ public class VirtualDisplayAdapter extends DisplayAdapter {
return mDisplayIdToMirror;
}
+ void setDisplayIdToMirror(int displayIdToMirror) {
+ if (mDisplayIdToMirror != displayIdToMirror) {
+ mDisplayIdToMirror = displayIdToMirror;
+ mInfo = null;
+ sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
+ sendTraversalRequestLocked();
+ }
+ }
+
@Override
public boolean isWindowManagerMirroringLocked() {
return mIsWindowManagerMirroring;
diff --git a/services/core/java/com/android/server/display/WifiDisplayController.java b/services/core/java/com/android/server/display/WifiDisplayController.java
index 5b204ad9c853..f6d06aa2fec0 100644
--- a/services/core/java/com/android/server/display/WifiDisplayController.java
+++ b/services/core/java/com/android/server/display/WifiDisplayController.java
@@ -885,7 +885,7 @@ final class WifiDisplayController implements DumpUtils.Dump {
}
});
}
- } else {
+ } else if (!networkInfo.isConnectedOrConnecting()) {
mConnectedDeviceGroupInfo = null;
// Disconnect if we lost the network while connecting or connected to a display.
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 b51f3f587659..21a851895c15 100644
--- a/services/core/java/com/android/server/display/color/ColorDisplayService.java
+++ b/services/core/java/com/android/server/display/color/ColorDisplayService.java
@@ -50,6 +50,7 @@ import android.database.ContentObserver;
import android.hardware.display.ColorDisplayManager;
import android.hardware.display.ColorDisplayManager.AutoMode;
import android.hardware.display.ColorDisplayManager.ColorMode;
+import android.hardware.display.DisplayManagerInternal;
import android.hardware.display.IColorDisplayManager;
import android.hardware.display.Time;
import android.net.Uri;
@@ -154,7 +155,8 @@ public final class ColorDisplayService extends SystemService {
@VisibleForTesting
final DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController =
- new DisplayWhiteBalanceTintController();
+ new DisplayWhiteBalanceTintController(
+ LocalServices.getService(DisplayManagerInternal.class));
private final NightDisplayTintController mNightDisplayTintController =
new NightDisplayTintController();
private final TintController mGlobalSaturationTintController =
diff --git a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
index 93a78c1507ad..c5dd6acdd008 100644
--- a/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
+++ b/services/core/java/com/android/server/display/color/DisplayWhiteBalanceTintController.java
@@ -16,6 +16,8 @@
package com.android.server.display.color;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.server.display.color.DisplayTransformManager.LEVEL_COLOR_MATRIX_DISPLAY_WHITE_BALANCE;
import android.annotation.NonNull;
@@ -24,10 +26,9 @@ import android.content.Context;
import android.content.res.Resources;
import android.graphics.ColorSpace;
import android.hardware.display.ColorDisplayManager;
+import android.hardware.display.DisplayManagerInternal;
import android.opengl.Matrix;
-import android.os.IBinder;
import android.util.Slog;
-import android.view.SurfaceControl;
import android.view.SurfaceControl.DisplayPrimaries;
import com.android.internal.R;
@@ -64,6 +65,12 @@ final class DisplayWhiteBalanceTintController extends TintController {
// This feature becomes disallowed if the device is in an unsupported strong/light state.
private boolean mIsAllowed = true;
+ private final DisplayManagerInternal mDisplayManagerInternal;
+
+ DisplayWhiteBalanceTintController(DisplayManagerInternal dm) {
+ mDisplayManagerInternal = dm;
+ }
+
@Override
public void setUp(Context context, boolean needsLinear) {
mSetUp = false;
@@ -287,12 +294,8 @@ final class DisplayWhiteBalanceTintController extends TintController {
}
private ColorSpace.Rgb getDisplayColorSpaceFromSurfaceControl() {
- final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
- if (displayToken == null) {
- return null;
- }
-
- DisplayPrimaries primaries = SurfaceControl.getDisplayNativePrimaries(displayToken);
+ DisplayPrimaries primaries =
+ mDisplayManagerInternal.getDisplayNativePrimaries(DEFAULT_DISPLAY);
if (primaries == null || primaries.red == null || primaries.green == null
|| primaries.blue == null || primaries.white == null) {
return null;
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index fee1f5c2a559..e1b18f2226b8 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -38,7 +38,6 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
-import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -66,6 +65,7 @@ import com.android.internal.util.DumpUtils;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.input.InputManagerInternal;
import com.android.server.wm.ActivityInterceptorCallback;
import com.android.server.wm.ActivityTaskManagerInternal;
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 99bbc3f2d86b..32ff5e220825 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -71,6 +71,9 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
private static final String TAG = "HdmiCecLocalDeviceAudioSystem";
private static final boolean WAKE_ON_HOTPLUG = false;
+ private static final int MAX_CHANNELS = 8;
+ private static final HashMap<Integer, List<Integer>> AUDIO_CODECS_MAP =
+ mapAudioCodecWithAudioFormat();
// Whether the System Audio Control feature is enabled or not. True by default.
@GuardedBy("mLock")
@@ -485,17 +488,17 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
}
- @AudioCodec int[] audioFormatCodes = parseAudioFormatCodes(message.getParams());
+ @AudioCodec int[] audioCodecs = parseAudioCodecs(message.getParams());
byte[] sadBytes;
if (config != null && config.size() > 0) {
- sadBytes = getSupportedShortAudioDescriptorsFromConfig(config, audioFormatCodes);
+ sadBytes = getSupportedShortAudioDescriptorsFromConfig(config, audioCodecs);
} else {
AudioDeviceInfo deviceInfo = getSystemAudioDeviceInfo();
if (deviceInfo == null) {
return Constants.ABORT_UNABLE_TO_DETERMINE;
}
- sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioFormatCodes);
+ sadBytes = getSupportedShortAudioDescriptors(deviceInfo, audioCodecs);
}
if (sadBytes.length == 0) {
@@ -508,11 +511,12 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
}
- private byte[] getSupportedShortAudioDescriptors(
- AudioDeviceInfo deviceInfo, @AudioCodec int[] audioFormatCodes) {
- ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length);
- for (@AudioCodec int audioFormatCode : audioFormatCodes) {
- byte[] sad = getSupportedShortAudioDescriptor(deviceInfo, audioFormatCode);
+ @VisibleForTesting
+ byte[] getSupportedShortAudioDescriptors(
+ AudioDeviceInfo deviceInfo, @AudioCodec int[] audioCodecs) {
+ ArrayList<byte[]> sads = new ArrayList<>(audioCodecs.length);
+ for (@AudioCodec int audioCodec : audioCodecs) {
+ byte[] sad = getSupportedShortAudioDescriptor(deviceInfo, audioCodec);
if (sad != null) {
if (sad.length == 3) {
@@ -520,7 +524,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
} else {
HdmiLogger.warning(
"Dropping Short Audio Descriptor with length %d for requested codec %x",
- sad.length, audioFormatCode);
+ sad.length, audioCodec);
}
}
}
@@ -528,7 +532,7 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
private byte[] getSupportedShortAudioDescriptorsFromConfig(
- List<DeviceConfig> deviceConfig, @AudioCodec int[] audioFormatCodes) {
+ List<DeviceConfig> deviceConfig, @AudioCodec int[] audioCodecs) {
DeviceConfig deviceConfigToUse = null;
String audioDeviceName = SystemProperties.get(
Constants.PROPERTY_SYSTEM_AUDIO_MODE_AUDIO_PORT,
@@ -544,13 +548,13 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
return new byte[0];
}
HashMap<Integer, byte[]> map = new HashMap<>();
- ArrayList<byte[]> sads = new ArrayList<>(audioFormatCodes.length);
+ ArrayList<byte[]> sads = new ArrayList<>(audioCodecs.length);
for (CodecSad codecSad : deviceConfigToUse.supportedCodecs) {
map.put(codecSad.audioCodec, codecSad.sad);
}
- for (int i = 0; i < audioFormatCodes.length; i++) {
- if (map.containsKey(audioFormatCodes[i])) {
- byte[] sad = map.get(audioFormatCodes[i]);
+ for (int i = 0; i < audioCodecs.length; i++) {
+ if (map.containsKey(audioCodecs[i])) {
+ byte[] sad = map.get(audioCodecs[i]);
if (sad != null && sad.length == 3) {
sads.add(sad);
}
@@ -572,42 +576,171 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
/**
* Returns a 3 byte short audio descriptor as described in CEC 1.4 table 29 or null if the
- * audioFormatCode is not supported.
+ * audioCodec is not supported.
*/
@Nullable
- private byte[] getSupportedShortAudioDescriptor(
- AudioDeviceInfo deviceInfo, @AudioCodec int audioFormatCode) {
- switch (audioFormatCode) {
- case Constants.AUDIO_CODEC_NONE: {
- return null;
- }
- case Constants.AUDIO_CODEC_LPCM: {
- return getLpcmShortAudioDescriptor(deviceInfo);
+ @VisibleForTesting
+ byte[] getSupportedShortAudioDescriptor(
+ AudioDeviceInfo deviceInfo, @AudioCodec int audioCodec) {
+ byte[] shortAudioDescriptor = new byte[3];
+
+ int[] deviceSupportedAudioFormats = deviceInfo.getEncodings();
+ // Return null when audioCodec or device does not support any audio formats.
+ if (!AUDIO_CODECS_MAP.containsKey(audioCodec) || deviceSupportedAudioFormats.length == 0) {
+ return null;
+ }
+ List<Integer> audioCodecSupportedAudioFormats = AUDIO_CODECS_MAP.get(audioCodec);
+
+ for (int supportedAudioFormat : deviceSupportedAudioFormats) {
+ if (audioCodecSupportedAudioFormats.contains(supportedAudioFormat)) {
+ // Initialise the first two bytes of short audio descriptor.
+ shortAudioDescriptor[0] = getFirstByteOfSAD(deviceInfo, audioCodec);
+ shortAudioDescriptor[1] = getSecondByteOfSAD(deviceInfo);
+ switch (audioCodec) {
+ case Constants.AUDIO_CODEC_NONE: {
+ return null;
+ }
+ case Constants.AUDIO_CODEC_LPCM: {
+ if (supportedAudioFormat == AudioFormat.ENCODING_PCM_16BIT) {
+ shortAudioDescriptor[2] = (byte) 0x01;
+ } else if (supportedAudioFormat
+ == AudioFormat.ENCODING_PCM_24BIT_PACKED) {
+ shortAudioDescriptor[2] = (byte) 0x04;
+ } else {
+ // Since no bit is reserved for these audio formats in LPCM codec.
+ shortAudioDescriptor[2] = (byte) 0x00;
+ }
+ return shortAudioDescriptor;
+ }
+ case Constants.AUDIO_CODEC_DD:
+ case Constants.AUDIO_CODEC_MPEG1:
+ case Constants.AUDIO_CODEC_MP3:
+ case Constants.AUDIO_CODEC_MPEG2:
+ case Constants.AUDIO_CODEC_AAC:
+ case Constants.AUDIO_CODEC_DTS: {
+ shortAudioDescriptor[2] = getThirdSadByteForCodecs2Through8(deviceInfo);
+ return shortAudioDescriptor;
+ }
+ case Constants.AUDIO_CODEC_DDP:
+ case Constants.AUDIO_CODEC_DTSHD:
+ case Constants.AUDIO_CODEC_TRUEHD: {
+ // Default value is 0x0 unless defined by Audio Codec Vendor.
+ shortAudioDescriptor[2] = (byte) 0x00;
+ return shortAudioDescriptor;
+ }
+ case Constants.AUDIO_CODEC_ATRAC:
+ case Constants.AUDIO_CODEC_ONEBITAUDIO:
+ case Constants.AUDIO_CODEC_DST:
+ case Constants.AUDIO_CODEC_WMAPRO:
+ // Not supported.
+ default: {
+ return null;
+ }
+ }
}
- // TODO(b/80297701): implement the rest of the codecs
- case Constants.AUDIO_CODEC_DD:
- case Constants.AUDIO_CODEC_MPEG1:
- case Constants.AUDIO_CODEC_MP3:
- case Constants.AUDIO_CODEC_MPEG2:
- case Constants.AUDIO_CODEC_AAC:
- case Constants.AUDIO_CODEC_DTS:
- case Constants.AUDIO_CODEC_ATRAC:
- case Constants.AUDIO_CODEC_ONEBITAUDIO:
- case Constants.AUDIO_CODEC_DDP:
- case Constants.AUDIO_CODEC_DTSHD:
- case Constants.AUDIO_CODEC_TRUEHD:
- case Constants.AUDIO_CODEC_DST:
- case Constants.AUDIO_CODEC_WMAPRO:
- default: {
- return null;
+ }
+ return null;
+ }
+
+ private static HashMap<Integer, List<Integer>> mapAudioCodecWithAudioFormat() {
+ // Mapping the values of @AudioCodec audio codecs with @AudioFormat audio formats.
+ HashMap<Integer, List<Integer>> audioCodecsMap = new HashMap<Integer, List<Integer>>();
+
+ audioCodecsMap.put(Constants.AUDIO_CODEC_NONE, List.of(AudioFormat.ENCODING_DEFAULT));
+ audioCodecsMap.put(
+ Constants.AUDIO_CODEC_LPCM,
+ List.of(
+ AudioFormat.ENCODING_PCM_8BIT,
+ AudioFormat.ENCODING_PCM_16BIT,
+ AudioFormat.ENCODING_PCM_FLOAT,
+ AudioFormat.ENCODING_PCM_24BIT_PACKED,
+ AudioFormat.ENCODING_PCM_32BIT));
+ audioCodecsMap.put(Constants.AUDIO_CODEC_DD, List.of(AudioFormat.ENCODING_AC3));
+ audioCodecsMap.put(Constants.AUDIO_CODEC_MPEG1, List.of(AudioFormat.ENCODING_AAC_HE_V1));
+ audioCodecsMap.put(Constants.AUDIO_CODEC_MPEG2, List.of(AudioFormat.ENCODING_AAC_HE_V2));
+ audioCodecsMap.put(Constants.AUDIO_CODEC_MP3, List.of(AudioFormat.ENCODING_MP3));
+ audioCodecsMap.put(Constants.AUDIO_CODEC_AAC, List.of(AudioFormat.ENCODING_AAC_LC));
+ audioCodecsMap.put(Constants.AUDIO_CODEC_DTS, List.of(AudioFormat.ENCODING_DTS));
+ audioCodecsMap.put(
+ Constants.AUDIO_CODEC_DDP,
+ List.of(AudioFormat.ENCODING_E_AC3, AudioFormat.ENCODING_E_AC3_JOC));
+ audioCodecsMap.put(Constants.AUDIO_CODEC_DTSHD, List.of(AudioFormat.ENCODING_DTS_HD));
+ audioCodecsMap.put(
+ Constants.AUDIO_CODEC_TRUEHD,
+ List.of(AudioFormat.ENCODING_DOLBY_TRUEHD, AudioFormat.ENCODING_DOLBY_MAT));
+
+ return audioCodecsMap;
+ }
+
+ private byte getFirstByteOfSAD(AudioDeviceInfo deviceInfo, @AudioCodec int audioCodec) {
+ byte firstByte = 0;
+ int maxNumberOfChannels = getMaxNumberOfChannels(deviceInfo);
+
+ // Fill bits 0-2 of the first byte.
+ firstByte |= (maxNumberOfChannels - 1);
+
+ // Fill bits 3-6 of the first byte.
+ firstByte |= (audioCodec << 3);
+
+ return firstByte;
+ }
+
+ private byte getSecondByteOfSAD(AudioDeviceInfo deviceInfo) {
+ ArrayList<Integer> samplingRates =
+ new ArrayList<Integer>(Arrays.asList(32, 44, 48, 88, 96, 176, 192));
+
+ // samplingRatesdevicesupports is guaranteed to be not null
+ int[] samplingRatesDeviceSupports = deviceInfo.getSampleRates();
+ if (samplingRatesDeviceSupports.length == 0) {
+ Slog.e(TAG, "Device supports arbitrary rates");
+ // Since device supports arbitrary rates, we will return 0x7f since bit 7 is reserved.
+ return (byte) 0x7f;
+ }
+ byte secondByte = 0;
+ for (int supportedSampleRate : samplingRatesDeviceSupports) {
+ if (samplingRates.contains(supportedSampleRate)) {
+ int index = samplingRates.indexOf(supportedSampleRate);
+ // Setting the bit of a sample rate which is being supported.
+ secondByte |= (1 << index);
}
}
+
+ return secondByte;
}
- @Nullable
- private byte[] getLpcmShortAudioDescriptor(AudioDeviceInfo deviceInfo) {
- // TODO(b/80297701): implement
- return null;
+ /**
+ * Empty array from deviceInfo.getChannelCounts() implies device supports arbitrary channel
+ * counts and hence we assume max channels are supported by the device.
+ */
+ private int getMaxNumberOfChannels(AudioDeviceInfo deviceInfo) {
+ int maxNumberOfChannels = MAX_CHANNELS;
+ int[] channelCounts = deviceInfo.getChannelCounts();
+ if (channelCounts.length != 0) {
+ maxNumberOfChannels = channelCounts[channelCounts.length - 1];
+ maxNumberOfChannels =
+ (maxNumberOfChannels > MAX_CHANNELS ? MAX_CHANNELS : maxNumberOfChannels);
+ }
+ return maxNumberOfChannels;
+ }
+
+ private byte getThirdSadByteForCodecs2Through8(AudioDeviceInfo deviceInfo) {
+ /*
+ * Here, we are assuming that max bit rate is closely equals to the max sampling rate the
+ * device supports.
+ */
+ int maxSamplingRate = 0;
+ int[] samplingRatesDeviceSupports = deviceInfo.getSampleRates();
+ if (samplingRatesDeviceSupports.length == 0) {
+ maxSamplingRate = 192;
+ } else {
+ for (int sampleRate : samplingRatesDeviceSupports) {
+ if (maxSamplingRate < sampleRate) {
+ maxSamplingRate = sampleRate;
+ }
+ }
+ }
+
+ return (byte) (maxSamplingRate / 8);
}
@Nullable
@@ -634,14 +767,14 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource {
}
@AudioCodec
- private int[] parseAudioFormatCodes(byte[] params) {
- @AudioCodec int[] audioFormatCodes = new int[params.length];
+ private int[] parseAudioCodecs(byte[] params) {
+ @AudioCodec int[] audioCodecs = new int[params.length];
for (int i = 0; i < params.length; i++) {
byte val = params[i];
- audioFormatCodes[i] =
- val >= 1 && val <= Constants.AUDIO_CODEC_MAX ? val : Constants.AUDIO_CODEC_NONE;
+ audioCodecs[i] =
+ val >= 1 && val <= Constants.AUDIO_CODEC_MAX ? val : Constants.AUDIO_CODEC_NONE;
}
- return audioFormatCodes;
+ return audioCodecs;
}
@Override
diff --git a/services/core/java/com/android/server/input/BatteryController.java b/services/core/java/com/android/server/input/BatteryController.java
index c57d7e79c12a..32f17dc18033 100644
--- a/services/core/java/com/android/server/input/BatteryController.java
+++ b/services/core/java/com/android/server/input/BatteryController.java
@@ -18,12 +18,17 @@ package com.android.server.input;
import android.annotation.BinderThread;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.BatteryState;
import android.hardware.input.IInputDeviceBatteryListener;
import android.hardware.input.InputManager;
+import android.os.Handler;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.UEventObserver;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
@@ -31,6 +36,8 @@ import android.util.Slog;
import android.view.InputDevice;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.input.BatteryController.UEventManager.UEventListener;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -41,25 +48,53 @@ import java.util.Set;
* A thread-safe component of {@link InputManagerService} responsible for managing the battery state
* of input devices.
*/
-final class BatteryController {
+final class BatteryController implements InputManager.InputDeviceListener {
private static final String TAG = BatteryController.class.getSimpleName();
// To enable these logs, run:
// 'adb shell setprop log.tag.BatteryController DEBUG' (requires restart)
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ @VisibleForTesting
+ static final long POLLING_PERIOD_MILLIS = 10_000; // 10 seconds
+
private final Object mLock = new Object();
private final Context mContext;
private final NativeInputManagerService mNative;
+ private final Handler mHandler;
+ private final UEventManager mUEventManager;
// Maps a pid to the registered listener record for that process. There can only be one battery
// listener per process.
@GuardedBy("mLock")
private final ArrayMap<Integer, ListenerRecord> mListenerRecords = new ArrayMap<>();
- BatteryController(Context context, NativeInputManagerService nativeService) {
+ // Maps a deviceId that is being monitored to the battery state for the device.
+ // This must be kept in sync with {@link #mListenerRecords}.
+ @GuardedBy("mLock")
+ private final ArrayMap<Integer, MonitoredDeviceState> mMonitoredDeviceStates = new ArrayMap<>();
+
+ @GuardedBy("mLock")
+ private boolean mIsPolling = false;
+ @GuardedBy("mLock")
+ private boolean mIsInteractive = true;
+
+ BatteryController(Context context, NativeInputManagerService nativeService, Looper looper) {
+ this(context, nativeService, looper, new UEventManager() {});
+ }
+
+ @VisibleForTesting
+ BatteryController(Context context, NativeInputManagerService nativeService, Looper looper,
+ UEventManager uEventManager) {
mContext = context;
mNative = nativeService;
+ mHandler = new Handler(looper);
+ mUEventManager = uEventManager;
+ }
+
+ void systemRunning() {
+ Objects.requireNonNull(mContext.getSystemService(InputManager.class))
+ .registerInputDeviceListener(this, mHandler);
}
/**
@@ -96,29 +131,63 @@ final class BatteryController {
+ " is already monitoring deviceId " + deviceId);
}
+ MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
+ if (deviceState == null) {
+ // This is the first listener that is monitoring this device.
+ deviceState = new MonitoredDeviceState(deviceId);
+ mMonitoredDeviceStates.put(deviceId, deviceState);
+ }
+
if (DEBUG) {
Slog.d(TAG, "Battery listener for pid " + pid
+ " is monitoring deviceId " + deviceId);
}
- notifyBatteryListener(deviceId, listenerRecord);
+ updatePollingLocked(true /*delayStart*/);
+ notifyBatteryListener(listenerRecord, deviceState);
}
}
- private void notifyBatteryListener(int deviceId, ListenerRecord record) {
- final long eventTime = SystemClock.uptimeMillis();
+ private static void notifyBatteryListener(ListenerRecord listenerRecord,
+ MonitoredDeviceState deviceState) {
try {
- record.mListener.onBatteryStateChanged(
- deviceId,
- hasBattery(deviceId),
- mNative.getBatteryStatus(deviceId),
- mNative.getBatteryCapacity(deviceId) / 100.f,
- eventTime);
+ listenerRecord.mListener.onBatteryStateChanged(
+ deviceState.mDeviceId,
+ deviceState.mHasBattery,
+ deviceState.mBatteryStatus,
+ deviceState.mBatteryCapacity,
+ deviceState.mLastUpdateTime);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to notify listener", e);
}
}
+ @GuardedBy("mLock")
+ private void notifyAllListenersForDeviceLocked(MonitoredDeviceState deviceState) {
+ mListenerRecords.forEach((pid, listenerRecord) -> {
+ if (listenerRecord.mMonitoredDevices.contains(deviceState.mDeviceId)) {
+ notifyBatteryListener(listenerRecord, deviceState);
+ }
+ });
+ }
+
+ @GuardedBy("mLock")
+ private void updatePollingLocked(boolean delayStart) {
+ if (mMonitoredDeviceStates.isEmpty() || !mIsInteractive) {
+ // Stop polling.
+ mIsPolling = false;
+ mHandler.removeCallbacks(this::handlePollEvent);
+ return;
+ }
+
+ if (mIsPolling) {
+ return;
+ }
+ // Start polling.
+ mIsPolling = true;
+ mHandler.postDelayed(this::handlePollEvent, delayStart ? POLLING_PERIOD_MILLIS : 0);
+ }
+
private boolean hasBattery(int deviceId) {
final InputDevice device =
Objects.requireNonNull(mContext.getSystemService(InputManager.class))
@@ -126,6 +195,12 @@ final class BatteryController {
return device != null && device.hasBattery();
}
+ @GuardedBy("mLock")
+ private MonitoredDeviceState getDeviceStateOrThrowLocked(int deviceId) {
+ return Objects.requireNonNull(mMonitoredDeviceStates.get(deviceId),
+ "Maps are out of sync: Cannot find device state for deviceId " + deviceId);
+ }
+
/**
* Unregister the battery listener for the given input device and stop monitoring its battery
* state. If there are no other input devices that this listener is monitoring, the listener is
@@ -170,12 +245,31 @@ final class BatteryController {
+ pid);
}
+ if (!hasRegisteredListenerForDeviceLocked(deviceId)) {
+ // There are no more listeners monitoring this device.
+ final MonitoredDeviceState deviceState = getDeviceStateOrThrowLocked(deviceId);
+ deviceState.stopMonitoring();
+ mMonitoredDeviceStates.remove(deviceId);
+ }
+
if (listenerRecord.mMonitoredDevices.isEmpty()) {
// There are no more devices being monitored by this listener.
listenerRecord.mListener.asBinder().unlinkToDeath(listenerRecord.mDeathRecipient, 0);
mListenerRecords.remove(pid);
if (DEBUG) Slog.d(TAG, "Battery listener removed for pid " + pid);
}
+
+ updatePollingLocked(false /*delayStart*/);
+ }
+
+ @GuardedBy("mLock")
+ private boolean hasRegisteredListenerForDeviceLocked(int deviceId) {
+ for (int i = 0; i < mListenerRecords.size(); i++) {
+ if (mListenerRecords.valueAt(i).mMonitoredDevices.contains(deviceId)) {
+ return true;
+ }
+ }
+ return false;
}
private void handleListeningProcessDied(int pid) {
@@ -194,10 +288,50 @@ final class BatteryController {
}
}
+ // Query the battery state for the device and notify all listeners if there is a change.
+ private void handleBatteryChangeNotification(int deviceId, long eventTime) {
+ synchronized (mLock) {
+ final MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
+ if (deviceState == null) {
+ return;
+ }
+ if (deviceState.updateBatteryState(eventTime)) {
+ notifyAllListenersForDeviceLocked(deviceState);
+ }
+ }
+ }
+
+ private void handlePollEvent() {
+ synchronized (mLock) {
+ if (!mIsPolling) {
+ return;
+ }
+ final long eventTime = SystemClock.uptimeMillis();
+ mMonitoredDeviceStates.forEach((deviceId, deviceState) -> {
+ // Re-acquire lock in the lambda to silence error-prone build warnings.
+ synchronized (mLock) {
+ if (deviceState.updateBatteryState(eventTime)) {
+ notifyAllListenersForDeviceLocked(deviceState);
+ }
+ }
+ });
+ mHandler.postDelayed(this::handlePollEvent, POLLING_PERIOD_MILLIS);
+ }
+ }
+
+ void onInteractiveChanged(boolean interactive) {
+ synchronized (mLock) {
+ mIsInteractive = interactive;
+ updatePollingLocked(false /*delayStart*/);
+ }
+ }
+
void dump(PrintWriter pw, String prefix) {
synchronized (mLock) {
- pw.println(prefix + TAG + ": " + mListenerRecords.size()
- + " battery listeners");
+ pw.println(prefix + TAG + ": "
+ + mListenerRecords.size() + " battery listeners"
+ + ", Polling = " + mIsPolling
+ + ", Interactive = " + mIsInteractive);
for (int i = 0; i < mListenerRecords.size(); i++) {
pw.println(prefix + " " + i + ": " + mListenerRecords.valueAt(i));
}
@@ -211,6 +345,29 @@ final class BatteryController {
}
}
+ @VisibleForTesting
+ @Override
+ public void onInputDeviceAdded(int deviceId) {}
+
+ @VisibleForTesting
+ @Override
+ public void onInputDeviceRemoved(int deviceId) {}
+
+ @VisibleForTesting
+ @Override
+ public void onInputDeviceChanged(int deviceId) {
+ synchronized (mLock) {
+ final MonitoredDeviceState deviceState = mMonitoredDeviceStates.get(deviceId);
+ if (deviceState == null) {
+ return;
+ }
+ final long eventTime = SystemClock.uptimeMillis();
+ if (deviceState.updateBatteryState(eventTime)) {
+ notifyAllListenersForDeviceLocked(deviceState);
+ }
+ }
+ }
+
// A record of a registered battery listener from one process.
private class ListenerRecord {
final int mPid;
@@ -232,4 +389,109 @@ final class BatteryController {
+ ", monitored devices=" + Arrays.toString(mMonitoredDevices.toArray());
}
}
+
+ // Holds the state of an InputDevice for which battery changes are currently being monitored.
+ private class MonitoredDeviceState {
+ private final int mDeviceId;
+
+ private long mLastUpdateTime = 0;
+ private boolean mHasBattery = false;
+ @BatteryState.BatteryStatus
+ private int mBatteryStatus = BatteryState.STATUS_UNKNOWN;
+ private float mBatteryCapacity = Float.NaN;
+
+ @Nullable
+ private UEventListener mUEventListener;
+
+ MonitoredDeviceState(int deviceId) {
+ mDeviceId = deviceId;
+
+ // Load the initial battery state and start monitoring.
+ final long eventTime = SystemClock.uptimeMillis();
+ updateBatteryState(eventTime);
+ }
+
+ // Returns true if the battery state changed since the last time it was updated.
+ boolean updateBatteryState(long eventTime) {
+ mLastUpdateTime = eventTime;
+
+ final boolean batteryPresenceChanged = mHasBattery != hasBattery(mDeviceId);
+ if (batteryPresenceChanged) {
+ mHasBattery = !mHasBattery;
+ if (mHasBattery) {
+ startMonitoring();
+ } else {
+ stopMonitoring();
+ }
+ }
+
+ final int oldStatus = mBatteryStatus;
+ final float oldCapacity = mBatteryCapacity;
+
+ if (mHasBattery) {
+ mBatteryStatus = mNative.getBatteryStatus(mDeviceId);
+ mBatteryCapacity = mNative.getBatteryCapacity(mDeviceId) / 100.f;
+ } else {
+ mBatteryStatus = BatteryState.STATUS_UNKNOWN;
+ mBatteryCapacity = Float.NaN;
+ }
+
+ return batteryPresenceChanged
+ || mBatteryStatus != oldStatus
+ || mBatteryCapacity != oldCapacity;
+ }
+
+ private void startMonitoring() {
+ final String batteryPath = mNative.getBatteryDevicePath(mDeviceId);
+ if (batteryPath == null) {
+ return;
+ }
+ mUEventListener = new UEventListener() {
+ @Override
+ void onUEvent(long eventTime) {
+ handleBatteryChangeNotification(mDeviceId, eventTime);
+ }
+ };
+ mUEventManager.addListener(mUEventListener, "DEVPATH=" + batteryPath);
+ }
+
+ // This must be called when the device is no longer being monitored.
+ void stopMonitoring() {
+ if (mUEventListener != null) {
+ mUEventManager.removeListener(mUEventListener);
+ mUEventListener = null;
+ }
+ }
+ }
+
+ // An interface used to change the API of UEventObserver to a more test-friendly format.
+ @VisibleForTesting
+ interface UEventManager {
+
+ @VisibleForTesting
+ abstract class UEventListener {
+ private final UEventObserver mObserver = new UEventObserver() {
+ @Override
+ public void onUEvent(UEvent event) {
+ final long eventTime = SystemClock.uptimeMillis();
+ if (DEBUG) {
+ Slog.d(TAG,
+ "UEventListener: Received UEvent: "
+ + event + " eventTime: " + eventTime);
+ }
+ UEventListener.this.onUEvent(eventTime);
+ }
+ };
+
+ abstract void onUEvent(long eventTime);
+ }
+
+ default void addListener(UEventListener listener, String match) {
+ listener.mObserver.startObserving(match);
+ }
+
+ default void removeListener(UEventListener listener) {
+ listener.mObserver.stopObserving();
+ }
+ }
}
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index fc6bc555e823..36099b0261c2 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.input;
+package com.android.server.input;
import android.annotation.NonNull;
import android.graphics.PointF;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 3def714f0c30..71feb952cb00 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -55,8 +55,6 @@ import android.hardware.input.IInputSensorEventListener;
import android.hardware.input.ITabletModeChangedListener;
import android.hardware.input.InputDeviceIdentifier;
import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
-import android.hardware.input.InputManagerInternal.LidSwitchCallback;
import android.hardware.input.InputSensorInfo;
import android.hardware.input.KeyboardLayout;
import android.hardware.input.TouchCalibration;
@@ -123,6 +121,7 @@ import com.android.internal.util.XmlUtils;
import com.android.server.DisplayThread;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
+import com.android.server.input.InputManagerInternal.LidSwitchCallback;
import com.android.server.policy.WindowManagerPolicy;
import libcore.io.IoUtils;
@@ -421,7 +420,7 @@ public class InputManagerService extends IInputManager.Stub
mContext = injector.getContext();
mHandler = new InputManagerHandler(injector.getLooper());
mNative = injector.getNativeService(this);
- mBatteryController = new BatteryController(mContext, mNative);
+ mBatteryController = new BatteryController(mContext, mNative, injector.getLooper());
mUseDevInputEventForAudioJack =
mContext.getResources().getBoolean(R.bool.config_useDevInputEventForAudioJack);
@@ -561,6 +560,8 @@ public class InputManagerService extends IInputManager.Stub
if (mWiredAccessoryCallbacks != null) {
mWiredAccessoryCallbacks.systemReady();
}
+
+ mBatteryController.systemRunning();
}
private void reloadKeyboardLayouts() {
@@ -3682,6 +3683,7 @@ public class InputManagerService extends IInputManager.Stub
@Override
public void setInteractive(boolean interactive) {
mNative.setInteractive(interactive);
+ mBatteryController.onInteractiveChanged(interactive);
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
index f89b6aedf1f5..a4830beecd04 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingModeController.java
@@ -21,7 +21,6 @@ import static android.view.InputDevice.SOURCE_STYLUS;
import android.annotation.AnyThread;
import android.annotation.Nullable;
import android.annotation.UiThread;
-import android.hardware.input.InputManagerInternal;
import android.os.IBinder;
import android.os.Looper;
import android.util.Slog;
@@ -35,6 +34,7 @@ import android.view.MotionEvent;
import android.view.SurfaceControl;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import com.android.server.wm.WindowManagerInternal;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 729de41dcbcd..457154633977 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -92,7 +92,6 @@ import android.database.ContentObserver;
import android.graphics.Matrix;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputManager;
-import android.hardware.input.InputManagerInternal;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManagerInternal;
import android.net.Uri;
@@ -188,6 +187,7 @@ import com.android.server.LocalServices;
import com.android.server.ServiceThread;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemService;
+import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal.InputMethodListListener;
import com.android.server.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
@@ -2963,9 +2963,12 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
try {
// Use PackageManager to load label
final PackageManager packageManager = mContext.getPackageManager();
- contentDescription = packageManager.getApplicationLabel(
- mIPackageManager.getApplicationInfo(packageName, 0,
- mSettings.getCurrentUserId()));
+ final ApplicationInfo applicationInfo = mIPackageManager
+ .getApplicationInfo(packageName, 0, mSettings.getCurrentUserId());
+ if (applicationInfo != null) {
+ contentDescription = packageManager
+ .getApplicationLabel(applicationInfo);
+ }
} catch (RemoteException e) {
/* ignore */
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 1d1057f8ae9f..9bd48f2e0a58 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -115,6 +115,7 @@ import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationPermissionsHelper;
import com.android.server.location.injector.LocationPowerSaveModeHelper;
import com.android.server.location.injector.LocationUsageLogger;
+import com.android.server.location.injector.PackageResetHelper;
import com.android.server.location.injector.ScreenInteractiveHelper;
import com.android.server.location.injector.SettingsHelper;
import com.android.server.location.injector.SystemAlarmHelper;
@@ -125,6 +126,7 @@ import com.android.server.location.injector.SystemDeviceStationaryHelper;
import com.android.server.location.injector.SystemEmergencyHelper;
import com.android.server.location.injector.SystemLocationPermissionsHelper;
import com.android.server.location.injector.SystemLocationPowerSaveModeHelper;
+import com.android.server.location.injector.SystemPackageResetHelper;
import com.android.server.location.injector.SystemScreenInteractiveHelper;
import com.android.server.location.injector.SystemSettingsHelper;
import com.android.server.location.injector.SystemUserInfoHelper;
@@ -1696,11 +1698,13 @@ public class LocationManagerService extends ILocationManager.Stub implements
private final SystemDeviceStationaryHelper mDeviceStationaryHelper;
private final SystemDeviceIdleHelper mDeviceIdleHelper;
private final LocationUsageLogger mLocationUsageLogger;
+ private final PackageResetHelper mPackageResetHelper;
// lazily instantiated since they may not always be used
@GuardedBy("this")
- private @Nullable SystemEmergencyHelper mEmergencyCallHelper;
+ @Nullable
+ private SystemEmergencyHelper mEmergencyCallHelper;
@GuardedBy("this")
private boolean mSystemReady;
@@ -1721,6 +1725,7 @@ public class LocationManagerService extends ILocationManager.Stub implements
mDeviceStationaryHelper = new SystemDeviceStationaryHelper();
mDeviceIdleHelper = new SystemDeviceIdleHelper(context);
mLocationUsageLogger = new LocationUsageLogger();
+ mPackageResetHelper = new SystemPackageResetHelper(context);
}
synchronized void onSystemReady() {
@@ -1811,5 +1816,10 @@ public class LocationManagerService extends ILocationManager.Stub implements
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
+
+ @Override
+ public PackageResetHelper getPackageResetHelper() {
+ return mPackageResetHelper;
+ }
}
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
index 15b209396a88..058c1c8efda5 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEventLogger.java
@@ -325,12 +325,8 @@ public class ContextHubEventLogger {
}
}
- /**
- * Creates a string representation of the logged events
- *
- * @return the dumped events
- */
- public synchronized String dump() {
+ @Override
+ public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Nanoapp Loads:");
sb.append(System.lineSeparator());
@@ -368,9 +364,4 @@ public class ContextHubEventLogger {
}
return sb.toString();
}
-
- @Override
- public String toString() {
- return dump();
- }
}
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 4ca0ea6e0e95..7ce1017ba4a8 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -1054,7 +1054,7 @@ public class ContextHubService extends IContextHubService.Stub {
pw.println("");
pw.println("=================== EVENTS ====================");
- pw.println(ContextHubEventLogger.getInstance().dump());
+ pw.println(ContextHubEventLogger.getInstance());
// dump eventLog
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 82bcca2b8470..349b94bc137b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -39,6 +39,7 @@ import com.android.server.LocalServices;
import com.android.server.location.injector.AppForegroundHelper;
import com.android.server.location.injector.Injector;
import com.android.server.location.injector.LocationPermissionsHelper;
+import com.android.server.location.injector.PackageResetHelper;
import com.android.server.location.injector.SettingsHelper;
import com.android.server.location.injector.UserInfoHelper;
import com.android.server.location.injector.UserInfoHelper.UserListener;
@@ -193,6 +194,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
protected final LocationPermissionsHelper mLocationPermissionsHelper;
protected final AppForegroundHelper mAppForegroundHelper;
protected final LocationManagerInternal mLocationManagerInternal;
+ private final PackageResetHelper mPackageResetHelper;
private final UserListener mUserChangedListener = this::onUserChanged;
private final ProviderEnabledListener mProviderEnabledChangedListener =
@@ -218,12 +220,25 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
};
private final AppForegroundHelper.AppForegroundListener mAppForegroundChangedListener =
this::onAppForegroundChanged;
+ private final PackageResetHelper.Responder mPackageResetResponder =
+ new PackageResetHelper.Responder() {
+ @Override
+ public void onPackageReset(String packageName) {
+ GnssListenerMultiplexer.this.onPackageReset(packageName);
+ }
+
+ @Override
+ public boolean isResetableForPackage(String packageName) {
+ return GnssListenerMultiplexer.this.isResetableForPackage(packageName);
+ }
+ };
protected GnssListenerMultiplexer(Injector injector) {
mUserInfoHelper = injector.getUserInfoHelper();
mSettingsHelper = injector.getSettingsHelper();
mLocationPermissionsHelper = injector.getLocationPermissionsHelper();
mAppForegroundHelper = injector.getAppForegroundHelper();
+ mPackageResetHelper = injector.getPackageResetHelper();
mLocationManagerInternal = Objects.requireNonNull(
LocalServices.getService(LocationManagerInternal.class));
}
@@ -357,6 +372,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
mLocationPackageBlacklistChangedListener);
mLocationPermissionsHelper.addListener(mLocationPermissionsListener);
mAppForegroundHelper.addListener(mAppForegroundChangedListener);
+ mPackageResetHelper.register(mPackageResetResponder);
}
@Override
@@ -374,6 +390,7 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
mLocationPackageBlacklistChangedListener);
mLocationPermissionsHelper.removeListener(mLocationPermissionsListener);
mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
+ mPackageResetHelper.unregister(mPackageResetResponder);
}
private void onUserChanged(int userId, int change) {
@@ -407,6 +424,24 @@ public abstract class GnssListenerMultiplexer<TRequest, TListener extends IInter
updateRegistrations(registration -> registration.onForegroundChanged(uid, foreground));
}
+ private void onPackageReset(String packageName) {
+ updateRegistrations(
+ registration -> {
+ if (registration.getIdentity().getPackageName().equals(
+ packageName)) {
+ registration.remove();
+ }
+
+ return false;
+ });
+ }
+
+ private boolean isResetableForPackage(String packageName) {
+ // invoked to find out if the given package has any state that can be "force quit"
+ return findRegistration(
+ registration -> registration.getIdentity().getPackageName().equals(packageName));
+ }
+
@Override
protected String getServiceState() {
if (!isSupported()) {
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 a6a3db11b729..e653f0466863 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -1616,6 +1616,10 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
updateEnabled();
restartLocationRequest();
}
+
+ // Re-register network callbacks to get an update of available networks right away.
+ mNetworkConnectivityHandler.unregisterNetworkCallbacks();
+ mNetworkConnectivityHandler.registerNetworkCallbacks();
}
@Override
@@ -1722,7 +1726,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements
String setId = null;
int subId = SubscriptionManager.getDefaultDataSubscriptionId();
- if (mNIHandler.getInEmergency() && mNetworkConnectivityHandler.getActiveSubId() >= 0) {
+ if (mGnssConfiguration.isActiveSimEmergencySuplEnabled() && mNIHandler.getInEmergency()
+ && mNetworkConnectivityHandler.getActiveSubId() >= 0) {
subId = mNetworkConnectivityHandler.getActiveSubId();
}
if (SubscriptionManager.isValidSubscriptionId(subId)) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index dc1f4ddb19b9..02bdfd5bcb56 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -301,6 +301,10 @@ class GnssNetworkConnectivityHandler {
mConnMgr.registerNetworkCallback(networkRequest, mNetworkConnectivityCallback, mHandler);
}
+ void unregisterNetworkCallbacks() {
+ mConnMgr.unregisterNetworkCallback(mNetworkConnectivityCallback);
+ }
+
/**
* @return {@code true} if there is a data network available for outgoing connections,
* {@code false} otherwise.
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index c0ce3a6853cf..b2c86721fdcc 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -63,4 +63,7 @@ public interface Injector {
/** Returns a LocationUsageLogger. */
LocationUsageLogger getLocationUsageLogger();
+
+ /** Returns a PackageResetHelper. */
+ PackageResetHelper getPackageResetHelper();
}
diff --git a/services/core/java/com/android/server/location/injector/PackageResetHelper.java b/services/core/java/com/android/server/location/injector/PackageResetHelper.java
new file mode 100644
index 000000000000..721c576b1b10
--- /dev/null
+++ b/services/core/java/com/android/server/location/injector/PackageResetHelper.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.injector;
+
+import static com.android.server.location.LocationManagerService.D;
+import static com.android.server.location.LocationManagerService.TAG;
+
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/** Helpers for tracking queries and resets of package state. */
+public abstract class PackageResetHelper {
+
+ /** Interface for responding to reset events. */
+ public interface Responder {
+
+ /**
+ * Called when a package's runtime state is being reset for whatever reason and any
+ * components carrying runtime state on behalf of the package should clear that state.
+ *
+ * @param packageName The name of the package.
+ */
+ void onPackageReset(String packageName);
+
+ /**
+ * Called when the system queries whether this package has any active state for the given
+ * package. Should return true if the component has some runtime state that is resetable of
+ * behalf of the given package, and false otherwise.
+ *
+ * @param packageName The name of the package.
+ * @return True if this component has resetable state for the given package.
+ */
+ boolean isResetableForPackage(String packageName);
+ }
+
+ private final CopyOnWriteArrayList<Responder> mResponders;
+
+ public PackageResetHelper() {
+ mResponders = new CopyOnWriteArrayList<>();
+ }
+
+ /** Begin listening for package reset events. */
+ public synchronized void register(Responder responder) {
+ boolean empty = mResponders.isEmpty();
+ mResponders.add(responder);
+ if (empty) {
+ onRegister();
+ }
+ }
+
+ /** Stop listening for package reset events. */
+ public synchronized void unregister(Responder responder) {
+ mResponders.remove(responder);
+ if (mResponders.isEmpty()) {
+ onUnregister();
+ }
+ }
+
+ @GuardedBy("this")
+ protected abstract void onRegister();
+
+ @GuardedBy("this")
+ protected abstract void onUnregister();
+
+ protected final void notifyPackageReset(String packageName) {
+ if (D) {
+ Log.d(TAG, "package " + packageName + " reset");
+ }
+
+ for (Responder responder : mResponders) {
+ responder.onPackageReset(packageName);
+ }
+ }
+
+ protected final boolean queryResetableForPackage(String packageName) {
+ for (Responder responder : mResponders) {
+ if (responder.isResetableForPackage(packageName)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/location/injector/SystemPackageResetHelper.java b/services/core/java/com/android/server/location/injector/SystemPackageResetHelper.java
new file mode 100644
index 000000000000..b0fe55d88b90
--- /dev/null
+++ b/services/core/java/com/android/server/location/injector/SystemPackageResetHelper.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.injector;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+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 com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+
+/** Listens to appropriate broadcasts for queries and resets. */
+public class SystemPackageResetHelper extends PackageResetHelper {
+
+ private final Context mContext;
+
+ @Nullable
+ private BroadcastReceiver mReceiver;
+
+ public SystemPackageResetHelper(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ protected void onRegister() {
+ Preconditions.checkState(mReceiver == null);
+ mReceiver = new Receiver();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ filter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
+ filter.addDataScheme("package");
+
+ // We don't filter for Intent.ACTION_PACKAGE_DATA_CLEARED as 1) it refers to persistent
+ // data, and 2) it should always be preceded by Intent.ACTION_PACKAGE_RESTARTED, which
+ // refers to runtime data. in this way we also avoid redundant callbacks.
+
+ mContext.registerReceiver(mReceiver, filter);
+ }
+
+ @Override
+ protected void onUnregister() {
+ Preconditions.checkState(mReceiver != null);
+ mContext.unregisterReceiver(mReceiver);
+ mReceiver = null;
+ }
+
+ private class Receiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == null) {
+ return;
+ }
+
+ Uri data = intent.getData();
+ if (data == null) {
+ return;
+ }
+
+ String packageName = data.getSchemeSpecificPart();
+ if (packageName == null) {
+ return;
+ }
+
+ switch (action) {
+ case Intent.ACTION_QUERY_PACKAGE_RESTART:
+ String[] packages = intent.getStringArrayExtra(Intent.EXTRA_PACKAGES);
+ if (packages != null) {
+ // it would be more efficient to pass through the whole array, but at the
+ // moment the array is always size 1, and this makes for a nicer callback.
+ for (String pkg : packages) {
+ if (queryResetableForPackage(pkg)) {
+ setResultCode(Activity.RESULT_OK);
+ break;
+ }
+ }
+ }
+ break;
+ case Intent.ACTION_PACKAGE_CHANGED:
+ // make sure this is an enabled/disabled change to the package as a whole, not
+ // just some of its components. This avoids unnecessary work in the callback.
+ boolean isPackageChange = false;
+ String[] components = intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ if (components != null) {
+ for (String component : components) {
+ if (packageName.equals(component)) {
+ isPackageChange = true;
+ break;
+ }
+ }
+ }
+
+ if (isPackageChange) {
+ try {
+ ApplicationInfo appInfo =
+ context.getPackageManager().getApplicationInfo(packageName,
+ PackageManager.ApplicationInfoFlags.of(0));
+ if (!appInfo.enabled) {
+ // move off main thread
+ FgThread.getExecutor().execute(
+ () -> notifyPackageReset(packageName));
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ return;
+ }
+ }
+ break;
+ case Intent.ACTION_PACKAGE_REMOVED:
+ // fall through
+ case Intent.ACTION_PACKAGE_RESTARTED:
+ // move off main thread
+ FgThread.getExecutor().execute(() -> notifyPackageReset(packageName));
+ break;
+ default:
+ break;
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index a69a079b679d..338a99567b67 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -106,6 +106,7 @@ import com.android.server.location.injector.LocationPermissionsHelper.LocationPe
import com.android.server.location.injector.LocationPowerSaveModeHelper;
import com.android.server.location.injector.LocationPowerSaveModeHelper.LocationPowerSaveModeChangedListener;
import com.android.server.location.injector.LocationUsageLogger;
+import com.android.server.location.injector.PackageResetHelper;
import com.android.server.location.injector.ScreenInteractiveHelper;
import com.android.server.location.injector.ScreenInteractiveHelper.ScreenInteractiveChangedListener;
import com.android.server.location.injector.SettingsHelper;
@@ -1373,6 +1374,7 @@ public class LocationProviderManager extends
protected final ScreenInteractiveHelper mScreenInteractiveHelper;
protected final LocationUsageLogger mLocationUsageLogger;
protected final LocationFudger mLocationFudger;
+ private final PackageResetHelper mPackageResetHelper;
private final UserListener mUserChangedListener = this::onUserChanged;
private final LocationSettings.LocationUserSettingsListener mLocationUserSettingsListener =
@@ -1407,6 +1409,18 @@ public class LocationProviderManager extends
this::onLocationPowerSaveModeChanged;
private final ScreenInteractiveChangedListener mScreenInteractiveChangedListener =
this::onScreenInteractiveChanged;
+ private final PackageResetHelper.Responder mPackageResetResponder =
+ new PackageResetHelper.Responder() {
+ @Override
+ public void onPackageReset(String packageName) {
+ LocationProviderManager.this.onPackageReset(packageName);
+ }
+
+ @Override
+ public boolean isResetableForPackage(String packageName) {
+ return LocationProviderManager.this.isResetableForPackage(packageName);
+ }
+ };
// acquiring mMultiplexerLock makes operations on mProvider atomic, but is otherwise unnecessary
protected final MockableLocationProvider mProvider;
@@ -1442,6 +1456,7 @@ public class LocationProviderManager extends
mScreenInteractiveHelper = injector.getScreenInteractiveHelper();
mLocationUsageLogger = injector.getLocationUsageLogger();
mLocationFudger = new LocationFudger(mSettingsHelper.getCoarseLocationAccuracyM());
+ mPackageResetHelper = injector.getPackageResetHelper();
mProvider = new MockableLocationProvider(mMultiplexerLock);
@@ -1970,6 +1985,7 @@ public class LocationProviderManager extends
mAppForegroundHelper.addListener(mAppForegroundChangedListener);
mLocationPowerSaveModeHelper.addListener(mLocationPowerSaveModeChangedListener);
mScreenInteractiveHelper.addListener(mScreenInteractiveChangedListener);
+ mPackageResetHelper.register(mPackageResetResponder);
}
@GuardedBy("mMultiplexerLock")
@@ -1988,6 +2004,7 @@ public class LocationProviderManager extends
mAppForegroundHelper.removeListener(mAppForegroundChangedListener);
mLocationPowerSaveModeHelper.removeListener(mLocationPowerSaveModeChangedListener);
mScreenInteractiveHelper.removeListener(mScreenInteractiveChangedListener);
+ mPackageResetHelper.unregister(mPackageResetResponder);
}
@GuardedBy("mMultiplexerLock")
@@ -2391,6 +2408,24 @@ public class LocationProviderManager extends
updateRegistrations(registration -> registration.onLocationPermissionsChanged(uid));
}
+ private void onPackageReset(String packageName) {
+ updateRegistrations(
+ registration -> {
+ if (registration.getIdentity().getPackageName().equals(
+ packageName)) {
+ registration.remove();
+ }
+
+ return false;
+ });
+ }
+
+ private boolean isResetableForPackage(String packageName) {
+ // invoked to find out if the given package has any state that can be "force quit"
+ return findRegistration(
+ registration -> registration.getIdentity().getPackageName().equals(packageName));
+ }
+
@GuardedBy("mMultiplexerLock")
@Override
public void onStateChanged(
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index cbbb3362a387..fdc5bab7c6eb 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -40,6 +40,8 @@ import android.util.ArrayMap;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.ILogAccessDialogCallback;
+import com.android.internal.app.LogAccessDialogActivity;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -101,7 +103,7 @@ public final class LogcatManagerService extends SystemService {
private final Injector mInjector;
private final Supplier<Long> mClock;
private final BinderService mBinderService;
- private final LogcatManagerServiceInternal mLocalService;
+ private final LogAccessDialogCallback mDialogCallback;
private final Handler mHandler;
private ActivityManagerInternal mActivityManagerInternal;
private ILogd mLogdService;
@@ -206,7 +208,8 @@ public final class LogcatManagerService extends SystemService {
}
}
- final class LogcatManagerServiceInternal {
+ final class LogAccessDialogCallback extends ILogAccessDialogCallback.Stub {
+ @Override
public void approveAccessForClient(int uid, @NonNull String packageName) {
final LogAccessClient client = new LogAccessClient(uid, packageName);
if (DEBUG) {
@@ -216,6 +219,7 @@ public final class LogcatManagerService extends SystemService {
mHandler.sendMessageAtTime(msg, mClock.get());
}
+ @Override
public void declineAccessForClient(int uid, @NonNull String packageName) {
final LogAccessClient client = new LogAccessClient(uid, packageName);
if (DEBUG) {
@@ -302,7 +306,7 @@ public final class LogcatManagerService extends SystemService {
mInjector = injector;
mClock = injector.createClock();
mBinderService = new BinderService();
- mLocalService = new LogcatManagerServiceInternal();
+ mDialogCallback = new LogAccessDialogCallback();
mHandler = new LogAccessRequestHandler(injector.getLooper(), this);
}
@@ -311,15 +315,14 @@ public final class LogcatManagerService extends SystemService {
try {
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
publishBinderService("logcat", mBinderService);
- publishLocalService(LogcatManagerServiceInternal.class, mLocalService);
} catch (Throwable t) {
Slog.e(TAG, "Could not start the LogcatManagerService.", t);
}
}
@VisibleForTesting
- LogcatManagerServiceInternal getLocalService() {
- return mLocalService;
+ LogAccessDialogCallback getDialogCallback() {
+ return mDialogCallback;
}
@VisibleForTesting
@@ -438,6 +441,7 @@ public final class LogcatManagerService extends SystemService {
mHandler.sendMessageAtTime(mHandler.obtainMessage(MSG_PENDING_TIMEOUT, client),
mClock.get() + PENDING_CONFIRMATION_TIMEOUT_MILLIS);
final Intent mIntent = createIntent(client);
+ mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivityAsUser(mIntent, UserHandle.SYSTEM);
}
@@ -538,6 +542,7 @@ public final class LogcatManagerService extends SystemService {
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, client.mPackageName);
intent.putExtra(Intent.EXTRA_UID, client.mUid);
+ intent.putExtra(LogAccessDialogActivity.EXTRA_CALLBACK, mDialogCallback.asBinder());
return intent;
}
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index 9a190316f4eb..b1e8b407d6e8 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -246,10 +246,13 @@ final class MediaButtonReceiverHolder {
@Override
public String toString() {
- if (mPendingIntent != null) {
- return "MBR {pi=" + mPendingIntent + ", type=" + mComponentType + "}";
- }
- return "Restored MBR {component=" + mComponentName + ", type=" + mComponentType + "}";
+ StringBuilder sb = new StringBuilder();
+ sb.append("MBR {pi=").append(mPendingIntent);
+ sb.append(", componentName=").append(mComponentName);
+ sb.append(", type=").append(mComponentType);
+ sb.append(", pkg=").append(mPackageName);
+ sb.append("}");
+ return sb.toString();
}
/**
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
index b688e0922d49..c8697b48b515 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderWatcher.java
@@ -41,6 +41,8 @@ import java.util.Collections;
final class MediaRoute2ProviderWatcher {
private static final String TAG = "MR2ProviderWatcher";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final PackageManager.ResolveInfoFlags RESOLVE_INFO_FLAGS_NONE =
+ PackageManager.ResolveInfoFlags.of(0);
private final Context mContext;
private final Callback mCallback;
@@ -110,8 +112,9 @@ final class MediaRoute2ProviderWatcher {
// Reorder the list so that providers left at the end will be the ones to remove.
int targetIndex = 0;
Intent intent = new Intent(MediaRoute2ProviderService.SERVICE_INTERFACE);
- for (ResolveInfo resolveInfo : mPackageManager.queryIntentServicesAsUser(
- intent, 0, mUserId)) {
+ for (ResolveInfo resolveInfo :
+ mPackageManager.queryIntentServicesAsUser(
+ intent, RESOLVE_INFO_FLAGS_NONE, mUserId)) {
ServiceInfo serviceInfo = resolveInfo.serviceInfo;
if (serviceInfo != null) {
int sourceIndex = findProvider(serviceInfo.packageName, serviceInfo.name);
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index bfa8af957208..cc8761d70530 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -601,10 +601,29 @@ class MediaRouter2ServiceImpl {
}
}
- //TODO(b/136703681): Review this is handling multi-user properly.
- void switchUser() {
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "MediaRouter2ServiceImpl");
+
+ String indent = prefix + " ";
+
+ synchronized (mLock) {
+ pw.println(indent + "mNextRouterOrManagerId=" + mNextRouterOrManagerId.get());
+ pw.println(indent + "mCurrentUserId=" + mCurrentUserId);
+
+ pw.println(indent + "UserRecords:");
+ if (mUserRecords.size() > 0) {
+ for (int i = 0; i < mUserRecords.size(); i++) {
+ mUserRecords.valueAt(i).dump(pw, indent + " ");
+ }
+ } else {
+ pw.println(indent + "<no user records>");
+ }
+ }
+ }
+
+ // TODO(b/136703681): Review this is handling multi-user properly.
+ void switchUser(int userId) {
synchronized (mLock) {
- int userId = ActivityManager.getCurrentUser();
if (mCurrentUserId != userId) {
final int oldUserId = mCurrentUserId;
mCurrentUserId = userId; // do this first
@@ -1197,6 +1216,36 @@ class MediaRouter2ServiceImpl {
}
return null;
}
+
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "UserRecord");
+
+ String indent = prefix + " ";
+
+ pw.println(indent + "mUserId=" + mUserId);
+
+ pw.println(indent + "Router Records:");
+ if (!mRouterRecords.isEmpty()) {
+ for (RouterRecord routerRecord : mRouterRecords) {
+ routerRecord.dump(pw, indent + " ");
+ }
+ } else {
+ pw.println(indent + "<no router records>");
+ }
+
+ pw.println(indent + "Manager Records:");
+ if (!mManagerRecords.isEmpty()) {
+ for (ManagerRecord managerRecord : mManagerRecords) {
+ managerRecord.dump(pw, indent + " ");
+ }
+ } else {
+ pw.println(indent + "<no manager records>");
+ }
+
+ if (!mHandler.runWithScissors(() -> mHandler.dump(pw, indent), 1000)) {
+ pw.println(indent + "<could not dump handler state>");
+ }
+ }
}
final class RouterRecord implements IBinder.DeathRecipient {
@@ -1236,6 +1285,22 @@ class MediaRouter2ServiceImpl {
public void binderDied() {
routerDied(this);
}
+
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "RouterRecord");
+
+ String indent = prefix + " ";
+
+ pw.println(indent + "mPackageName=" + mPackageName);
+ pw.println(indent + "mSelectRouteSequenceNumbers=" + mSelectRouteSequenceNumbers);
+ pw.println(indent + "mUid=" + mUid);
+ pw.println(indent + "mPid=" + mPid);
+ pw.println(indent + "mHasConfigureWifiDisplayPermission="
+ + mHasConfigureWifiDisplayPermission);
+ pw.println(indent + "mHasModifyAudioRoutingPermission="
+ + mHasModifyAudioRoutingPermission);
+ pw.println(indent + "mRouterId=" + mRouterId);
+ }
}
final class ManagerRecord implements IBinder.DeathRecipient {
@@ -1267,8 +1332,20 @@ class MediaRouter2ServiceImpl {
managerDied(this);
}
- public void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + this);
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "ManagerRecord");
+
+ String indent = prefix + " ";
+
+ pw.println(indent + "mPackageName=" + mPackageName);
+ pw.println(indent + "mManagerId=" + mManagerId);
+ pw.println(indent + "mUid=" + mUid);
+ pw.println(indent + "mPid=" + mPid);
+ pw.println(indent + "mIsScanning=" + mIsScanning);
+
+ if (mLastSessionCreationRequest != null) {
+ mLastSessionCreationRequest.dump(pw, indent);
+ }
}
public void startScan() {
@@ -1455,6 +1532,15 @@ class MediaRouter2ServiceImpl {
}
}
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "UserHandler");
+
+ String indent = prefix + " ";
+ pw.println(indent + "mRunning=" + mRunning);
+
+ mWatcher.dump(pw, prefix);
+ }
+
private void onProviderStateChangedOnHandler(@NonNull MediaRoute2Provider provider) {
MediaRoute2ProviderInfo currentInfo = provider.getProviderInfo();
@@ -2340,5 +2426,14 @@ class MediaRouter2ServiceImpl {
mOldSession = oldSession;
mRoute = route;
}
+
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.println(prefix + "SessionCreationRequest");
+
+ String indent = prefix + " ";
+
+ pw.println(indent + "mUniqueRequestId=" + mUniqueRequestId);
+ pw.println(indent + "mManagerRequestId=" + mManagerRequestId);
+ }
}
}
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 4806b522eca6..d7d0b42aa0a7 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -17,7 +17,9 @@
package com.android.server.media;
import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
import android.app.ActivityManager;
+import android.app.UserSwitchObserver;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
@@ -146,18 +148,27 @@ public final class MediaRouterService extends IMediaRouterService.Stub
context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null);
}
- public void systemRunning() {
- IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_USER_SWITCHED)) {
- switchUser();
- }
- }
- }, filter);
-
- switchUser();
+ /**
+ * Initializes the MediaRouter service.
+ *
+ * @throws RemoteException If an error occurs while registering the {@link UserSwitchObserver}.
+ */
+ @RequiresPermission(
+ anyOf = {
+ "android.permission.INTERACT_ACROSS_USERS",
+ "android.permission.INTERACT_ACROSS_USERS_FULL"
+ })
+ public void systemRunning() throws RemoteException {
+ ActivityManager.getService()
+ .registerUserSwitchObserver(
+ new UserSwitchObserver() {
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ switchUser(newUserId);
+ }
+ },
+ TAG);
+ switchUser(ActivityManager.getCurrentUser());
}
@Override
@@ -387,6 +398,9 @@ public final class MediaRouterService extends IMediaRouterService.Stub
userRecord.dump(pw, "");
}
}
+
+ pw.println();
+ mService2.dump(pw, "");
}
// Binder call
@@ -631,9 +645,8 @@ public final class MediaRouterService extends IMediaRouterService.Stub
}
}
- void switchUser() {
+ void switchUser(int userId) {
synchronized (mLock) {
- int userId = ActivityManager.getCurrentUser();
if (mCurrentUserId != userId) {
final int oldUserId = mCurrentUserId;
mCurrentUserId = userId; // do this first
@@ -650,7 +663,7 @@ public final class MediaRouterService extends IMediaRouterService.Stub
}
}
}
- mService2.switchUser();
+ mService2.switchUser(userId);
}
void clientDied(ClientRecord clientRecord) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 0fac808fb13b..4d55d4e545ee 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -1786,8 +1786,8 @@ abstract public class ManagedServices {
* from receiving events from the profile.
*/
public boolean isPermittedForProfile(int userId) {
- if (!mUserProfiles.canProfileUseBoundServices(userId)) {
- return false;
+ if (!mUserProfiles.isProfileUser(userId)) {
+ return true;
}
DevicePolicyManager dpm =
(DevicePolicyManager) mContext.getSystemService(DEVICE_POLICY_SERVICE);
@@ -1862,16 +1862,16 @@ abstract public class ManagedServices {
}
}
- public boolean canProfileUseBoundServices(int userId) {
+ public boolean isProfileUser(int userId) {
synchronized (mCurrentProfiles) {
UserInfo user = mCurrentProfiles.get(userId);
if (user == null) {
return false;
}
if (user.isManagedProfile() || user.isCloneProfile()) {
- return false;
+ return true;
}
- return true;
+ return false;
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 0b4f736445ab..4b18add39999 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1829,7 +1829,7 @@ public class NotificationManagerService extends SystemService {
} else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
- if (mUserProfiles.canProfileUseBoundServices(userId)) {
+ if (!mUserProfiles.isProfileUser(userId)) {
// reload per-user settings
mSettingsObserver.update(null);
// Refresh managed services
@@ -1843,7 +1843,7 @@ public class NotificationManagerService extends SystemService {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
if (userId != USER_NULL) {
mUserProfiles.updateCache(context);
- if (mUserProfiles.canProfileUseBoundServices(userId)) {
+ if (!mUserProfiles.isProfileUser(userId)) {
allowDefaultApprovedServices(userId);
}
}
@@ -1861,7 +1861,7 @@ public class NotificationManagerService extends SystemService {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
mUserProfiles.updateCache(context);
mAssistants.onUserUnlocked(userId);
- if (mUserProfiles.canProfileUseBoundServices(userId)) {
+ if (!mUserProfiles.isProfileUser(userId)) {
mConditionProviders.onUserUnlocked(userId);
mListeners.onUserUnlocked(userId);
mZenModeHelper.onUserUnlocked(userId);
@@ -1978,34 +1978,39 @@ public class NotificationManagerService extends SystemService {
return (haystack & needle) != 0;
}
- public boolean isInLockDownMode() {
- return mIsInLockDownMode;
+ // Return whether the user is in lockdown mode.
+ // If the flag is not set, we assume the user is not in lockdown.
+ public boolean isInLockDownMode(int userId) {
+ return mUserInLockDownMode.get(userId, false);
}
@Override
public synchronized void onStrongAuthRequiredChanged(int userId) {
boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId),
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- mUserInLockDownMode.put(userId, userInLockDownModeNext);
- boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1;
- if (mIsInLockDownMode == isInLockDownModeNext) {
+ // Nothing happens if the lockdown mode of userId keeps the same.
+ if (userInLockDownModeNext == isInLockDownMode(userId)) {
return;
}
- if (isInLockDownModeNext) {
- cancelNotificationsWhenEnterLockDownMode();
+ // When the lockdown mode is changed, we perform the following steps.
+ // If the userInLockDownModeNext is true, all the function calls to
+ // notifyPostedLocked and notifyRemovedLocked will not be executed.
+ // The cancelNotificationsWhenEnterLockDownMode calls notifyRemovedLocked
+ // and postNotificationsWhenExitLockDownMode calls notifyPostedLocked.
+ // So we shall call cancelNotificationsWhenEnterLockDownMode before
+ // we set mUserInLockDownMode as true.
+ // On the other hand, if the userInLockDownModeNext is false, we shall call
+ // postNotificationsWhenExitLockDownMode after we put false into mUserInLockDownMode
+ if (userInLockDownModeNext) {
+ cancelNotificationsWhenEnterLockDownMode(userId);
}
- // When the mIsInLockDownMode is true, both notifyPostedLocked and
- // notifyRemovedLocked will be dismissed. So we shall call
- // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode
- // as true and call postNotificationsWhenExitLockDownMode after we set
- // mIsInLockDownMode as false.
- mIsInLockDownMode = isInLockDownModeNext;
+ mUserInLockDownMode.put(userId, userInLockDownModeNext);
- if (!isInLockDownModeNext) {
- postNotificationsWhenExitLockDownMode();
+ if (!userInLockDownModeNext) {
+ postNotificationsWhenExitLockDownMode(userId);
}
}
}
@@ -9712,11 +9717,14 @@ public class NotificationManagerService extends SystemService {
}
}
- private void cancelNotificationsWhenEnterLockDownMode() {
+ private void cancelNotificationsWhenEnterLockDownMode(int userId) {
synchronized (mNotificationLock) {
int numNotifications = mNotificationList.size();
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
+ if (rec.getUser().getIdentifier() != userId) {
+ continue;
+ }
mListeners.notifyRemovedLocked(rec, REASON_LOCKDOWN,
rec.getStats());
}
@@ -9724,14 +9732,23 @@ public class NotificationManagerService extends SystemService {
}
}
- private void postNotificationsWhenExitLockDownMode() {
+ private void postNotificationsWhenExitLockDownMode(int userId) {
synchronized (mNotificationLock) {
int numNotifications = mNotificationList.size();
+ // Set the delay to spread out the burst of notifications.
+ long delay = 0;
for (int i = 0; i < numNotifications; i++) {
NotificationRecord rec = mNotificationList.get(i);
- mListeners.notifyPostedLocked(rec, rec);
+ if (rec.getUser().getIdentifier() != userId) {
+ continue;
+ }
+ mHandler.postDelayed(() -> {
+ synchronized (mNotificationLock) {
+ mListeners.notifyPostedLocked(rec, rec);
+ }
+ }, delay);
+ delay += 20;
}
-
}
}
@@ -9910,6 +9927,9 @@ public class NotificationManagerService extends SystemService {
for (int i = 0; i < N; i++) {
NotificationRecord record = mNotificationList.get(i);
+ if (isInLockDownMode(record.getUser().getIdentifier())) {
+ continue;
+ }
if (!isVisibleToListener(record.getSbn(), record.getNotificationType(), info)) {
continue;
}
@@ -9951,8 +9971,8 @@ public class NotificationManagerService extends SystemService {
rankings.toArray(new NotificationListenerService.Ranking[0]));
}
- boolean isInLockDownMode() {
- return mStrongAuthTracker.isInLockDownMode();
+ boolean isInLockDownMode(int userId) {
+ return mStrongAuthTracker.isInLockDownMode(userId);
}
boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -11014,7 +11034,7 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
boolean notifyAllListeners) {
- if (isInLockDownMode()) {
+ if (isInLockDownMode(r.getUser().getIdentifier())) {
return;
}
@@ -11120,7 +11140,7 @@ public class NotificationManagerService extends SystemService {
@GuardedBy("mNotificationLock")
public void notifyRemovedLocked(NotificationRecord r, int reason,
NotificationStats notificationStats) {
- if (isInLockDownMode()) {
+ if (isInLockDownMode(r.getUser().getIdentifier())) {
return;
}
@@ -11169,10 +11189,6 @@ public class NotificationManagerService extends SystemService {
*/
@GuardedBy("mNotificationLock")
public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
- if (isInLockDownMode()) {
- return;
- }
-
boolean isHiddenRankingUpdate = changedHiddenNotifications != null
&& changedHiddenNotifications.size() > 0;
// TODO (b/73052211): if the ranking update changed the notification type,
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 8e944b7a965d..bea616869014 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -267,8 +267,8 @@ class IdmapDaemon {
try {
SystemService.start(IDMAP_DAEMON);
} catch (RuntimeException e) {
+ Slog.wtf(TAG, "Failed to enable idmap2 daemon", e);
if (e.getMessage().contains("failed to set system property")) {
- Slog.w(TAG, "Failed to enable idmap2 daemon", e);
return null;
}
}
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index ba7143876917..baa471c297d3 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -23,7 +23,9 @@ import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
import static android.content.Intent.ACTION_USER_ADDED;
import static android.content.Intent.ACTION_USER_REMOVED;
+import static android.content.Intent.EXTRA_PACKAGE_NAME;
import static android.content.Intent.EXTRA_REASON;
+import static android.content.Intent.EXTRA_USER_ID;
import static android.content.om.OverlayManagerTransaction.Request.TYPE_REGISTER_FABRICATED;
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
@@ -39,6 +41,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
import android.app.IActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -56,6 +59,7 @@ import android.content.pm.overlay.OverlayPaths;
import android.content.res.ApkAssets;
import android.net.Uri;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Environment;
import android.os.FabricatedOverlayInternal;
import android.os.HandlerThread;
@@ -1009,7 +1013,9 @@ public final class OverlayManagerService extends SystemService {
}
opti++;
- if ("-h".equals(opt)) {
+ if ("-a".equals(opt)) {
+ // dumpsys will pass in -a; silently ignore it
+ } else if ("-h".equals(opt)) {
pw.println("dump [-h] [--verbose] [--user USER_ID] [[FIELD] PACKAGE]");
pw.println(" Print debugging information about the overlay manager.");
pw.println(" With optional parameter PACKAGE, limit output to the specified");
@@ -1098,6 +1104,7 @@ public final class OverlayManagerService extends SystemService {
private void enforceActor(@NonNull OverlayIdentifier overlay, @NonNull String methodName,
int realUserId) throws SecurityException {
OverlayInfo overlayInfo = mImpl.getOverlayInfo(overlay, realUserId);
+
if (overlayInfo == null) {
throw new IllegalArgumentException("Unable to retrieve overlay information for "
+ overlay);
@@ -1416,20 +1423,41 @@ public final class OverlayManagerService extends SystemService {
private static void broadcastActionOverlayChanged(@NonNull final Set<String> targetPackages,
final int userId) {
+ final ActivityManagerInternal amInternal =
+ LocalServices.getService(ActivityManagerInternal.class);
CollectionUtils.forEach(targetPackages, target -> {
final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
Uri.fromParts("package", target, null));
intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- try {
- ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null,
- null, null, android.app.AppOpsManager.OP_NONE, null, false, false, userId);
- } catch (RemoteException e) {
- Slog.e(TAG, "broadcastActionOverlayChanged remote exception", e);
- }
+ intent.putExtra(EXTRA_PACKAGE_NAME, target);
+ intent.putExtra(EXTRA_USER_ID, userId);
+ amInternal.broadcastIntent(intent, null /* resultTo */, null /* requiredPermissions */,
+ false /* serialized */, userId, null /* appIdAllowList */,
+ OverlayManagerService::filterReceiverAccess, null /* bOptions */);
});
}
/**
+ * A callback from the broadcast queue to determine whether the intent
+ * {@link Intent#ACTION_OVERLAY_CHANGED} is visible to the receiver.
+ *
+ * @param callingUid The receiver's uid.
+ * @param extras The extras of intent that contains {@link Intent#EXTRA_PACKAGE_NAME} and
+ * {@link Intent#EXTRA_USER_ID} to check.
+ * @return {@code null} if the intent is not visible to the receiver.
+ */
+ @Nullable
+ private static Bundle filterReceiverAccess(int callingUid, @NonNull Bundle extras) {
+ final String packageName = extras.getString(EXTRA_PACKAGE_NAME);
+ final int userId = extras.getInt(EXTRA_USER_ID);
+ if (LocalServices.getService(PackageManagerInternal.class).filterAppAccess(
+ packageName, callingUid, userId, false /* filterUninstalled */)) {
+ return null;
+ }
+ return extras;
+ }
+
+ /**
* Tell the activity manager to tell a set of packages to reload their
* resources.
*/
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index ae305cacd2c4..8e672c3b32c5 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -519,6 +519,7 @@ final class OverlayManagerServiceImpl {
throws OperationFailedException {
final OverlayIdentifier overlayIdentifier = new OverlayIdentifier(
info.packageName, info.overlayName);
+
final Set<PackageAndUser> updatedTargets = new ArraySet<>();
OverlayInfo oi = mSettings.getNullableOverlayInfo(overlayIdentifier, userId);
if (oi != null) {
diff --git a/services/core/java/com/android/server/pm/ApexPackageInfo.java b/services/core/java/com/android/server/pm/ApexPackageInfo.java
index 4dd9c49321b2..672ae2eb0fb2 100644
--- a/services/core/java/com/android/server/pm/ApexPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ApexPackageInfo.java
@@ -329,17 +329,15 @@ class ApexPackageInfo {
ApexInfo ai = parsingApexInfo.get(parseResult.scanFile);
if (throwable == null) {
- // Calling hideAsFinal to assign derived fields for the app info flags.
- parseResult.parsedPackage.hideAsFinal();
-
// TODO: When ENABLE_FEATURE_SCAN_APEX is finalized, remove this and the entire
// calling path code
ScanPackageUtils.applyPolicy(parseResult.parsedPackage,
PackageManagerService.SCAN_AS_SYSTEM,
mPackageManager == null ? null : mPackageManager.getPlatformPackage(),
false);
- results.add(new ApexManager.ScanResult(
- ai, parseResult.parsedPackage, parseResult.parsedPackage.getPackageName()));
+ // Calling hideAsFinal to assign derived fields for the app info flags.
+ AndroidPackage finalPkg = parseResult.parsedPackage.hideAsFinal();
+ results.add(new ApexManager.ScanResult(ai, finalPkg, finalPkg.getPackageName()));
} else if (throwable instanceof PackageManagerException) {
throw new IllegalStateException("Unable to parse: " + ai.modulePath, throwable);
} else {
diff --git a/services/core/java/com/android/server/pm/AppsFilterBase.java b/services/core/java/com/android/server/pm/AppsFilterBase.java
index 14140b5fe264..3b676c65db75 100644
--- a/services/core/java/com/android/server/pm/AppsFilterBase.java
+++ b/services/core/java/com/android/server/pm/AppsFilterBase.java
@@ -28,7 +28,6 @@ import android.content.pm.SigningDetails;
import android.os.Binder;
import android.os.Handler;
import android.os.Process;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -199,6 +198,7 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
protected SnapshotCache<WatchedSparseBooleanMatrix> mShouldFilterCacheSnapshot;
protected volatile boolean mCacheReady = false;
+ protected volatile boolean mCacheEnabled = true;
protected static final boolean CACHE_VALID = true;
protected static final boolean CACHE_INVALID = false;
@@ -216,12 +216,12 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
return mQueriesViaComponent.contains(callingAppId, targetAppId);
}
- protected boolean isImplicitlyQueryable(int callingAppId, int targetAppId) {
- return mImplicitlyQueryable.contains(callingAppId, targetAppId);
+ protected boolean isImplicitlyQueryable(int callingUid, int targetUid) {
+ return mImplicitlyQueryable.contains(callingUid, targetUid);
}
- protected boolean isRetainedImplicitlyQueryable(int callingAppId, int targetAppId) {
- return mRetainedImplicitlyQueryable.contains(callingAppId, targetAppId);
+ protected boolean isRetainedImplicitlyQueryable(int callingUid, int targetUid) {
+ return mRetainedImplicitlyQueryable.contains(callingUid, targetUid);
}
protected boolean isQueryableViaUsesLibrary(int callingAppId, int targetAppId) {
@@ -337,13 +337,14 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
|| callingAppId == targetPkgSetting.getAppId()) {
return false;
} else if (Process.isSdkSandboxUid(callingAppId)) {
+ final int targetAppId = targetPkgSetting.getAppId();
+ final int targetUid = UserHandle.getUid(userId, targetAppId);
// we only allow sdk sandbox processes access to forcequeryable packages
return !isForceQueryable(targetPkgSetting.getAppId())
- && !isImplicitlyQueryable(callingAppId, targetPkgSetting.getAppId());
+ && !isImplicitlyQueryable(callingUid, targetUid);
}
// use cache
- if (mCacheReady && SystemProperties.getBoolean("debug.pm.use_app_filter_cache",
- true)) {
+ if (mCacheReady && mCacheEnabled) {
if (!shouldFilterApplicationUsingCache(callingUid,
targetPkgSetting.getAppId(),
userId)) {
diff --git a/services/core/java/com/android/server/pm/AppsFilterImpl.java b/services/core/java/com/android/server/pm/AppsFilterImpl.java
index 79d72a3ad7f0..4c21195e2890 100644
--- a/services/core/java/com/android/server/pm/AppsFilterImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterImpl.java
@@ -36,6 +36,7 @@ import android.content.pm.PackageManagerInternal;
import android.content.pm.SigningDetails;
import android.content.pm.UserInfo;
import android.os.Handler;
+import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.provider.DeviceConfig;
@@ -223,6 +224,12 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
return new AppsFilterSnapshotImpl(AppsFilterImpl.this);
}
};
+ readCacheEnabledSysProp();
+ SystemProperties.addChangeCallback(this::readCacheEnabledSysProp);
+ }
+
+ private void readCacheEnabledSysProp() {
+ mCacheEnabled = SystemProperties.getBoolean("debug.pm.use_app_filter_cache", true);
}
/**
diff --git a/services/core/java/com/android/server/pm/AppsFilterLocked.java b/services/core/java/com/android/server/pm/AppsFilterLocked.java
index 870f9da26210..29bb14e371c0 100644
--- a/services/core/java/com/android/server/pm/AppsFilterLocked.java
+++ b/services/core/java/com/android/server/pm/AppsFilterLocked.java
@@ -66,16 +66,16 @@ abstract class AppsFilterLocked extends AppsFilterBase {
}
@Override
- protected boolean isImplicitlyQueryable(int callingAppId, int targetAppId) {
+ protected boolean isImplicitlyQueryable(int callingUid, int targetUid) {
synchronized (mImplicitlyQueryableLock) {
- return super.isImplicitlyQueryable(callingAppId, targetAppId);
+ return super.isImplicitlyQueryable(callingUid, targetUid);
}
}
@Override
- protected boolean isRetainedImplicitlyQueryable(int callingAppId, int targetAppId) {
+ protected boolean isRetainedImplicitlyQueryable(int callingUid, int targetUid) {
synchronized (mImplicitlyQueryableLock) {
- return super.isRetainedImplicitlyQueryable(callingAppId, targetAppId);
+ return super.isRetainedImplicitlyQueryable(callingUid, targetUid);
}
}
diff --git a/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java b/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java
index 019c853471fe..4e268a277102 100644
--- a/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java
+++ b/services/core/java/com/android/server/pm/AppsFilterSnapshotImpl.java
@@ -74,6 +74,7 @@ public final class AppsFilterSnapshotImpl extends AppsFilterBase {
// cache is not ready, use an empty cache for the snapshot
mShouldFilterCache = new WatchedSparseBooleanMatrix();
}
+ mCacheEnabled = orig.mCacheEnabled;
mShouldFilterCacheSnapshot = new SnapshotCache.Sealed<>();
mBackgroundHandler = null;
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 3211ca14753d..50bb05161ffd 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -2161,7 +2161,7 @@ public class ComputerEngine implements Computer {
}
public final String resolveExternalPackageName(AndroidPackage pkg) {
- if (pkg.getStaticSharedLibName() != null) {
+ if (pkg.getStaticSharedLibraryName() != null) {
return pkg.getManifestPackageName();
}
return pkg.getPackageName();
@@ -2378,7 +2378,7 @@ public class ComputerEngine implements Computer {
}
final SharedLibraryInfo libraryInfo = getSharedLibraryInfo(
- ps.getPkg().getStaticSharedLibName(), ps.getPkg().getStaticSharedLibVersion());
+ ps.getPkg().getStaticSharedLibraryName(), ps.getPkg().getStaticSharedLibVersion());
if (libraryInfo == null) {
return false;
}
@@ -2434,7 +2434,7 @@ public class ComputerEngine implements Computer {
}
final SharedLibraryInfo libraryInfo = getSharedLibraryInfo(
- ps.getPkg().getSdkLibName(), ps.getPkg().getSdkLibVersionMajor());
+ ps.getPkg().getSdkLibraryName(), ps.getPkg().getSdkLibVersionMajor());
if (libraryInfo == null) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 9f6aa633c3b2..7ff91f827db1 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -184,11 +184,11 @@ final class DeletePackageHelper {
if (pkg != null) {
SharedLibraryInfo libraryInfo = null;
- if (pkg.getStaticSharedLibName() != null) {
- libraryInfo = computer.getSharedLibraryInfo(pkg.getStaticSharedLibName(),
+ if (pkg.getStaticSharedLibraryName() != null) {
+ libraryInfo = computer.getSharedLibraryInfo(pkg.getStaticSharedLibraryName(),
pkg.getStaticSharedLibVersion());
- } else if (pkg.getSdkLibName() != null) {
- libraryInfo = computer.getSharedLibraryInfo(pkg.getSdkLibName(),
+ } else if (pkg.getSdkLibraryName() != null) {
+ libraryInfo = computer.getSharedLibraryInfo(pkg.getSdkLibraryName(),
pkg.getSdkLibVersionMajor());
}
@@ -271,7 +271,8 @@ final class DeletePackageHelper {
// other processes clean up before deleting resources.
synchronized (mPm.mInstallLock) {
if (info.mArgs != null) {
- mRemovePackageHelper.cleanUpResources(info.mArgs);
+ mRemovePackageHelper.cleanUpResources(info.mArgs.mCodeFile,
+ info.mArgs.mInstructionSets);
}
boolean reEnableStub = false;
@@ -543,7 +544,7 @@ final class DeletePackageHelper {
}
outInfo.mRemovedPackage = ps.getPackageName();
outInfo.mInstallerPackageName = ps.getInstallSource().installerPackageName;
- outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibName() != null;
+ outInfo.mIsStaticSharedLib = pkg != null && pkg.getStaticSharedLibraryName() != null;
outInfo.mRemovedAppId = ps.getAppId();
outInfo.mRemovedUsers = userIds;
outInfo.mBroadcastUsers = userIds;
@@ -894,8 +895,8 @@ final class DeletePackageHelper {
final String packageName = ps.getPkg().getPackageName();
// Skip over if system app, static shared library or and SDK library.
if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0
- || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibName())
- || !TextUtils.isEmpty(ps.getPkg().getSdkLibName())) {
+ || !TextUtils.isEmpty(ps.getPkg().getStaticSharedLibraryName())
+ || !TextUtils.isEmpty(ps.getPkg().getSdkLibraryName())) {
continue;
}
if (DEBUG_CLEAN_APKS) {
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index 2b8a196ba528..cd074c0d4a5d 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -385,6 +385,11 @@ final class DexOptHelper {
} else if (snapshot.isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
return false;
}
+ var pkg = snapshot.getPackage(options.getPackageName());
+ if (pkg != null && pkg.isApex()) {
+ // skip APEX
+ return true;
+ }
if (options.isDexoptOnlySecondaryDex()) {
return mPm.getDexManager().dexoptSecondaryDex(options);
@@ -427,6 +432,10 @@ final class DexOptHelper {
// Package could not be found. Report failure.
return PackageDexOptimizer.DEX_OPT_FAILED;
}
+ if (p.isApex()) {
+ // APEX needs no dexopt
+ return PackageDexOptimizer.DEX_OPT_SKIPPED;
+ }
mPm.getPackageUsage().maybeWriteAsync(mPm.mSettings.getPackagesLocked());
mPm.mCompilerStats.maybeWriteAsync();
}
@@ -498,6 +507,9 @@ final class DexOptHelper {
if (packageState == null || pkg == null) {
throw new IllegalArgumentException("Unknown package: " + packageName);
}
+ if (pkg.isApex()) {
+ throw new IllegalArgumentException("Can't dexopt APEX package: " + packageName);
+ }
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
diff --git a/services/core/java/com/android/server/pm/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAppsHelper.java
index 797d4c3cfed3..f6472a739979 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAppsHelper.java
@@ -33,11 +33,9 @@ import static com.android.server.pm.PackageManagerService.SCAN_REQUIRE_KNOWN;
import static com.android.server.pm.PackageManagerService.SYSTEM_PARTITIONS;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_APK_IN_APEX;
-import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_FRAMEWORK_RES_SPLITS;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Environment;
import android.os.SystemClock;
import android.os.Trace;
@@ -121,29 +119,6 @@ final class InitAppsHelper {
mExecutorService = ParallelPackageParser.makeExecutorService();
}
- private List<File> getFrameworkResApkSplitFiles() {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanFrameworkResApkSplits");
- try {
- final List<File> splits = new ArrayList<>();
- final List<ApexManager.ActiveApexInfo> activeApexInfos =
- mPm.mApexManager.getActiveApexInfos();
- for (int i = 0; i < activeApexInfos.size(); i++) {
- ApexManager.ActiveApexInfo apexInfo = activeApexInfos.get(i);
- File splitsFolder = new File(apexInfo.apexDirectory, "etc/splits");
- if (splitsFolder.isDirectory()) {
- for (File file : splitsFolder.listFiles()) {
- if (ApkLiteParseUtils.isApkFile(file)) {
- splits.add(file);
- }
- }
- }
- }
- return splits;
- } finally {
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
- }
-
private List<ScanPartition> getSystemScanPartitions() {
final List<ScanPartition> scanPartitions = new ArrayList<>();
scanPartitions.addAll(mSystemPartitions);
@@ -270,7 +245,7 @@ final class InitAppsHelper {
long startTime) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
- scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0,
+ scanDirTracedLI(mPm.getAppInstallDir(), 0,
mScanFlags | SCAN_REQUIRE_KNOWN, packageParser, mExecutorService);
List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
@@ -338,15 +313,13 @@ final class InitAppsHelper {
if (partition.getOverlayFolder() == null) {
continue;
}
- scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null,
+ scanDirTracedLI(partition.getOverlayFolder(),
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
- List<File> frameworkSplits = getFrameworkResApkSplitFiles();
- scanDirTracedLI(frameworkDir, frameworkSplits,
- mSystemParseFlags | PARSE_FRAMEWORK_RES_SPLITS,
- mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
+ scanDirTracedLI(frameworkDir,
+ mSystemParseFlags, mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
packageParser, executorService);
if (!mPm.mPackages.containsKey("android")) {
throw new IllegalStateException(
@@ -356,12 +329,12 @@ final class InitAppsHelper {
for (int i = 0, size = mDirsToScanAsSystem.size(); i < size; i++) {
final ScanPartition partition = mDirsToScanAsSystem.get(i);
if (partition.getPrivAppFolder() != null) {
- scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null,
+ scanDirTracedLI(partition.getPrivAppFolder(),
mSystemParseFlags,
mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
packageParser, executorService);
}
- scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null,
+ scanDirTracedLI(partition.getAppFolder(),
mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
packageParser, executorService);
}
@@ -379,7 +352,7 @@ final class InitAppsHelper {
}
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- private void scanDirTracedLI(File scanDir, List<File> frameworkSplits,
+ private void scanDirTracedLI(File scanDir,
int parseFlags, int scanFlags,
PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
@@ -388,7 +361,7 @@ final class InitAppsHelper {
// when scanning apk in apexes, we want to check the maxSdkVersion
parseFlags |= PARSE_APK_IN_APEX;
}
- mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
+ mInstallPackageHelper.installPackagesFromDir(scanDir, parseFlags,
scanFlags, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
diff --git a/services/core/java/com/android/server/pm/InstallArgs.java b/services/core/java/com/android/server/pm/InstallArgs.java
index 65c237804d39..a94a4e2a70be 100644
--- a/services/core/java/com/android/server/pm/InstallArgs.java
+++ b/services/core/java/com/android/server/pm/InstallArgs.java
@@ -70,10 +70,9 @@ final class InstallArgs {
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, int packageSource) {
+ int autoRevokePermissionsMode, String traceMethod, int traceCookie,
+ SigningDetails signingDetails, int installReason, int installScenario,
+ boolean forceQueryableOverride, int dataLoaderType, int packageSource) {
mOriginInfo = originInfo;
mMoveInfo = moveInfo;
mInstallFlags = installFlags;
@@ -96,18 +95,6 @@ final class InstallArgs {
mPackageSource = packageSource;
}
- /** New install */
- InstallArgs(InstallingSession 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.mPackageSource);
- }
-
/**
* Create args that describe an existing installed package. Typically used
* when cleaning up old installs, or used as a move source.
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index d20171024cd3..81d47a0f0a9c 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -105,7 +105,6 @@ import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ApplicationInfo;
import android.content.pm.DataLoaderType;
-import android.content.pm.IPackageInstallObserver2;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
@@ -147,6 +146,7 @@ import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.F2fsUtils;
@@ -168,6 +168,7 @@ import com.android.server.pm.permission.Permission;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedLibraryWrapper;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ParsedInstrumentation;
import com.android.server.pm.pkg.component.ParsedPermission;
@@ -275,9 +276,9 @@ final class InstallPackageHelper {
// Prune unused SharedUserSetting
if (mPm.mSettings.checkAndPruneSharedUserLPw(requestSharedUserSetting, false)) {
// Set the app ID in removed info for UID_REMOVED broadcasts
- if (reconciledPkg.mInstallResult != null
- && reconciledPkg.mInstallResult.mRemovedInfo != null) {
- reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId =
+ if (reconciledPkg.mInstallRequest != null
+ && reconciledPkg.mInstallRequest.getRemovedInfo() != null) {
+ reconciledPkg.mInstallRequest.getRemovedInfo().mRemovedAppId =
requestSharedUserSetting.mAppId;
}
}
@@ -307,24 +308,26 @@ final class InstallPackageHelper {
mPm.mSettings.convertSharedUserSettingsLPw(sharedUserSetting);
}
}
- if (reconciledPkg.mInstallArgs != null
- && reconciledPkg.mInstallArgs.mForceQueryableOverride) {
+ if (reconciledPkg.mInstallRequest != null
+ && reconciledPkg.mInstallRequest.isForceQueryableOverride()) {
pkgSetting.setForceQueryableOverride(true);
}
// If this is part of a standard install, set the initiating package name, else rely on
// previous device state.
- if (reconciledPkg.mInstallArgs != null) {
- InstallSource installSource = reconciledPkg.mInstallArgs.mInstallSource;
- if (installSource.initiatingPackageName != null) {
- final PackageSetting ips = mPm.mSettings.getPackageLPr(
- installSource.initiatingPackageName);
- if (ips != null) {
- installSource = installSource.setInitiatingPackageSignatures(
- ips.getSignatures());
+ if (reconciledPkg.mInstallRequest != null) {
+ InstallSource installSource = reconciledPkg.mInstallRequest.getInstallSource();
+ if (installSource != null) {
+ if (installSource.initiatingPackageName != null) {
+ final PackageSetting ips = mPm.mSettings.getPackageLPr(
+ installSource.initiatingPackageName);
+ if (ips != null) {
+ installSource = installSource.setInitiatingPackageSignatures(
+ ips.getSignatures());
+ }
}
+ pkgSetting.setInstallSource(installSource);
}
- pkgSetting.setInstallSource(installSource);
}
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
@@ -349,7 +352,7 @@ final class InstallPackageHelper {
}
if (reconciledPkg.mCollectedSharedLibraryInfos != null
- || (oldPkgSetting != null && oldPkgSetting.getUsesLibraryInfos() != null)) {
+ || (oldPkgSetting != null && oldPkgSetting.getUsesLibraries() != null)) {
// Reconcile if the new package or the old package uses shared libraries.
// It is possible that the old package uses shared libraries but the new one doesn't.
mSharedLibraries.executeSharedLibrariesUpdate(pkg, pkgSetting, null, null,
@@ -418,8 +421,8 @@ final class InstallPackageHelper {
reconciledPkg.mAllowedSharedLibraryInfos,
reconciledPkg.getCombinedAvailablePackages(), scanFlags);
- if (reconciledPkg.mInstallResult != null) {
- reconciledPkg.mInstallResult.mLibraryConsumers = clientLibPkgs;
+ if (reconciledPkg.mInstallRequest != null) {
+ reconciledPkg.mInstallRequest.setLibraryConsumers(clientLibPkgs);
}
if ((scanFlags & SCAN_BOOTING) != 0) {
@@ -440,7 +443,7 @@ final class InstallPackageHelper {
// Also need to kill any apps that are dependent on the library, except the case of
// installation of new version static shared library.
if (clientLibPkgs != null) {
- if (pkg.getStaticSharedLibName() == null || isReplace) {
+ if (pkg.getStaticSharedLibraryName() == null || isReplace) {
for (int i = 0; i < clientLibPkgs.size(); i++) {
AndroidPackage clientPkg = clientLibPkgs.get(i);
mPm.killApplication(clientPkg.getPackageName(),
@@ -615,20 +618,18 @@ final class InstallPackageHelper {
mPm.updateSequenceNumberLP(pkgSetting, new int[]{ userId });
}
// start async restore with no post-install since we finish install here
- PackageInstalledInfo res = new PackageInstalledInfo(
- PackageManager.INSTALL_SUCCEEDED);
- res.mPkg = pkgSetting.getPkg();
- res.mNewUsers = new int[]{ userId };
- PostInstallData postInstallData =
- new PostInstallData(null, res, () -> {
+ InstallRequest request = new InstallRequest(userId,
+ PackageManager.INSTALL_SUCCEEDED, pkgSetting.getPkg(), new int[]{ userId },
+ () -> {
mPm.restorePermissionsAndUpdateRolesForNewUserInstall(packageName,
userId);
if (intentSender != null) {
- onRestoreComplete(res.mReturnCode, mContext, intentSender);
+ onRestoreComplete(PackageManager.INSTALL_SUCCEEDED, mContext,
+ intentSender);
}
});
- restoreAndPostInstall(userId, res, postInstallData);
+ restoreAndPostInstall(request);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -647,18 +648,17 @@ final class InstallPackageHelper {
}
}
- /** @param data Post-install is performed only if this is non-null. */
- public void restoreAndPostInstall(
- int userId, PackageInstalledInfo res, @Nullable PostInstallData data) {
+ public void restoreAndPostInstall(InstallRequest request) {
+ final int userId = request.getUserId();
if (DEBUG_INSTALL) {
- Log.v(TAG, "restoreAndPostInstall userId=" + userId + " package=" + res.mPkg);
+ Log.v(TAG,
+ "restoreAndPostInstall userId=" + userId + " package=" + request.getPkg());
}
// A restore should be requested at this point if (a) the install
// succeeded, (b) the operation is not an update.
- final boolean update = res.mRemovedInfo != null
- && res.mRemovedInfo.mRemovedPackage != null;
- boolean doRestore = !update && res.mPkg != null;
+ final boolean update = request.isUpdate();
+ boolean doRestore = !update && request.getPkg() != 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
@@ -666,23 +666,17 @@ final class InstallPackageHelper {
int token;
if (mPm.mNextInstallToken < 0) mPm.mNextInstallToken = 1;
token = mPm.mNextInstallToken++;
- if (data != null) {
- mPm.mRunningInstalls.put(token, data);
- } else if (DEBUG_INSTALL) {
- Log.v(TAG, "No post-install required for " + token);
- }
+ mPm.mRunningInstalls.put(token, request);
if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
- if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
+ if (request.getReturnCode() == 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.mFreezer != null) {
- res.mFreezer.close();
- }
- doRestore = performBackupManagerRestore(userId, token, res);
+ request.closeFreezer();
+ doRestore = performBackupManagerRestore(userId, token, request);
}
// If this is an update to a package that might be potentially downgraded, then we
@@ -690,8 +684,8 @@ final class InstallPackageHelper {
// need to be snapshotted or restored for the package.
//
// TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
- if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
- doRestore = performRollbackManagerRestore(userId, token, res, data);
+ if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
+ doRestore = performRollbackManagerRestore(userId, token, request);
}
if (!doRestore) {
@@ -707,10 +701,10 @@ final class InstallPackageHelper {
}
/**
- * Perform Backup Manager restore for a given {@link PackageInstalledInfo}.
+ * Perform Backup Manager restore for a given {@link InstallRequest}.
* Returns whether the restore successfully completed.
*/
- private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
+ private boolean performBackupManagerRestore(int userId, int token, InstallRequest request) {
IBackupManager iBackupManager = mInjector.getIBackupManager();
if (iBackupManager != null) {
// For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
@@ -725,7 +719,7 @@ final class InstallPackageHelper {
try {
if (iBackupManager.isUserReadyForBackup(userId)) {
iBackupManager.restoreAtInstallForUser(
- userId, res.mPkg.getPackageName(), token);
+ userId, request.getPkg().getPackageName(), token);
} else {
Slog.w(TAG, "User " + userId + " is not ready. Restore at install "
+ "didn't take place.");
@@ -745,12 +739,11 @@ final class InstallPackageHelper {
}
/**
- * Perform Rollback Manager restore for a given {@link PackageInstalledInfo}.
+ * Perform Rollback Manager restore for a given {@link InstallRequest}.
* Returns whether the restore successfully completed.
*/
- private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
- PostInstallData data) {
- final String packageName = res.mPkg.getPackageName();
+ private boolean performRollbackManagerRestore(int userId, int token, InstallRequest request) {
+ final String packageName = request.getPkg().getPackageName();
final int[] allUsers = mPm.mUserManager.getUserIds();
final int[] installedUsers;
@@ -762,19 +755,20 @@ final class InstallPackageHelper {
if (ps != null) {
appId = ps.getAppId();
ceDataInode = ps.getCeDataInode(userId);
+ // NOTE: We ignore the user specified in the InstallParam because we know this is
+ // an update, and hence need to restore data for all installed users.
+ installedUsers = ps.queryInstalledUsers(allUsers, true);
+ } else {
+ installedUsers = new int[0];
}
-
- // NOTE: We ignore the user specified in the InstallParam because we know this is
- // an update, and hence need to restore data for all installed users.
- installedUsers = ps.queryInstalledUsers(allUsers, true);
}
- boolean doSnapshotOrRestore = data != null && data.args != null
- && ((data.args.mInstallFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
- || (data.args.mInstallFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
+ final int installFlags = request.getInstallFlags();
+ boolean doSnapshotOrRestore = ((installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
+ || (installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
if (ps != null && doSnapshotOrRestore) {
- final String seInfo = AndroidPackageUtils.getSeInfo(res.mPkg, ps);
+ final String seInfo = AndroidPackageUtils.getSeInfo(request.getPkg(), ps);
final RollbackManagerInternal rollbackManager =
mInjector.getLocalService(RollbackManagerInternal.class);
rollbackManager.snapshotAndRestoreUserData(packageName,
@@ -817,9 +811,8 @@ final class InstallPackageHelper {
@GuardedBy("mPm.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, InstallRequest> installRequests = new ArrayMap<>(requests.size());
final Map<String, Settings.VersionInfo> versionInfos = new ArrayMap<>(requests.size());
final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());
boolean success = false;
@@ -832,32 +825,30 @@ final class InstallPackageHelper {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "preparePackage");
prepareResult =
- preparePackageLI(request.mArgs, request.mInstallResult);
+ preparePackageLI(request);
} catch (PrepareFailure prepareFailure) {
- request.mInstallResult.setError(prepareFailure.error,
+ request.setError(prepareFailure.error,
prepareFailure.getMessage());
- request.mInstallResult.mOrigPackage = prepareFailure.mConflictingPackage;
- request.mInstallResult.mOrigPermission = prepareFailure.mConflictingPermission;
+ request.setOriginPackage(prepareFailure.mConflictingPackage);
+ request.setOriginPermission(prepareFailure.mConflictingPermission);
return;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- request.mInstallResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
- request.mInstallResult.mInstallerPackageName =
- request.mArgs.mInstallSource.installerPackageName;
+ request.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ request.setInstallerPackageName(request.getSourceInstallerPackageName());
final String packageName = prepareResult.mPackageToScan.getPackageName();
prepareResults.put(packageName, prepareResult);
- installResults.put(packageName, request.mInstallResult);
- installArgs.put(packageName, request.mArgs);
+ installRequests.put(packageName, request);
try {
final ScanResult result = scanPackageTracedLI(
prepareResult.mPackageToScan, prepareResult.mParseFlags,
prepareResult.mScanFlags, System.currentTimeMillis(),
- request.mArgs.mUser, request.mArgs.mAbiOverride);
+ request.getUser(), request.getAbiOverride());
if (null != preparedScans.put(result.mPkgSetting.getPkg().getPackageName(),
result)) {
- request.mInstallResult.setError(
+ request.setError(
PackageManager.INSTALL_FAILED_DUPLICATE_PACKAGE,
"Duplicate package "
+ result.mPkgSetting.getPkg().getPackageName()
@@ -868,7 +859,7 @@ final class InstallPackageHelper {
result.mRequest.mOldPkg, result.mPkgSetting.getPkg())) {
// TODO: INSTALL_FAILED_UPDATE_INCOMPATIBLE is about incomptabible
// signatures. Is there a better error code?
- request.mInstallResult.setError(
+ request.setError(
INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Update attempted to change value of "
+ PackageManager.PROPERTY_NO_APP_DATA_STORAGE);
@@ -883,15 +874,15 @@ final class InstallPackageHelper {
versionInfos.put(result.mPkgSetting.getPkg().getPackageName(),
mPm.getSettingsVersionForPackage(result.mPkgSetting.getPkg()));
} catch (PackageManagerException e) {
- request.mInstallResult.setError("Scanning Failed.", e);
+ request.setError("Scanning Failed.", e);
return;
}
}
CommitRequest commitRequest;
synchronized (mPm.mLock) {
- ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
- installResults, prepareResults,
+ ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans,
+ installRequests, prepareResults,
Collections.unmodifiableMap(mPm.mPackages), versionInfos);
Map<String, ReconciledPackage> reconciledPackages;
try {
@@ -901,7 +892,7 @@ final class InstallPackageHelper {
mPm.mSettings.getKeySetManagerService(), mPm.mSettings);
} catch (ReconcileFailure e) {
for (InstallRequest request : requests) {
- request.mInstallResult.setError("Reconciliation failed...", e);
+ request.setError("Reconciliation failed...", e);
}
return;
} finally {
@@ -921,25 +912,24 @@ final class InstallPackageHelper {
} finally {
if (success) {
for (InstallRequest request : requests) {
- final InstallArgs args = request.mArgs;
- if (args.mDataLoaderType != DataLoaderType.INCREMENTAL) {
+ if (request.getDataLoaderType() != DataLoaderType.INCREMENTAL) {
continue;
}
- if (args.mSigningDetails.getSignatureSchemeVersion() != SIGNING_BLOCK_V4) {
+ if (request.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 String baseCodePath = request.getPkg().getBaseApkPath();
+ final String[] splitCodePaths = request.getPkg().getSplitCodePaths();
+ final Uri originUri = request.getOriginUri();
final int verificationId = mPm.mPendingVerificationToken++;
final String rootHashString = PackageManagerServiceUtils
.buildVerificationRootHashString(baseCodePath, splitCodePaths);
VerificationUtils.broadcastPackageVerified(verificationId, originUri,
PackageManager.VERIFICATION_ALLOW, rootHashString,
- args.mDataLoaderType, args.mUser, mContext);
+ request.getDataLoaderType(), request.getUser(), mContext);
}
} else {
for (ScanResult result : preparedScans.values()) {
@@ -951,11 +941,9 @@ final class InstallPackageHelper {
// 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;
+ request.closeFreezer();
+ if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
+ request.setReturnCode(PackageManager.INSTALL_UNKNOWN);
}
}
}
@@ -980,18 +968,19 @@ final class InstallPackageHelper {
}
@GuardedBy("mPm.mInstallLock")
- private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
+ private PrepareResult preparePackageLI(InstallRequest request)
throws PrepareFailure {
- final int installFlags = args.mInstallFlags;
- final boolean onExternal = args.mVolumeUuid != null;
+ final int installFlags = request.getInstallFlags();
+ final boolean onExternal = request.getVolumeUuid() != 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 isApex = ((installFlags & PackageManager.INSTALL_APEX) != 0);
- final boolean isRollback = args.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ final boolean isRollback =
+ request.getInstallReason() == PackageManager.INSTALL_REASON_ROLLBACK;
@PackageManagerService.ScanFlags int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
- if (args.mMoveInfo != null) {
+ if (request.isMoveInstall()) {
// moving a complete application; perform an initial scan on the new install location
scanFlags |= SCAN_INITIAL;
}
@@ -1012,7 +1001,7 @@ final class InstallPackageHelper {
}
final File tmpPackageFile = new File(
- isApex ? res.mApexInfo.modulePath : args.getCodePath());
+ isApex ? request.getApexInfo().modulePath : request.getCodePath());
if (DEBUG_INSTALL) Slog.d(TAG, "installPackageLI: path=" + tmpPackageFile);
// Validity check
@@ -1066,7 +1055,8 @@ final class InstallPackageHelper {
}
}
- String pkgName = res.mName = parsedPackage.getPackageName();
+ String pkgName = parsedPackage.getPackageName();
+ request.setName(pkgName);
if (parsedPackage.isTestOnly()) {
if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {
throw new PrepareFailure(INSTALL_FAILED_TEST_ONLY, "installPackageLI");
@@ -1074,8 +1064,8 @@ final class InstallPackageHelper {
}
// either use what we've been given or parse directly from the APK
- if (args.mSigningDetails != SigningDetails.UNKNOWN) {
- parsedPackage.setSigningDetails(args.mSigningDetails);
+ if (request.getSigningDetails() != SigningDetails.UNKNOWN) {
+ parsedPackage.setSigningDetails(request.getSigningDetails());
} else {
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
final ParseResult<SigningDetails> result = ParsingPackageUtils.getSigningDetails(
@@ -1152,7 +1142,7 @@ final class InstallPackageHelper {
if (signatureCheckPs == null && parsedPackage.isSdkLibrary()) {
WatchedLongSparseArray<SharedLibraryInfo> libraryInfos =
mSharedLibraries.getSharedLibraryInfos(
- parsedPackage.getSdkLibName());
+ parsedPackage.getSdkLibraryName());
if (libraryInfos != null && libraryInfos.size() > 0) {
// Any existing version would do.
SharedLibraryInfo libraryInfo = libraryInfos.valueAt(0);
@@ -1223,7 +1213,8 @@ final class InstallPackageHelper {
if (ps.getPkg() != null) {
systemApp = ps.getPkg().isSystem();
}
- res.mOrigUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true);
+ request.setOriginUsers(
+ ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true));
}
final int numGroups = ArrayUtils.size(parsedPackage.getPermissionGroups());
@@ -1299,7 +1290,7 @@ final class InstallPackageHelper {
// it as dangerous leading to the group auto-grant.
if ((perm.getProtectionLevel() & PermissionInfo.PROTECTION_MASK_BASE)
== PermissionInfo.PROTECTION_DANGEROUS) {
- if (bp != null && !bp.isRuntime()) {
+ if (!bp.isRuntime()) {
Slog.w(TAG, "Package " + parsedPackage.getPackageName()
+ " trying to change a non-runtime permission "
+ perm.getName()
@@ -1372,7 +1363,7 @@ final class InstallPackageHelper {
}
}
- if (args.mMoveInfo != null) {
+ if (request.isMoveInstall()) {
// We did an in-place move, so dex is ready to roll
scanFlags |= SCAN_NO_DEX;
scanFlags |= SCAN_MOVE;
@@ -1380,7 +1371,7 @@ final class InstallPackageHelper {
synchronized (mPm.mLock) {
final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
if (ps == null) {
- res.setError(INSTALL_FAILED_INTERNAL_ERROR,
+ request.setError(INSTALL_FAILED_INTERNAL_ERROR,
"Missing settings for moved package " + pkgName);
}
@@ -1403,7 +1394,7 @@ final class InstallPackageHelper {
}
boolean isUpdatedSystemAppFromExistingSetting = pkgSetting != null
&& pkgSetting.getPkgState().isUpdatedSystemApp();
- final String abiOverride = deriveAbiOverride(args.mAbiOverride);
+ final String abiOverride = deriveAbiOverride(request.getAbiOverride());
boolean isUpdatedSystemAppInferred = oldPackage != null && oldPackage.isSystem();
final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths>
derivedAbi = mPackageAbiHelper.derivePackageAbi(parsedPackage,
@@ -1419,7 +1410,7 @@ final class InstallPackageHelper {
}
if (!isApex) {
- doRenameLI(args, res.mReturnCode, res.mReturnMsg, parsedPackage);
+ doRenameLI(request, parsedPackage);
try {
setUpFsVerityIfPossible(parsedPackage);
@@ -1430,8 +1421,8 @@ final class InstallPackageHelper {
}
} else {
// Use the path returned by apexd
- parsedPackage.setPath(res.mApexInfo.modulePath);
- parsedPackage.setBaseApkPath(res.mApexInfo.modulePath);
+ parsedPackage.setPath(request.getApexInfo().modulePath);
+ parsedPackage.setBaseApkPath(request.getApexInfo().modulePath);
}
final PackageFreezer freezer =
@@ -1559,8 +1550,8 @@ final class InstallPackageHelper {
// don't allow an upgrade from full to ephemeral
if (isInstantApp) {
- if (args.mUser == null
- || args.mUser.getIdentifier() == UserHandle.USER_ALL) {
+ if (request.getUser() == null
+ || request.getUserId() == UserHandle.USER_ALL) {
for (int currentUser : allUsers) {
if (!ps.getInstantApp(currentUser)) {
// can't downgrade from full to instant
@@ -1571,10 +1562,10 @@ final class InstallPackageHelper {
PackageManager.INSTALL_FAILED_SESSION_INVALID);
}
}
- } else if (!ps.getInstantApp(args.mUser.getIdentifier())) {
+ } else if (!ps.getInstantApp(request.getUserId())) {
// can't downgrade from full to instant
Slog.w(TAG, "Can't replace full app with instant app: " + pkgName11
- + " for user: " + args.mUser.getIdentifier());
+ + " for user: " + request.getUserId());
throw new PrepareFailure(
PackageManager.INSTALL_FAILED_SESSION_INVALID);
}
@@ -1582,25 +1573,29 @@ final class InstallPackageHelper {
}
// Update what is removed
- res.mRemovedInfo = new PackageRemovedInfo(mPm);
- res.mRemovedInfo.mUid = oldPackage.getUid();
- res.mRemovedInfo.mRemovedPackage = oldPackage.getPackageName();
- res.mRemovedInfo.mInstallerPackageName = ps.getInstallSource().installerPackageName;
- res.mRemovedInfo.mIsStaticSharedLib =
- parsedPackage.getStaticSharedLibName() != null;
- res.mRemovedInfo.mIsUpdate = true;
- res.mRemovedInfo.mOrigUsers = installedUsers;
- res.mRemovedInfo.mInstallReasons = new SparseArray<>(installedUsers.length);
+ PackageRemovedInfo removedInfo = new PackageRemovedInfo(mPm);
+ removedInfo.mUid = oldPackage.getUid();
+ removedInfo.mRemovedPackage = oldPackage.getPackageName();
+ removedInfo.mInstallerPackageName =
+ ps.getInstallSource().installerPackageName;
+ removedInfo.mIsStaticSharedLib =
+ parsedPackage.getStaticSharedLibraryName() != null;
+ removedInfo.mIsUpdate = true;
+ removedInfo.mOrigUsers = installedUsers;
+ removedInfo.mInstallReasons = new SparseIntArray(installedUsers.length);
for (int i = 0; i < installedUsers.length; i++) {
final int userId = installedUsers[i];
- res.mRemovedInfo.mInstallReasons.put(userId, ps.getInstallReason(userId));
+ removedInfo.mInstallReasons.put(userId,
+ ps.getInstallReason(userId));
}
- res.mRemovedInfo.mUninstallReasons = new SparseArray<>(uninstalledUsers.length);
+ removedInfo.mUninstallReasons = new SparseIntArray(uninstalledUsers.length);
for (int i = 0; i < uninstalledUsers.length; i++) {
final int userId = uninstalledUsers[i];
- res.mRemovedInfo.mUninstallReasons.put(userId, ps.getUninstallReason(userId));
+ removedInfo.mUninstallReasons.put(userId,
+ ps.getUninstallReason(userId));
}
- res.mRemovedInfo.mIsExternal = oldPackage.isExternalStorage();
+ removedInfo.mIsExternal = oldPackage.isExternalStorage();
+ request.setRemovedInfo(removedInfo);
sysPkg = oldPackage.isSystem();
if (sysPkg) {
@@ -1625,7 +1620,7 @@ final class InstallPackageHelper {
Slog.d(TAG, "replaceSystemPackageLI: new=" + parsedPackage
+ ", old=" + oldPackage);
}
- res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ request.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
targetParseFlags = systemParseFlags;
targetScanFlags = systemScanFlags;
} else { // non system replace
@@ -1674,7 +1669,7 @@ final class InstallPackageHelper {
oldPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
ps, disabledPs);
} finally {
- res.mFreezer = freezer;
+ request.setFreezer(freezer);
if (shouldCloseFreezerBeforeReturn) {
freezer.close();
}
@@ -1686,24 +1681,26 @@ final class InstallPackageHelper {
* scanned package should be updated to reflect the rename.
*/
@GuardedBy("mPm.mInstallLock")
- private void doRenameLI(InstallArgs args, int status, String statusMsg,
+ private void doRenameLI(InstallRequest request,
ParsedPackage parsedPackage) throws PrepareFailure {
- if (args.mMoveInfo != null) {
+ final int status = request.getReturnCode();
+ final String statusMsg = request.getReturnMsg();
+ if (request.isMoveInstall()) {
if (status != PackageManager.INSTALL_SUCCEEDED) {
- mRemovePackageHelper.cleanUpForMoveInstall(args.mMoveInfo.mToUuid,
- args.mMoveInfo.mPackageName, args.mMoveInfo.mFromCodePath);
+ mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(),
+ request.getMovePackageName(), request.getMoveFromCodePath());
throw new PrepareFailure(status, statusMsg);
}
return;
}
// For file installations
if (status != PackageManager.INSTALL_SUCCEEDED) {
- mRemovePackageHelper.removeCodePath(args.mCodeFile);
+ mRemovePackageHelper.removeCodePath(request.getCodeFile());
throw new PrepareFailure(status, statusMsg);
}
- final File targetDir = resolveTargetDir(args);
- final File beforeCodeFile = args.mCodeFile;
+ final File targetDir = resolveTargetDir(request.getInstallFlags(), request.getCodeFile());
+ final File beforeCodeFile = request.getCodeFile();
final File afterCodeFile = PackageManagerServiceUtils.getNextCodePath(targetDir,
parsedPackage.getPackageName());
@@ -1732,7 +1729,7 @@ final class InstallPackageHelper {
}
// Reflect the rename internally
- args.mCodeFile = afterCodeFile;
+ request.setCodeFile(afterCodeFile);
// Reflect the rename in scanned details
try {
@@ -1750,12 +1747,12 @@ final class InstallPackageHelper {
// 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(InstallArgs args) {
- boolean isStagedInstall = (args.mInstallFlags & INSTALL_STAGED) != 0;
+ private File resolveTargetDir(int installFlags, File codeFile) {
+ boolean isStagedInstall = (installFlags & INSTALL_STAGED) != 0;
if (isStagedInstall) {
return Environment.getDataAppDirectory(null);
} else {
- return args.mCodeFile.getParentFile();
+ return codeFile.getParentFile();
}
}
@@ -1905,7 +1902,7 @@ final class InstallPackageHelper {
final ScanRequest scanRequest = scanResult.mRequest;
final ParsedPackage parsedPackage = scanRequest.mParsedPackage;
final String packageName = parsedPackage.getPackageName();
- final PackageInstalledInfo res = reconciledPkg.mInstallResult;
+ final InstallRequest installRequest = reconciledPkg.mInstallRequest;
final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
final DeletePackageHelper deletePackageHelper = new DeletePackageHelper(mPm);
@@ -1921,9 +1918,10 @@ final class InstallPackageHelper {
.setFirstInstallTimeFromReplaced(deletedPkgSetting, request.mAllUsers)
.setLastUpdateTime(System.currentTimeMillis());
- res.mRemovedInfo.mBroadcastAllowList = mPm.mAppsFilter.getVisibilityAllowList(
- mPm.snapshotComputer(), reconciledPkg.mPkgSetting, request.mAllUsers,
- mPm.mSettings.getPackagesLocked());
+ installRequest.getRemovedInfo().mBroadcastAllowList =
+ mPm.mAppsFilter.getVisibilityAllowList(mPm.snapshotComputer(),
+ reconciledPkg.mPkgSetting, request.mAllUsers,
+ mPm.mSettings.getPackagesLocked());
if (reconciledPkg.mPrepareResult.mSystem) {
// Remove existing system package
removePackageHelper.removePackage(oldPackage, true);
@@ -1931,7 +1929,7 @@ final class InstallPackageHelper {
// 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 = new InstallArgs(
+ installRequest.getRemovedInfo().mArgs = new InstallArgs(
oldPackage.getPath(),
getAppDexInstructionSets(
AndroidPackageUtils.getPrimaryCpuAbi(oldPackage,
@@ -1939,7 +1937,7 @@ final class InstallPackageHelper {
AndroidPackageUtils.getSecondaryCpuAbi(oldPackage,
deletedPkgSetting)));
} else {
- res.mRemovedInfo.mArgs = null;
+ installRequest.getRemovedInfo().mArgs = null;
}
} else {
try {
@@ -1957,7 +1955,7 @@ final class InstallPackageHelper {
// 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)
+ if ((installRequest.getInstallFlags() & PackageManager.DONT_KILL_APP)
== 0) {
Set<String> oldCodePaths = ps1.getOldCodePaths();
if (oldCodePaths == null) {
@@ -1970,12 +1968,11 @@ final class InstallPackageHelper {
ps1.setOldCodePaths(null);
}
- if (reconciledPkg.mInstallResult.mReturnCode
- == PackageManager.INSTALL_SUCCEEDED) {
+ if (installRequest.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
PackageSetting ps2 = mPm.mSettings.getPackageLPr(
parsedPackage.getPackageName());
if (ps2 != null) {
- res.mRemovedInfo.mRemovedForAllUsers =
+ installRequest.getRemovedInfo().mRemovedForAllUsers =
mPm.mPackages.get(ps2.getPackageName()) == null;
}
}
@@ -1984,15 +1981,16 @@ final class InstallPackageHelper {
AndroidPackage pkg = commitReconciledScanResultLocked(
reconciledPkg, request.mAllUsers);
- updateSettingsLI(pkg, reconciledPkg, request.mAllUsers, res);
+ updateSettingsLI(pkg, reconciledPkg, request.mAllUsers, installRequest);
final PackageSetting ps = mPm.mSettings.getPackageLPr(packageName);
if (ps != null) {
- res.mNewUsers = ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true);
+ installRequest.setNewUsers(
+ ps.queryInstalledUsers(mPm.mUserManager.getUserIds(), true));
ps.setUpdateAvailable(false /*updateAvailable*/);
}
- if (res.mReturnCode == PackageManager.INSTALL_SUCCEEDED) {
- mPm.updateSequenceNumberLP(ps, res.mNewUsers);
+ if (installRequest.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
+ mPm.updateSequenceNumberLP(ps, installRequest.getNewUsers());
mPm.updateInstantAppInstallerLocked(packageName);
}
}
@@ -2005,20 +2003,18 @@ final class InstallPackageHelper {
}
private void updateSettingsLI(AndroidPackage newPackage, ReconciledPackage reconciledPkg,
- int[] allUsers, PackageInstalledInfo res) {
- updateSettingsInternalLI(newPackage, reconciledPkg, allUsers, res);
+ int[] allUsers, InstallRequest installRequest) {
+ updateSettingsInternalLI(newPackage, reconciledPkg, allUsers, installRequest);
}
private void updateSettingsInternalLI(AndroidPackage pkg, ReconciledPackage reconciledPkg,
- int[] allUsers, PackageInstalledInfo res) {
+ int[] allUsers, InstallRequest installRequest) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings");
final String pkgName = pkg.getPackageName();
- final int[] installedForUsers = res.mOrigUsers;
- final InstallArgs installArgs = reconciledPkg.mInstallArgs;
- final int installReason = installArgs.mInstallReason;
- InstallSource installSource = installArgs.mInstallSource;
- final String installerPackageName = installSource.installerPackageName;
+ final int[] installedForUsers = installRequest.getOriginUsers();
+ final int installReason = installRequest.getInstallReason();
+ final String installerPackageName = installRequest.getSourceInstallerPackageName();
if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + pkg.getPath());
synchronized (mPm.mLock) {
@@ -2026,15 +2022,15 @@ final class InstallPackageHelper {
// 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();
+ final int userId = installRequest.getUserId();
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 (installedForUsers != null) {
+ for (int origUserId : installedForUsers) {
if (userId == UserHandle.USER_ALL || userId == origUserId) {
ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT,
origUserId, installerPackageName);
@@ -2064,9 +2060,9 @@ final class InstallPackageHelper {
// Retrieve the overlays for shared libraries of the package.
if (!ps.getPkgState().getUsesLibraryInfos().isEmpty()) {
- for (SharedLibraryInfo sharedLib : ps.getPkgState().getUsesLibraryInfos()) {
+ for (SharedLibraryWrapper sharedLib : ps.getPkgState().getUsesLibraryInfos()) {
for (int currentUserId : UserManagerService.getInstance().getUserIds()) {
- if (!sharedLib.isDynamic()) {
+ if (sharedLib.getType() != SharedLibraryInfo.TYPE_DYNAMIC) {
// TODO(146804378): Support overlaying static shared libraries
continue;
}
@@ -2101,20 +2097,27 @@ final class InstallPackageHelper {
// 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();
+ if (installRequest.getRemovedInfo() != null
+ && installRequest.getRemovedInfo().mInstallReasons != null) {
+ final int installReasonCount =
+ installRequest.getRemovedInfo().mInstallReasons.size();
for (int i = 0; i < installReasonCount; i++) {
- final int previousUserId = res.mRemovedInfo.mInstallReasons.keyAt(i);
+ final int previousUserId =
+ installRequest.getRemovedInfo().mInstallReasons.keyAt(i);
final int previousInstallReason =
- res.mRemovedInfo.mInstallReasons.valueAt(i);
+ installRequest.getRemovedInfo().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);
+ if (installRequest.getRemovedInfo() != null
+ && installRequest.getRemovedInfo().mUninstallReasons != null) {
+ for (int i = 0; i < installRequest.getRemovedInfo().mUninstallReasons.size();
+ i++) {
+ final int previousUserId =
+ installRequest.getRemovedInfo().mUninstallReasons.keyAt(i);
+ final int previousReason =
+ installRequest.getRemovedInfo().mUninstallReasons.valueAt(i);
ps.setUninstallReason(previousReason, previousUserId);
}
}
@@ -2153,41 +2156,41 @@ final class InstallPackageHelper {
final PermissionManagerServiceInternal.PackageInstalledParams.Builder
permissionParamsBuilder =
new PermissionManagerServiceInternal.PackageInstalledParams.Builder();
- final boolean grantPermissions = (installArgs.mInstallFlags
+ final boolean grantPermissions = (installRequest.getInstallFlags()
& PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
if (grantPermissions) {
final List<String> grantedPermissions =
- installArgs.mInstallGrantPermissions != null
- ? Arrays.asList(installArgs.mInstallGrantPermissions)
+ installRequest.getInstallGrantPermissions() != null
+ ? Arrays.asList(installRequest.getInstallGrantPermissions())
: pkg.getRequestedPermissions();
permissionParamsBuilder.setGrantedPermissions(grantedPermissions);
}
final boolean allowlistAllRestrictedPermissions =
- (installArgs.mInstallFlags
+ (installRequest.getInstallFlags()
& PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS) != 0;
final List<String> allowlistedRestrictedPermissions =
allowlistAllRestrictedPermissions ? pkg.getRequestedPermissions()
- : installArgs.mAllowlistedRestrictedPermissions;
+ : installRequest.getAllowlistedRestrictedPermissions();
if (allowlistedRestrictedPermissions != null) {
permissionParamsBuilder.setAllowlistedRestrictedPermissions(
allowlistedRestrictedPermissions);
}
- final int autoRevokePermissionsMode = installArgs.mAutoRevokePermissionsMode;
+ final int autoRevokePermissionsMode = installRequest.getAutoRevokePermissionsMode();
permissionParamsBuilder.setAutoRevokePermissionsMode(autoRevokePermissionsMode);
final ScanResult scanResult = reconciledPkg.mScanResult;
mPm.mPermissionManager.onPackageInstalled(pkg, scanResult.mPreviousAppId,
permissionParamsBuilder.build(), userId);
// Apply restricted settings on potentially dangerous packages.
- if (installArgs.mPackageSource == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
- || installArgs.mPackageSource
+ if (installRequest.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE
+ || installRequest.getPackageSource()
== PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {
enableRestrictedSettings(pkgName, pkg.getUid());
}
}
- res.mName = pkgName;
- res.mUid = pkg.getUid();
- res.mPkg = pkg;
- res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
+ installRequest.setName(pkgName);
+ installRequest.setUid(pkg.getUid());
+ installRequest.setPkg(pkg);
+ installRequest.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
//to update install status
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "writeSettings");
mPm.writeSettingsLPrTEMP();
@@ -2252,13 +2255,13 @@ final class InstallPackageHelper {
// can be used for optimizations.
mArtManagerService.prepareAppProfiles(
pkg,
- mPm.resolveUserIds(reconciledPkg.mInstallArgs.mUser.getIdentifier()),
+ mPm.resolveUserIds(reconciledPkg.mInstallRequest.getUserId()),
/* updateReferenceProfileContent= */ true);
// Compute the compilation reason from the installation scenario.
final int compilationReason =
mDexManager.getCompilationReasonForInstallScenario(
- reconciledPkg.mInstallArgs.mInstallScenario);
+ reconciledPkg.mInstallRequest.getInstallScenario());
// Construct the DexoptOptions early to see if we should skip running dexopt.
//
@@ -2267,8 +2270,9 @@ final class InstallPackageHelper {
//
// Also, don't fail application installs if the dexopt step fails.
final boolean isBackupOrRestore =
- reconciledPkg.mInstallArgs.mInstallReason == INSTALL_REASON_DEVICE_RESTORE
- || reconciledPkg.mInstallArgs.mInstallReason
+ reconciledPkg.mInstallRequest.getInstallReason()
+ == INSTALL_REASON_DEVICE_RESTORE
+ || reconciledPkg.mInstallRequest.getInstallReason()
== INSTALL_REASON_DEVICE_SETUP;
final int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
@@ -2580,35 +2584,31 @@ final class InstallPackageHelper {
}
}
- void handlePackagePostInstall(PackageInstalledInfo res, InstallArgs installArgs,
- boolean launchedForRestore) {
+ void handlePackagePostInstall(InstallRequest request, boolean launchedForRestore) {
final boolean killApp =
- (installArgs.mInstallFlags & PackageManager.INSTALL_DONT_KILL_APP) == 0;
+ (request.getInstallFlags() & PackageManager.INSTALL_DONT_KILL_APP) == 0;
final boolean virtualPreload =
- ((installArgs.mInstallFlags & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
- final String installerPackage = installArgs.mInstallSource.installerPackageName;
- final IPackageInstallObserver2 installObserver = installArgs.mObserver;
- final int dataLoaderType = installArgs.mDataLoaderType;
- final boolean succeeded = res.mReturnCode == PackageManager.INSTALL_SUCCEEDED;
- final boolean update = res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null;
- final String packageName = res.mName;
+ ((request.getInstallFlags() & PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
+ final String installerPackage = request.getInstallerPackageName();
+ final int dataLoaderType = request.getDataLoaderType();
+ final boolean succeeded = request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED;
+ final boolean update = request.isUpdate();
+ final String packageName = request.getName();
final PackageStateInternal pkgSetting =
succeeded ? mPm.snapshotComputer().getPackageStateInternal(packageName) : null;
final boolean removedBeforeUpdate = (pkgSetting == null)
|| (pkgSetting.isSystem() && !pkgSetting.getPath().getPath().equals(
- res.mPkg.getPath()));
+ request.getPkg().getPath()));
if (succeeded && removedBeforeUpdate) {
Slog.e(TAG, packageName + " was removed before handlePackagePostInstall "
+ "could be executed");
- res.mReturnCode = INSTALL_FAILED_PACKAGE_CHANGED;
- res.mReturnMsg = "Package was removed before install could complete.";
+ request.setReturnCode(INSTALL_FAILED_PACKAGE_CHANGED);
+ request.setReturnMessage("Package was removed before install could complete.");
// Remove the update failed package's older resources safely now
- InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
- if (args != null) {
- mRemovePackageHelper.cleanUpResources(args);
- }
- mPm.notifyInstallObserver(res, installObserver);
+ mRemovePackageHelper.cleanUpResources(request.getOldCodeFile(),
+ request.getOldInstructionSet());
+ mPm.notifyInstallObserver(request);
return;
}
@@ -2617,28 +2617,31 @@ final class InstallPackageHelper {
mPm.mPerUidReadTimeoutsCache = null;
// Send the removed broadcasts
- if (res.mRemovedInfo != null) {
- if (res.mRemovedInfo.mIsExternal) {
+ if (request.getRemovedInfo() != null) {
+ if (request.getRemovedInfo().mIsExternal) {
if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.mRemovedInfo.mRemovedPackage
+ Slog.i(TAG, "upgrading pkg " + request.getRemovedInfo().mRemovedPackage
+ " is ASEC-hosted -> UNAVAILABLE");
}
- final String[] pkgNames = new String[]{res.mRemovedInfo.mRemovedPackage};
- final int[] uids = new int[]{res.mRemovedInfo.mUid};
+ final String[] pkgNames = new String[]{
+ request.getRemovedInfo().mRemovedPackage};
+ final int[] uids = new int[]{request.getRemovedInfo().mUid};
mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer,
false /* mediaStatus */, true /* replacing */, pkgNames, uids);
}
- res.mRemovedInfo.sendPackageRemovedBroadcasts(killApp, false /*removedBySystem*/);
+ request.getRemovedInfo().sendPackageRemovedBroadcasts(
+ killApp, false /*removedBySystem*/);
}
final String installerPackageName =
- res.mInstallerPackageName != null
- ? res.mInstallerPackageName
- : res.mRemovedInfo != null
- ? res.mRemovedInfo.mInstallerPackageName
+ request.getInstallerPackageName() != null
+ ? request.getInstallerPackageName()
+ : request.getRemovedInfo() != null
+ ? request.getRemovedInfo().mInstallerPackageName
: null;
- mPm.notifyInstantAppPackageInstalled(res.mPkg.getPackageName(), res.mNewUsers);
+ mPm.notifyInstantAppPackageInstalled(request.getPkg().getPackageName(),
+ request.getNewUsers());
// Determine the set of users who are adding this package for
// the first time vs. those who are seeing an update.
@@ -2646,8 +2649,9 @@ final class InstallPackageHelper {
int[] firstInstantUserIds = EMPTY_INT_ARRAY;
int[] updateUserIds = EMPTY_INT_ARRAY;
int[] instantUserIds = EMPTY_INT_ARRAY;
- final boolean allNewUsers = res.mOrigUsers == null || res.mOrigUsers.length == 0;
- for (int newUser : res.mNewUsers) {
+ final boolean allNewUsers = request.getOriginUsers() == null
+ || request.getOriginUsers().length == 0;
+ for (int newUser : request.getNewUsers()) {
final boolean isInstantApp = pkgSetting.getUserStateOrDefault(newUser)
.isInstantApp();
if (allNewUsers) {
@@ -2659,7 +2663,7 @@ final class InstallPackageHelper {
continue;
}
boolean isNew = true;
- for (int origUser : res.mOrigUsers) {
+ for (int origUser : request.getOriginUsers()) {
if (origUser == newUser) {
isNew = false;
break;
@@ -2681,20 +2685,20 @@ final class InstallPackageHelper {
}
// Send installed broadcasts if the package is not a static shared lib.
- if (res.mPkg.getStaticSharedLibName() == null) {
- mPm.mProcessLoggingHandler.invalidateBaseApkHash(res.mPkg.getBaseApkPath());
+ if (request.getPkg().getStaticSharedLibraryName() == null) {
+ mPm.mProcessLoggingHandler.invalidateBaseApkHash(request.getPkg().getBaseApkPath());
// Send added for users that see the package for the first time
// sendPackageAddedForNewUsers also deals with system apps
- int appId = UserHandle.getAppId(res.mUid);
- boolean isSystem = res.mPkg.isSystem();
+ int appId = UserHandle.getAppId(request.getUid());
+ boolean isSystem = request.getPkg().isSystem();
mPm.sendPackageAddedForNewUsers(mPm.snapshotComputer(), 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();
- extras.putInt(Intent.EXTRA_UID, res.mUid);
+ extras.putInt(Intent.EXTRA_UID, request.getUid());
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
@@ -2743,8 +2747,8 @@ final class InstallPackageHelper {
mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
packageName, extras, 0 /*flags*/,
null /*targetPackage*/, null /*finishedReceiver*/,
- updateUserIds, instantUserIds, res.mRemovedInfo.mBroadcastAllowList,
- null);
+ updateUserIds, instantUserIds,
+ request.getRemovedInfo().mBroadcastAllowList, null);
if (installerPackageName != null) {
mPm.sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,
extras, 0 /*flags*/,
@@ -2768,7 +2772,7 @@ final class InstallPackageHelper {
null /*broadcastAllowList*/,
mBroadcastHelper.getTemporaryAppAllowlistBroadcastOptions(
REASON_PACKAGE_REPLACED).toBundle());
- } else if (launchedForRestore && !res.mPkg.isSystem()) {
+ } else if (launchedForRestore && !request.getPkg().isSystem()) {
// First-install and we did a restore, so we're responsible for the
// first-launch broadcast.
if (DEBUG_BACKUP) {
@@ -2780,17 +2784,17 @@ final class InstallPackageHelper {
}
// Send broadcast package appeared if external for all users
- if (res.mPkg.isExternalStorage()) {
+ if (request.getPkg().isExternalStorage()) {
if (!update) {
final StorageManager storageManager =
mInjector.getSystemService(StorageManager.class);
VolumeInfo volume =
storageManager.findVolumeByUuid(
StorageManager.convert(
- res.mPkg.getVolumeUuid()).toString());
+ request.getPkg().getVolumeUuid()).toString());
int packageExternalStorageType =
PackageManagerServiceUtils.getPackageExternalStorageType(volume,
- res.mPkg.isExternalStorage());
+ request.getPkg().isExternalStorage());
// If the package was installed externally, log it.
if (packageExternalStorageType != StorageEnums.UNKNOWN) {
FrameworkStatsLog.write(
@@ -2799,19 +2803,20 @@ final class InstallPackageHelper {
}
}
if (DEBUG_INSTALL) {
- Slog.i(TAG, "upgrading pkg " + res.mPkg + " is external");
+ Slog.i(TAG, "upgrading pkg " + request.getPkg() + " is external");
}
final String[] pkgNames = new String[]{packageName};
- final int[] uids = new int[]{res.mPkg.getUid()};
+ final int[] uids = new int[]{request.getPkg().getUid()};
mBroadcastHelper.sendResourcesChangedBroadcast(mPm::snapshotComputer,
true /* mediaStatus */, true /* replacing */, pkgNames, uids);
}
- } else if (!ArrayUtils.isEmpty(res.mLibraryConsumers)) { // if static shared lib
+ } else if (!ArrayUtils.isEmpty(request.getLibraryConsumers())) { // if static shared lib
// No need to kill consumers if it's installation of new version static shared lib.
final Computer snapshot = mPm.snapshotComputer();
- final boolean dontKillApp = !update && res.mPkg.getStaticSharedLibName() != null;
- for (int i = 0; i < res.mLibraryConsumers.size(); i++) {
- AndroidPackage pkg = res.mLibraryConsumers.get(i);
+ final boolean dontKillApp = !update
+ && request.getPkg().getStaticSharedLibraryName() != null;
+ for (int i = 0; i < request.getLibraryConsumers().size(); i++) {
+ AndroidPackage pkg = request.getLibraryConsumers().get(i);
// send broadcast that all consumers of the static shared library have changed
mPm.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(), dontKillApp,
new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
@@ -2828,9 +2833,9 @@ final class InstallPackageHelper {
}
if (allNewUsers && !update) {
- mPm.notifyPackageAdded(packageName, res.mUid);
+ mPm.notifyPackageAdded(packageName, request.getUid());
} else {
- mPm.notifyPackageChanged(packageName, res.mUid);
+ mPm.notifyPackageChanged(packageName, request.getUid());
}
// Log current value of "unknown sources" setting
@@ -2838,7 +2843,8 @@ final class InstallPackageHelper {
getUnknownSourcesSettings());
// Remove the replaced package's older resources safely now
- InstallArgs args = res.mRemovedInfo != null ? res.mRemovedInfo.mArgs : null;
+ InstallArgs args = request.getRemovedInfo() != null
+ ? request.getRemovedInfo().mArgs : null;
if (args != null) {
if (!killApp) {
// If we didn't kill the app, defer the deletion of code/resource files, since
@@ -2847,7 +2853,7 @@ final class InstallPackageHelper {
// ApplicationInfo changes have propagated to all application threads.
mPm.scheduleDeferredNoKillPostDelete(args);
} else {
- mRemovePackageHelper.cleanUpResources(args);
+ mRemovePackageHelper.cleanUpResources(args.mCodeFile, args.mInstructionSets);
}
} else {
// Force a gc to clear up things. Ask for a background one, it's fine to go on
@@ -2874,21 +2880,21 @@ final class InstallPackageHelper {
final boolean deferInstallObserver = succeeded && update;
if (deferInstallObserver) {
if (killApp) {
- mPm.scheduleDeferredPendingKillInstallObserver(res, installObserver);
+ mPm.scheduleDeferredPendingKillInstallObserver(request);
} else {
- mPm.scheduleDeferredNoKillInstallObserver(res, installObserver);
+ mPm.scheduleDeferredNoKillInstallObserver(request);
}
} else {
- mPm.notifyInstallObserver(res, installObserver);
+ mPm.notifyInstallObserver(request);
}
// Prune unused static shared libraries which have been cached a period of time
mPm.schedulePruneUnusedStaticSharedLibraries(true /* delay */);
// Log tracing if needed
- if (installArgs.mTraceMethod != null) {
- Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, installArgs.mTraceMethod,
- installArgs.mTraceCookie);
+ if (request.getTraceMethod() != null) {
+ Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, request.getTraceMethod(),
+ request.getTraceCookie());
}
}
@@ -3446,8 +3452,8 @@ final class InstallPackageHelper {
if (throwable == null) {
try {
- AndroidPackage pkg = addForInitLI(
- parseResult.parsedPackage, newParseFlags, newScanFlags, null);
+ addForInitLI(parseResult.parsedPackage, newParseFlags, newScanFlags, null);
+ AndroidPackage pkg = parseResult.parsedPackage.hideAsFinal();
if (ai.isFactory && !ai.isActive) {
disableSystemPackageLPw(pkg);
}
@@ -3467,7 +3473,7 @@ final class InstallPackageHelper {
}
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags,
+ public void installPackagesFromDir(File scanDir, int parseFlags,
int scanFlags, PackageParser2 packageParser,
ExecutorService executorService) {
final File[] files = scanDir.listFiles();
@@ -3481,7 +3487,7 @@ final class InstallPackageHelper {
+ " flags=0x" + Integer.toHexString(parseFlags));
}
ParallelPackageParser parallelPackageParser =
- new ParallelPackageParser(packageParser, executorService, frameworkSplits);
+ new ParallelPackageParser(packageParser, executorService);
// Submit files for parsing in parallel
int fileCount = 0;
@@ -3936,10 +3942,10 @@ final class InstallPackageHelper {
+ "; " + pkgSetting.getPathString()
+ " --> " + parsedPackage.getPath());
- final InstallArgs args = new InstallArgs(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()));
- mRemovePackageHelper.cleanUpResources(args);
+ mRemovePackageHelper.cleanUpResources(
+ new File(pkgSetting.getPathString()),
+ getAppDexInstructionSets(pkgSetting.getPrimaryCpuAbi(),
+ pkgSetting.getSecondaryCpuAbi()));
synchronized (mPm.mLock) {
mPm.mSettings.enableSystemPackageLPw(pkgSetting.getPackageName());
}
@@ -4021,10 +4027,9 @@ final class InstallPackageHelper {
+ parsedPackage.getLongVersionCode()
+ "; " + pkgSetting.getPathString() + " --> "
+ parsedPackage.getPath());
- InstallArgs args = new InstallArgs(
- pkgSetting.getPathString(), getAppDexInstructionSets(
- pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()));
- mRemovePackageHelper.cleanUpResources(args);
+ mRemovePackageHelper.cleanUpResources(new File(pkgSetting.getPathString()),
+ getAppDexInstructionSets(
+ pkgSetting.getPrimaryCpuAbi(), pkgSetting.getSecondaryCpuAbi()));
} else {
// The application on /system is older than the application on /data. Hide
// the application on /system and the version on /data will be scanned later
@@ -4293,7 +4298,7 @@ final class InstallPackageHelper {
long maxVersionCode = Long.MAX_VALUE;
WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
- mSharedLibraries.getSharedLibraryInfos(pkg.getStaticSharedLibName());
+ mSharedLibraries.getSharedLibraryInfos(pkg.getStaticSharedLibraryName());
if (versionedLib != null) {
final int versionCount = versionedLib.size();
for (int i = 0; i < versionCount; i++) {
diff --git a/services/core/java/com/android/server/pm/InstallRequest.java b/services/core/java/com/android/server/pm/InstallRequest.java
index 753d012a32e3..36bbf41bffe6 100644
--- a/services/core/java/com/android/server/pm/InstallRequest.java
+++ b/services/core/java/com/android/server/pm/InstallRequest.java
@@ -16,12 +16,389 @@
package com.android.server.pm;
+import static android.content.pm.PackageManager.INSTALL_REASON_UNKNOWN;
+import static android.content.pm.PackageManager.INSTALL_SCENARIO_DEFAULT;
+
+import static com.android.server.pm.PackageManagerService.TAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.apex.ApexInfo;
+import android.app.AppOpsManager;
+import android.content.pm.DataLoaderType;
+import android.content.pm.IPackageInstallObserver2;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.SigningDetails;
+import android.net.Uri;
+import android.os.UserHandle;
+import android.util.ExceptionUtils;
+import android.util.Slog;
+
+import com.android.server.pm.pkg.AndroidPackage;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
final class InstallRequest {
- public final InstallArgs mArgs;
- public final PackageInstalledInfo mInstallResult;
+ private final int mUserId;
+ @Nullable
+ private final InstallArgs mInstallArgs;
+ @NonNull
+ private final PackageInstalledInfo mInstalledInfo;
+ @Nullable
+ private Runnable mPostInstallRunnable;
+ @Nullable
+ private PackageRemovedInfo mRemovedInfo;
+
+ // New install
+ InstallRequest(InstallingSession params) {
+ mUserId = params.getUser().getIdentifier();
+ mInstallArgs = new InstallArgs(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.mPackageSource);
+ mInstalledInfo = new PackageInstalledInfo();
+ }
+
+ // Install existing package as user
+ InstallRequest(int userId, int returnCode, AndroidPackage pkg, int[] newUsers,
+ Runnable runnable) {
+ mUserId = userId;
+ mInstallArgs = null;
+ mInstalledInfo = new PackageInstalledInfo();
+ mInstalledInfo.mReturnCode = returnCode;
+ mInstalledInfo.mPkg = pkg;
+ mInstalledInfo.mNewUsers = newUsers;
+ mPostInstallRunnable = runnable;
+ }
+
+ private static class PackageInstalledInfo {
+ String mName;
+ int mUid = -1;
+ // 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;
+ // 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;
+ // The ApexInfo returned by ApexManager#installPackage, used by rebootless APEX install
+ ApexInfo mApexInfo;
+ }
+
+ public String getName() {
+ return mInstalledInfo.mName;
+ }
+
+ public String getReturnMsg() {
+ return mInstalledInfo.mReturnMsg;
+ }
+
+ public OriginInfo getOriginInfo() {
+ return mInstallArgs == null ? null : mInstallArgs.mOriginInfo;
+ }
+
+ public PackageRemovedInfo getRemovedInfo() {
+ return mRemovedInfo;
+ }
+
+ public String getOrigPackage() {
+ return mInstalledInfo.mOrigPackage;
+ }
+
+ public String getOrigPermission() {
+ return mInstalledInfo.mOrigPermission;
+ }
+
+ @Nullable
+ public File getCodeFile() {
+ return mInstallArgs == null ? null : mInstallArgs.mCodeFile;
+ }
+
+ @Nullable
+ public String getCodePath() {
+ return (mInstallArgs != null && mInstallArgs.mCodeFile != null)
+ ? mInstallArgs.mCodeFile.getAbsolutePath() : null;
+ }
+
+ @Nullable
+ public String getAbiOverride() {
+ return mInstallArgs == null ? null : mInstallArgs.mAbiOverride;
+ }
+
+ public int getReturnCode() {
+ return mInstalledInfo.mReturnCode;
+ }
+
+ @Nullable
+ public IPackageInstallObserver2 getObserver() {
+ return mInstallArgs == null ? null : mInstallArgs.mObserver;
+ }
+
+ public boolean isMoveInstall() {
+ return mInstallArgs != null && mInstallArgs.mMoveInfo != null;
+ }
+
+ @Nullable
+ public String getMoveToUuid() {
+ return (mInstallArgs != null && mInstallArgs.mMoveInfo != null)
+ ? mInstallArgs.mMoveInfo.mToUuid : null;
+ }
+
+ @Nullable
+ public String getMovePackageName() {
+ return (mInstallArgs != null && mInstallArgs.mMoveInfo != null)
+ ? mInstallArgs.mMoveInfo.mPackageName : null;
+ }
+
+ @Nullable
+ public String getMoveFromCodePath() {
+ return (mInstallArgs != null && mInstallArgs.mMoveInfo != null)
+ ? mInstallArgs.mMoveInfo.mFromCodePath : null;
+ }
+
+ @Nullable
+ public File getOldCodeFile() {
+ return (mRemovedInfo != null && mRemovedInfo.mArgs != null)
+ ? mRemovedInfo.mArgs.mCodeFile : null;
+ }
+
+ @Nullable
+ public String[] getOldInstructionSet() {
+ return (mRemovedInfo != null && mRemovedInfo.mArgs != null)
+ ? mRemovedInfo.mArgs.mInstructionSets : null;
+ }
+
+ public UserHandle getUser() {
+ return new UserHandle(mUserId);
+ }
+
+ public int getUserId() {
+ return mUserId;
+ }
+
+ public int getInstallFlags() {
+ return mInstallArgs == null ? 0 : mInstallArgs.mInstallFlags;
+ }
+
+ public int getInstallReason() {
+ return mInstallArgs == null ? INSTALL_REASON_UNKNOWN : mInstallArgs.mInstallReason;
+ }
+
+ @Nullable
+ public String getVolumeUuid() {
+ return mInstallArgs == null ? null : mInstallArgs.mVolumeUuid;
+ }
+
+ public AndroidPackage getPkg() {
+ return mInstalledInfo.mPkg;
+ }
+
+ @Nullable
+ public String getTraceMethod() {
+ return mInstallArgs == null ? null : mInstallArgs.mTraceMethod;
+ }
+
+ public int getTraceCookie() {
+ return mInstallArgs == null ? 0 : mInstallArgs.mTraceCookie;
+ }
+
+ public boolean isUpdate() {
+ return mRemovedInfo != null && mRemovedInfo.mRemovedPackage != null;
+ }
+
+ @Nullable
+ public String getRemovedPackage() {
+ return mRemovedInfo != null ? mRemovedInfo.mRemovedPackage : null;
+ }
+
+ public boolean isInstallForExistingUser() {
+ return mInstallArgs == null;
+ }
+
+ @Nullable
+ public InstallSource getInstallSource() {
+ return mInstallArgs == null ? null : mInstallArgs.mInstallSource;
+ }
+
+ @Nullable
+ public String getInstallerPackageName() {
+ return (mInstallArgs != null && mInstallArgs.mInstallSource != null)
+ ? mInstallArgs.mInstallSource.installerPackageName : null;
+ }
+
+ public int getDataLoaderType() {
+ return mInstallArgs == null ? DataLoaderType.NONE : mInstallArgs.mDataLoaderType;
+ }
+
+ public int getSignatureSchemeVersion() {
+ return mInstallArgs == null ? SigningDetails.SignatureSchemeVersion.UNKNOWN
+ : mInstallArgs.mSigningDetails.getSignatureSchemeVersion();
+ }
+
+ @NonNull
+ public SigningDetails getSigningDetails() {
+ return mInstallArgs == null ? SigningDetails.UNKNOWN : mInstallArgs.mSigningDetails;
+ }
+
+ @Nullable
+ public Uri getOriginUri() {
+ return mInstallArgs == null ? null : Uri.fromFile(mInstallArgs.mOriginInfo.mResolvedFile);
+ }
+
+ public ApexInfo getApexInfo() {
+ return mInstalledInfo.mApexInfo;
+ }
+
+ public String getSourceInstallerPackageName() {
+ return mInstallArgs.mInstallSource.installerPackageName;
+ }
+
+ public boolean isRollback() {
+ return mInstallArgs != null
+ && mInstallArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ }
+
+ public int[] getNewUsers() {
+ return mInstalledInfo.mNewUsers;
+ }
+
+ public int[] getOriginUsers() {
+ return mInstalledInfo.mOrigUsers;
+ }
+
+ public int getUid() {
+ return mInstalledInfo.mUid;
+ }
+
+ @Nullable
+ public String[] getInstallGrantPermissions() {
+ return mInstallArgs == null ? null : mInstallArgs.mInstallGrantPermissions;
+ }
+
+ public ArrayList<AndroidPackage> getLibraryConsumers() {
+ return mInstalledInfo.mLibraryConsumers;
+ }
+
+ @Nullable
+ public List<String> getAllowlistedRestrictedPermissions() {
+ return mInstallArgs == null ? null : mInstallArgs.mAllowlistedRestrictedPermissions;
+ }
+
+ public int getAutoRevokePermissionsMode() {
+ return mInstallArgs == null
+ ? AppOpsManager.MODE_DEFAULT : mInstallArgs.mAutoRevokePermissionsMode;
+ }
+
+ public int getPackageSource() {
+ return mInstallArgs == null
+ ? PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED : mInstallArgs.mPackageSource;
+ }
+
+ public int getInstallScenario() {
+ return mInstallArgs == null ? INSTALL_SCENARIO_DEFAULT : mInstallArgs.mInstallScenario;
+ }
+
+ public boolean isForceQueryableOverride() {
+ return mInstallArgs != null && mInstallArgs.mForceQueryableOverride;
+ }
+
+ public void closeFreezer() {
+ if (mInstalledInfo.mFreezer != null) {
+ mInstalledInfo.mFreezer.close();
+ }
+ }
+
+ public void runPostInstallRunnable() {
+ if (mPostInstallRunnable != null) {
+ mPostInstallRunnable.run();
+ }
+ }
+
+ public void setCodeFile(File codeFile) {
+ if (mInstallArgs != null) {
+ mInstallArgs.mCodeFile = codeFile;
+ }
+ }
+
+ public void setError(int code, String msg) {
+ setReturnCode(code);
+ setReturnMessage(msg);
+ Slog.w(TAG, msg);
+ }
+
+ public void setError(String msg, PackageManagerException e) {
+ mInstalledInfo.mReturnCode = e.error;
+ setReturnMessage(ExceptionUtils.getCompleteMessage(msg, e));
+ Slog.w(TAG, msg, e);
+ }
+
+ public void setReturnCode(int returnCode) {
+ mInstalledInfo.mReturnCode = returnCode;
+ }
+
+ public void setReturnMessage(String returnMsg) {
+ mInstalledInfo.mReturnMsg = returnMsg;
+ }
+
+ public void setApexInfo(ApexInfo apexInfo) {
+ mInstalledInfo.mApexInfo = apexInfo;
+ }
+
+ public void setPkg(AndroidPackage pkg) {
+ mInstalledInfo.mPkg = pkg;
+ }
+
+ public void setUid(int uid) {
+ mInstalledInfo.mUid = uid;
+ }
+
+ public void setNewUsers(int[] newUsers) {
+ mInstalledInfo.mNewUsers = newUsers;
+ }
+
+ public void setOriginPackage(String originPackage) {
+ mInstalledInfo.mOrigPackage = originPackage;
+ }
+
+ public void setOriginPermission(String originPermission) {
+ mInstalledInfo.mOrigPermission = originPermission;
+ }
+
+ public void setInstallerPackageName(String installerPackageName) {
+ mInstalledInfo.mInstallerPackageName = installerPackageName;
+ }
+
+ public void setName(String packageName) {
+ mInstalledInfo.mName = packageName;
+ }
+
+ public void setOriginUsers(int[] userIds) {
+ mInstalledInfo.mOrigUsers = userIds;
+ }
+
+ public void setFreezer(PackageFreezer freezer) {
+ mInstalledInfo.mFreezer = freezer;
+ }
+
+ public void setRemovedInfo(PackageRemovedInfo removedInfo) {
+ mRemovedInfo = removedInfo;
+ }
- InstallRequest(InstallArgs args, PackageInstalledInfo res) {
- mArgs = args;
- mInstallResult = res;
+ public void setLibraryConsumers(ArrayList<AndroidPackage> libraryConsumers) {
+ mInstalledInfo.mLibraryConsumers = libraryConsumers;
}
}
diff --git a/services/core/java/com/android/server/pm/InstallSource.java b/services/core/java/com/android/server/pm/InstallSource.java
index 404285ca5b4d..e5f7f7166186 100644
--- a/services/core/java/com/android/server/pm/InstallSource.java
+++ b/services/core/java/com/android/server/pm/InstallSource.java
@@ -131,7 +131,8 @@ public final class InstallSource {
@Nullable PackageSignatures initiatingPackageSignatures) {
if (initiatingPackageName == null && originatingPackageName == null
&& installerPackageName == null && initiatingPackageSignatures == null
- && !isInitiatingPackageUninstalled) {
+ && !isInitiatingPackageUninstalled
+ && packageSource == PackageInstaller.PACKAGE_SOURCE_UNSPECIFIED) {
return isOrphaned ? EMPTY_ORPHANED : EMPTY;
}
return new InstallSource(initiatingPackageName, originatingPackageName,
diff --git a/services/core/java/com/android/server/pm/InstallingSession.java b/services/core/java/com/android/server/pm/InstallingSession.java
index a7f1727dfa61..8d5a5e156e71 100644
--- a/services/core/java/com/android/server/pm/InstallingSession.java
+++ b/services/core/java/com/android/server/pm/InstallingSession.java
@@ -253,68 +253,69 @@ class InstallingSession {
}
private void processPendingInstall() {
- InstallArgs args = new InstallArgs(this);
+ InstallRequest installRequest = new InstallRequest(this);
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
- mRet = copyApk(args);
+ mRet = copyApk(installRequest);
}
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
F2fsUtils.releaseCompressedBlocks(
- mPm.mContext.getContentResolver(), new File(args.getCodePath()));
+ mPm.mContext.getContentResolver(), new File(installRequest.getCodePath()));
}
+ installRequest.setReturnCode(mRet);
if (mParentInstallingSession != null) {
- mParentInstallingSession.tryProcessInstallRequest(args, mRet);
+ mParentInstallingSession.tryProcessInstallRequest(installRequest);
} else {
- PackageInstalledInfo res = new PackageInstalledInfo(mRet);
// Queue up an async operation since the package installation may take a little while.
mPm.mHandler.post(() -> processInstallRequests(
- res.mReturnCode == PackageManager.INSTALL_SUCCEEDED /* success */,
- Collections.singletonList(new InstallRequest(args, res))));
+ mRet == PackageManager.INSTALL_SUCCEEDED /* success */,
+ Collections.singletonList(installRequest)));
}
}
- private int copyApk(InstallArgs args) {
+ private int copyApk(InstallRequest request) {
if (mMoveInfo == null) {
- return copyApkForFileInstall(args);
+ return copyApkForFileInstall(request);
} else {
- return copyApkForMoveInstall(args);
+ return copyApkForMoveInstall(request);
}
}
- private int copyApkForFileInstall(InstallArgs args) {
+ private int copyApkForFileInstall(InstallRequest request) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
if (mOriginInfo.mStaged) {
if (DEBUG_INSTALL) {
Slog.d(TAG, mOriginInfo.mFile + " already staged; skipping copy");
}
- args.mCodeFile = mOriginInfo.mFile;
+ request.setCodeFile(mOriginInfo.mFile);
return PackageManager.INSTALL_SUCCEEDED;
}
try {
final boolean isEphemeral =
(mInstallFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
- args.mCodeFile =
- mPm.mInstallerService.allocateStageDirLegacy(mVolumeUuid, isEphemeral);
+ request.setCodeFile(
+ mPm.mInstallerService.allocateStageDirLegacy(mVolumeUuid, isEphemeral));
} 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(), args.mCodeFile);
+ mOriginInfo.mFile.getAbsolutePath(), request.getCodeFile());
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
- final boolean isIncremental = isIncrementalPath(args.mCodeFile.getAbsolutePath());
- final File libraryRoot = new File(args.mCodeFile, LIB_DIR_NAME);
+ final boolean isIncremental = isIncrementalPath(
+ request.getCodeFile().getAbsolutePath());
+ final File libraryRoot = new File(request.getCodeFile(), LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
- handle = NativeLibraryHelper.Handle.create(args.mCodeFile);
+ handle = NativeLibraryHelper.Handle.create(request.getCodeFile());
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
- args.mAbiOverride, isIncremental);
+ request.getAbiOverride(), isIncremental);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
@@ -328,7 +329,7 @@ class InstallingSession {
}
}
- private int copyApkForMoveInstall(InstallArgs args) {
+ private int copyApkForMoveInstall(InstallRequest request) {
if (DEBUG_INSTALL) {
Slog.d(TAG, "Moving " + mMoveInfo.mPackageName + " from "
+ mMoveInfo.mFromUuid + " to " + mMoveInfo.mToUuid);
@@ -345,8 +346,9 @@ class InstallingSession {
}
final String toPathName = new File(mMoveInfo.mFromCodePath).getName();
- args.mCodeFile = new File(Environment.getDataAppDirectory(mMoveInfo.mToUuid), toPathName);
- if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + args.mCodeFile);
+ request.setCodeFile(
+ new File(Environment.getDataAppDirectory(mMoveInfo.mToUuid), toPathName));
+ if (DEBUG_INSTALL) Slog.d(TAG, "codeFile after move is " + request.getCodeFile());
return PackageManager.INSTALL_SUCCEEDED;
}
@@ -460,7 +462,7 @@ class InstallingSession {
List<InstallRequest> apexInstallRequests = new ArrayList<>();
List<InstallRequest> apkInstallRequests = new ArrayList<>();
for (InstallRequest request : installRequests) {
- if ((request.mArgs.mInstallFlags & PackageManager.INSTALL_APEX) != 0) {
+ if ((request.getInstallFlags() & PackageManager.INSTALL_APEX) != 0) {
apexInstallRequests.add(request);
} else {
apkInstallRequests.add(request);
@@ -485,7 +487,7 @@ class InstallingSession {
// processInstallRequestAsync. In that case just notify the observer about the
// failure.
InstallRequest request = apexInstallRequests.get(0);
- mPm.notifyInstallObserver(request.mInstallResult, request.mArgs.mObserver);
+ mPm.notifyInstallObserver(request);
}
return;
}
@@ -496,28 +498,25 @@ class InstallingSession {
private void processApkInstallRequests(boolean success, List<InstallRequest> installRequests) {
if (success) {
for (InstallRequest request : installRequests) {
- if (request.mInstallResult.mReturnCode != PackageManager.INSTALL_SUCCEEDED) {
- cleanUpForFailedInstall(request.mArgs);
+ if (request.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
+ cleanUpForFailedInstall(request);
}
}
mInstallPackageHelper.installPackagesTraced(installRequests);
for (InstallRequest request : installRequests) {
- doPostInstall(request.mInstallResult.mReturnCode, request.mArgs);
+ doPostInstall(request);
}
}
for (InstallRequest request : installRequests) {
- mInstallPackageHelper.restoreAndPostInstall(request.mArgs.mUser.getIdentifier(),
- request.mInstallResult,
- new PostInstallData(request.mArgs,
- request.mInstallResult, null));
+ mInstallPackageHelper.restoreAndPostInstall(request);
}
}
- private void doPostInstall(int status, InstallArgs args) {
+ private void doPostInstall(InstallRequest request) {
if (mMoveInfo != null) {
- if (status == PackageManager.INSTALL_SUCCEEDED) {
+ if (request.getReturnCode() == PackageManager.INSTALL_SUCCEEDED) {
mRemovePackageHelper.cleanUpForMoveInstall(mMoveInfo.mFromUuid,
mMoveInfo.mPackageName, mMoveInfo.mFromCodePath);
} else {
@@ -525,18 +524,18 @@ class InstallingSession {
mMoveInfo.mPackageName, mMoveInfo.mFromCodePath);
}
} else {
- if (status != PackageManager.INSTALL_SUCCEEDED) {
- mRemovePackageHelper.removeCodePath(args.mCodeFile);
+ if (request.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
+ mRemovePackageHelper.removeCodePath(request.getCodeFile());
}
}
}
- private void cleanUpForFailedInstall(InstallArgs args) {
- if (args.mMoveInfo != null) {
- mRemovePackageHelper.cleanUpForMoveInstall(args.mMoveInfo.mToUuid,
- args.mMoveInfo.mPackageName, args.mMoveInfo.mFromCodePath);
+ private void cleanUpForFailedInstall(InstallRequest request) {
+ if (request.isMoveInstall()) {
+ mRemovePackageHelper.cleanUpForMoveInstall(request.getMoveToUuid(),
+ request.getMovePackageName(), request.getMoveFromCodePath());
} else {
- mRemovePackageHelper.removeCodePath(args.mCodeFile);
+ mRemovePackageHelper.removeCodePath(request.getCodeFile());
}
}
@@ -560,7 +559,7 @@ class InstallingSession {
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 dir = request.getOriginInfo().mResolvedFile;
final File[] apexes = dir.listFiles();
if (apexes == null) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
@@ -580,7 +579,7 @@ class InstallingSession {
// The newly installed APEX will not be reverted even if
// processApkInstallRequests() fails. Need a way to keep info stored in apexd
// and PMS in sync in the face of install failures.
- request.mInstallResult.mApexInfo = apexInfo;
+ request.setApexInfo(apexInfo);
mPm.mHandler.post(() -> processApkInstallRequests(true, requests));
return;
} else {
@@ -588,10 +587,10 @@ class InstallingSession {
}
}
} catch (PackageManagerException e) {
- request.mInstallResult.setError("APEX installation failed", e);
+ request.setError("APEX installation failed", e);
}
PackageManagerService.invalidatePackageInfoCache();
- mPm.notifyInstallObserver(request.mInstallResult, request.mArgs.mObserver);
+ mPm.notifyInstallObserver(request);
}
/**
@@ -600,7 +599,7 @@ class InstallingSession {
*/
private class MultiPackageInstallingSession {
private final List<InstallingSession> mChildInstallingSessions;
- private final Map<InstallArgs, Integer> mCurrentState;
+ private final Map<InstallRequest, Integer> mCurrentState;
@NonNull
final PackageManagerService mPm;
final UserHandle mUser;
@@ -636,8 +635,8 @@ class InstallingSession {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- public void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
- mCurrentState.put(args, currentStatus);
+ public void tryProcessInstallRequest(InstallRequest request) {
+ mCurrentState.put(request, request.getReturnCode());
if (mCurrentState.size() != mChildInstallingSessions.size()) {
return;
}
@@ -651,9 +650,9 @@ class InstallingSession {
}
}
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)));
+ for (Map.Entry<InstallRequest, Integer> entry : mCurrentState.entrySet()) {
+ entry.getKey().setReturnCode(completeStatus);
+ installRequests.add(entry.getKey());
}
int finalCompleteStatus = completeStatus;
mPm.mHandler.post(() -> processInstallRequests(
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 3fb4066c965a..178d0ea594b1 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -189,6 +189,11 @@ public class PackageDexOptimizer {
return false;
}
+ // We do not dexopt APEX packages.
+ if (pkg.isApex()) {
+ return false;
+ }
+
// We do not dexopt unused packages.
// It's possible for this to be called before app hibernation service is ready due to
// an OTA dexopt. In this case, we ignore the hibernation check here. This is fine since
diff --git a/services/core/java/com/android/server/pm/PackageHandler.java b/services/core/java/com/android/server/pm/PackageHandler.java
index 1ea4d629eef2..4e8b0a14ae1d 100644
--- a/services/core/java/com/android/server/pm/PackageHandler.java
+++ b/services/core/java/com/android/server/pm/PackageHandler.java
@@ -89,18 +89,14 @@ final class PackageHandler extends Handler {
case POST_INSTALL: {
if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
- PostInstallData data = mPm.mRunningInstalls.get(msg.arg1);
+ InstallRequest request = mPm.mRunningInstalls.get(msg.arg1);
final boolean didRestore = (msg.arg2 != 0);
mPm.mRunningInstalls.delete(msg.arg1);
- if (data != null && data.res.mFreezer != null) {
- data.res.mFreezer.close();
- }
-
- if (data != null && data.mPostInstallRunnable != null) {
- data.mPostInstallRunnable.run();
- } else if (data != null && data.args != null) {
- mInstallPackageHelper.handlePackagePostInstall(data.res, data.args, didRestore);
+ request.closeFreezer();
+ request.runPostInstallRunnable();
+ if (!request.isInstallForExistingUser()) {
+ mInstallPackageHelper.handlePackagePostInstall(request, didRestore);
} else if (DEBUG_INSTALL) {
// No post-install when we run restore from installExistingPackageForUser
Slog.i(TAG, "Nothing to do for post-install token " + msg.arg1);
@@ -111,7 +107,7 @@ final class PackageHandler extends Handler {
case DEFERRED_NO_KILL_POST_DELETE: {
InstallArgs args = (InstallArgs) msg.obj;
if (args != null) {
- mRemovePackageHelper.cleanUpResources(args);
+ mRemovePackageHelper.cleanUpResources(args.mCodeFile, args.mInstructionSets);
}
} break;
case DEFERRED_NO_KILL_INSTALL_OBSERVER:
diff --git a/services/core/java/com/android/server/pm/PackageInstalledInfo.java b/services/core/java/com/android/server/pm/PackageInstalledInfo.java
deleted file mode 100644
index 247abf3b6685..000000000000
--- a/services/core/java/com/android/server/pm/PackageInstalledInfo.java
+++ /dev/null
@@ -1,78 +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.pm;
-
-import static com.android.server.pm.PackageManagerService.TAG;
-
-import android.apex.ApexInfo;
-import android.util.ExceptionUtils;
-import android.util.Slog;
-
-import com.android.server.pm.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;
-
- // The ApexInfo returned by ApexManager#installPackage, used by rebootless APEX install
- ApexInfo mApexInfo;
-
- 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 66a97b3b9a89..218d9d1fd085 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1344,9 +1344,14 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements
}
private String getDeviceOwnerDeletedPackageMsg() {
- DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
- return dpm.getResources().getString(PACKAGE_DELETED_BY_DO,
- () -> mContext.getString(R.string.package_deleted_device_owner));
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
+ return dpm.getResources().getString(PACKAGE_DELETED_BY_DO,
+ () -> mContext.getString(R.string.package_deleted_device_owner));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
index 34d6d292dd19..65e7ce1466dd 100644
--- a/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
+++ b/services/core/java/com/android/server/pm/PackageManagerInternalBase.java
@@ -465,6 +465,20 @@ abstract class PackageManagerInternalBase extends PackageManagerInternal {
filterCallingUid);
}
+ /**
+ * @deprecated similar to {@link resolveIntent} but limits the matches to exported components.
+ */
+ @Override
+ @Deprecated
+ public final ResolveInfo resolveIntentExported(Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags,
+ @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
+ boolean resolveForStart, int filterCallingUid) {
+ return getResolveIntentHelper().resolveIntentInternal(snapshot(),
+ intent, resolvedType, flags, privateResolveFlags, userId, resolveForStart,
+ filterCallingUid, true);
+ }
+
@Override
@Deprecated
public final ResolveInfo resolveService(Intent intent, String resolvedType,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 259202f86bc9..2c460f858d36 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -82,7 +82,6 @@ import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver2;
-import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageMoveObserver;
@@ -826,10 +825,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService
@Watched(manual = true)
private final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();
- private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
+ private final Map<String, InstallRequest>
mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
- private final Map<String, Pair<PackageInstalledInfo, IPackageInstallObserver2>>
+ private final Map<String, InstallRequest>
mPendingKillInstallObservers = Collections.synchronizedMap(new HashMap<>());
// Internal interface for permission manager
@@ -908,7 +907,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
// Stores a list of users whose package restrictions file needs to be updated
final ArraySet<Integer> mDirtyUsers = new ArraySet<>();
- final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<>();
+ final SparseArray<InstallRequest> mRunningInstalls = new SparseArray<>();
int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
final @NonNull String[] mRequiredVerifierPackages;
@@ -1155,32 +1154,30 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
void notifyInstallObserver(String packageName, boolean killApp) {
- final Pair<PackageInstalledInfo, IPackageInstallObserver2> pair =
+ final InstallRequest installRequest =
killApp ? mPendingKillInstallObservers.remove(packageName)
: mNoKillInstallObservers.remove(packageName);
- if (pair != null) {
- notifyInstallObserver(pair.first, pair.second);
+ if (installRequest != null) {
+ notifyInstallObserver(installRequest);
}
}
- void notifyInstallObserver(PackageInstalledInfo info,
- IPackageInstallObserver2 installObserver) {
- if (installObserver != null) {
+ void notifyInstallObserver(InstallRequest request) {
+ if (request.getObserver() != null) {
try {
- Bundle extras = extrasForInstallResult(info);
- installObserver.onPackageInstalled(info.mName, info.mReturnCode,
- info.mReturnMsg, extras);
+ Bundle extras = extrasForInstallResult(request);
+ request.getObserver().onPackageInstalled(request.getName(),
+ request.getReturnCode(), request.getReturnMsg(), extras);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
}
- void scheduleDeferredNoKillInstallObserver(PackageInstalledInfo info,
- IPackageInstallObserver2 observer) {
- String packageName = info.mPkg.getPackageName();
- mNoKillInstallObservers.put(packageName, Pair.create(info, observer));
+ void scheduleDeferredNoKillInstallObserver(InstallRequest request) {
+ String packageName = request.getPkg().getPackageName();
+ mNoKillInstallObservers.put(packageName, request);
Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName);
mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
}
@@ -1196,10 +1193,9 @@ public class PackageManagerService implements PackageSender, TestUtilityService
delay ? getPruneUnusedSharedLibrariesDelay() : 0);
}
- void scheduleDeferredPendingKillInstallObserver(PackageInstalledInfo info,
- IPackageInstallObserver2 observer) {
- final String packageName = info.mPkg.getPackageName();
- mPendingKillInstallObservers.put(packageName, Pair.create(info, observer));
+ void scheduleDeferredPendingKillInstallObserver(InstallRequest request) {
+ final String packageName = request.getPkg().getPackageName();
+ mPendingKillInstallObservers.put(packageName, request);
final Message message = mHandler.obtainMessage(DEFERRED_PENDING_KILL_INSTALL_OBSERVER,
packageName);
mHandler.sendMessageDelayed(message, DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS);
@@ -1312,21 +1308,22 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
}
- private static Bundle extrasForInstallResult(PackageInstalledInfo res) {
+ private static Bundle extrasForInstallResult(InstallRequest request) {
Bundle extras = null;
- switch (res.mReturnCode) {
+ switch (request.getReturnCode()) {
case PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION: {
extras = new Bundle();
extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PERMISSION,
- res.mOrigPermission);
+ request.getOrigPermission());
extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE,
- res.mOrigPackage);
+ request.getOrigPackage());
break;
}
case PackageManager.INSTALL_SUCCEEDED: {
extras = new Bundle();
extras.putBoolean(Intent.EXTRA_REPLACING,
- res.mRemovedInfo != null && res.mRemovedInfo.mRemovedPackage != null);
+ request.getRemovedInfo() != null
+ && request.getRemovedInfo().mRemovedPackage != null);
break;
}
}
@@ -3111,14 +3108,14 @@ public class PackageManagerService implements PackageSender, TestUtilityService
// state for observers to see the FIRST_LAUNCH signal.
mHandler.post(() -> {
for (int i = 0; i < mRunningInstalls.size(); i++) {
- final PostInstallData data = mRunningInstalls.valueAt(i);
- if (data.res.mReturnCode != PackageManager.INSTALL_SUCCEEDED) {
+ final InstallRequest installRequest = mRunningInstalls.valueAt(i);
+ if (installRequest.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
continue;
}
- if (packageName.equals(data.res.mPkg.getPackageName())) {
+ if (packageName.equals(installRequest.getPkg().getPackageName())) {
// right package; but is it for the right user?
- for (int uIndex = 0; uIndex < data.res.mNewUsers.length; uIndex++) {
- if (userId == data.res.mNewUsers[uIndex]) {
+ for (int uIndex = 0; uIndex < installRequest.getNewUsers().length; uIndex++) {
+ if (userId == installRequest.getNewUsers()[uIndex]) {
if (DEBUG_BACKUP) {
Slog.i(TAG, "Package " + packageName
+ " being restored so deferring FIRST_LAUNCH");
@@ -5492,19 +5489,19 @@ public class PackageManagerService implements PackageSender, TestUtilityService
AndroidPackage pkg = packageState.getPkg();
if (pkg != null) {
// Cannot hide SDK libs as they are controlled by SDK manager.
- if (pkg.getSdkLibName() != null) {
+ if (pkg.getSdkLibraryName() != null) {
Slog.w(TAG, "Cannot hide package: " + packageName
+ " providing SDK library: "
- + pkg.getSdkLibName());
+ + pkg.getSdkLibraryName());
return false;
}
// Cannot hide static shared libs as they are considered
// a part of the using app (emulating static linking). Also
// static libs are installed always on internal storage.
- if (pkg.getStaticSharedLibName() != null) {
+ if (pkg.getStaticSharedLibraryName() != null) {
Slog.w(TAG, "Cannot hide package: " + packageName
+ " providing static shared library: "
- + pkg.getStaticSharedLibName());
+ + pkg.getStaticSharedLibraryName());
return false;
}
}
@@ -5547,17 +5544,17 @@ public class PackageManagerService implements PackageSender, TestUtilityService
if (packageState != null && packageState.getPkg() != null) {
AndroidPackage pkg = packageState.getPkg();
// Cannot block uninstall SDK libs as they are controlled by SDK manager.
- if (pkg.getSdkLibName() != null) {
+ if (pkg.getSdkLibraryName() != null) {
Slog.w(PackageManagerService.TAG, "Cannot block uninstall of package: " + packageName
- + " providing SDK library: " + pkg.getSdkLibName());
+ + " providing SDK library: " + pkg.getSdkLibraryName());
return false;
}
// Cannot block uninstall of static shared libs as they are
// considered a part of the using app (emulating static linking).
// Also static libs are installed always on internal storage.
- if (pkg.getStaticSharedLibName() != null) {
+ if (pkg.getStaticSharedLibraryName() != null) {
Slog.w(PackageManagerService.TAG, "Cannot block uninstall of package: " + packageName
- + " providing static shared library: " + pkg.getStaticSharedLibName());
+ + " providing static shared library: " + pkg.getStaticSharedLibraryName());
return false;
}
}
@@ -6542,7 +6539,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
if (dependentState == null) {
continue;
}
- if (!Objects.equals(dependentState.getUserStateOrDefault(userId)
+ if (canSetOverlayPaths(dependentState.getUserStateOrDefault(userId)
.getSharedLibraryOverlayPaths()
.get(libName), newOverlayPaths)) {
String dependentPackageName = dependent.getPackageName();
@@ -6566,7 +6563,10 @@ public class PackageManagerService implements PackageSender, TestUtilityService
}
}
- outUpdatedPackageNames.add(targetPackageName);
+ if (canSetOverlayPaths(packageState.getUserStateOrDefault(userId).getOverlayPaths(),
+ newOverlayPaths)) {
+ outUpdatedPackageNames.add(targetPackageName);
+ }
}
commitPackageStateMutation(null, mutator -> {
@@ -6617,6 +6617,17 @@ public class PackageManagerService implements PackageSender, TestUtilityService
invalidatePackageInfoCache();
}
+ private boolean canSetOverlayPaths(OverlayPaths origPaths, OverlayPaths newPaths) {
+ if (Objects.equals(origPaths, newPaths)) {
+ return false;
+ }
+ if ((origPaths == null && newPaths.isEmpty())
+ || (newPaths == null && origPaths.isEmpty())) {
+ return false;
+ }
+ return true;
+ }
+
private void maybeUpdateSystemOverlays(String targetPackageName, OverlayPaths newOverlayPaths) {
if (!mResolverReplaced) {
if (targetPackageName.equals("android")) {
diff --git a/services/core/java/com/android/server/pm/PackageRemovedInfo.java b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
index 28ad4b61d8c7..3c863d080d79 100644
--- a/services/core/java/com/android/server/pm/PackageRemovedInfo.java
+++ b/services/core/java/com/android/server/pm/PackageRemovedInfo.java
@@ -28,6 +28,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.os.PowerExemptionManager;
import android.util.SparseArray;
+import android.util.SparseIntArray;
import com.android.internal.util.ArrayUtils;
import com.android.server.LocalServices;
@@ -42,8 +43,8 @@ final class PackageRemovedInfo {
int[] mRemovedUsers = null;
int[] mBroadcastUsers = null;
int[] mInstantUserIds = null;
- SparseArray<Integer> mInstallReasons;
- SparseArray<Integer> mUninstallReasons;
+ SparseIntArray mInstallReasons;
+ SparseIntArray mUninstallReasons;
boolean mIsRemovedPackageSystemUpdate = false;
boolean mIsUpdate;
boolean mDataRemoved;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 4764a5c41db4..9050722ec561 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -42,15 +42,17 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import com.android.server.pm.parsing.pkg.AndroidPackageInternal;
-import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.pm.permission.LegacyPermissionDataProvider;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUnserialized;
+import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateImpl;
import com.android.server.pm.pkg.PackageUserStateInternal;
+import com.android.server.pm.pkg.SharedLibrary;
+import com.android.server.pm.pkg.SharedLibraryWrapper;
import com.android.server.pm.pkg.SuspendParams;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.WatchedArraySet;
@@ -1203,8 +1205,14 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
@NonNull
@Override
- public List<SharedLibraryInfo> getUsesLibraryInfos() {
- return pkgState.getUsesLibraryInfos();
+ public List<SharedLibrary> getUsesLibraries() {
+ return (List<SharedLibrary>) (List<?>) pkgState.getUsesLibraryInfos();
+ }
+
+ @NonNull
+ public PackageSetting addUsesLibraryInfo(@NonNull SharedLibraryInfo value) {
+ pkgState.addUsesLibraryInfo(new SharedLibraryWrapper(value));
+ return this;
}
@NonNull
@@ -1213,6 +1221,12 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
return pkgState.getUsesLibraryFiles();
}
+ @NonNull
+ public PackageSetting addUsesLibraryFile(String value) {
+ pkgState.addUsesLibraryFile(value);
+ return this;
+ }
+
@Override
public boolean isHiddenUntilInstalled() {
return pkgState.isHiddenUntilInstalled();
@@ -1314,6 +1328,13 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
return this;
}
+ @NonNull
+ @Override
+ public PackageUserState getStateForUser(@NonNull UserHandle user) {
+ PackageUserState userState = getUserStates().get(user.getIdentifier());
+ return userState == null ? PackageUserState.DEFAULT : userState;
+ }
+
// Code below generated by codegen v1.0.23.
@@ -1475,10 +1496,10 @@ public class PackageSetting extends SettingBase implements PackageStateInternal
}
@DataClass.Generated(
- time = 1659546705292L,
+ time = 1662666062860L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "private int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackageInternal)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic void setSharedUserAppId(int)\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int mSharedUserAppId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackageInternal pkg\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic void setSharedUserAppId(int)\npublic @java.lang.Override int getSharedUserAppId()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting,boolean)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponents(int)\n com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(com.android.server.utils.WatchedArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackage getAndroidPackage()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<com.android.server.pm.pkg.SharedLibrary> getUsesLibraries()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryInfo(android.content.pm.SharedLibraryInfo)\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @android.annotation.NonNull com.android.server.pm.PackageSetting addUsesLibraryFile(java.lang.String)\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserState getStateForUser(android.os.UserHandle)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/ParallelPackageParser.java b/services/core/java/com/android/server/pm/ParallelPackageParser.java
index 45030bfa59ce..56258844a3fe 100644
--- a/services/core/java/com/android/server/pm/ParallelPackageParser.java
+++ b/services/core/java/com/android/server/pm/ParallelPackageParser.java
@@ -27,7 +27,6 @@ import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import java.io.File;
-import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
@@ -55,17 +54,9 @@ class ParallelPackageParser {
private final ExecutorService mExecutorService;
- private final List<File> mFrameworkSplits;
-
ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService) {
- this(packageParser, executorService, /* frameworkSplits= */ null);
- }
-
- ParallelPackageParser(PackageParser2 packageParser, ExecutorService executorService,
- List<File> frameworkSplits) {
mPackageParser = packageParser;
mExecutorService = executorService;
- mFrameworkSplits = frameworkSplits;
}
static class ParseResult {
@@ -134,6 +125,6 @@ class ParallelPackageParser {
@VisibleForTesting
protected ParsedPackage parsePackage(File scanFile, int parseFlags)
throws PackageManagerException {
- return mPackageParser.parsePackage(scanFile, parseFlags, true, mFrameworkSplits);
+ return mPackageParser.parsePackage(scanFile, parseFlags, true);
}
}
diff --git a/services/core/java/com/android/server/pm/PostInstallData.java b/services/core/java/com/android/server/pm/PostInstallData.java
deleted file mode 100644
index 65d2a659bb45..000000000000
--- a/services/core/java/com/android/server/pm/PostInstallData.java
+++ /dev/null
@@ -1,40 +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.pm;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * Record-keeping of restore-after-install operations that are currently in flight
- * between the Package Manager and the Backup Manager
- */
-public final class PostInstallData {
- @Nullable
- public final InstallArgs args;
- @NonNull
- public final PackageInstalledInfo res;
- @Nullable
- public final Runnable mPostInstallRunnable;
-
- PostInstallData(@Nullable InstallArgs args, @NonNull PackageInstalledInfo res,
- @Nullable Runnable postInstallRunnable) {
- this.args = args;
- this.res = res;
- mPostInstallRunnable = postInstallRunnable;
- }
-}
diff --git a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
index 0f7c6526da17..165b450ce65b 100644
--- a/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
+++ b/services/core/java/com/android/server/pm/ReconcilePackageUtils.java
@@ -77,11 +77,10 @@ final class ReconcilePackageUtils {
}
// the following may be null if we're just reconciling on boot (and not during install)
- final InstallArgs installArgs = request.mInstallArgs.get(installPackageName);
- final PackageInstalledInfo res = request.mInstallResults.get(installPackageName);
+ final InstallRequest installRequest = request.mInstallRequests.get(installPackageName);
final PrepareResult prepareResult = request.mPreparedPackages.get(installPackageName);
- final boolean isInstall = installArgs != null;
- if (isInstall && (res == null || prepareResult == null)) {
+ final boolean isInstall = installRequest != null;
+ if (isInstall && prepareResult == null) {
throw new ReconcileFailure("Reconcile arguments are not balanced for "
+ installPackageName + "!");
}
@@ -92,7 +91,8 @@ final class ReconcilePackageUtils {
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 = DeletePackageHelper.mayDeletePackageLocked(res.mRemovedInfo,
+ deletePackageAction = DeletePackageHelper.mayDeletePackageLocked(
+ installRequest.getRemovedInfo(),
prepareResult.mOriginalPs, prepareResult.mDisabledPs,
deleteFlags, null /* all users */);
if (deletePackageAction == null) {
@@ -146,8 +146,8 @@ final class ReconcilePackageUtils {
request.mVersionInfos.get(installPackageName);
final boolean compareCompat = isCompatSignatureUpdateNeeded(versionInfo);
final boolean compareRecover = isRecoverSignatureUpdateNeeded(versionInfo);
- final boolean isRollback = installArgs != null
- && installArgs.mInstallReason == PackageManager.INSTALL_REASON_ROLLBACK;
+ final boolean isRollback = installRequest != null
+ && installRequest.isRollback();
final boolean compatMatch =
PackageManagerServiceUtils.verifySignatures(signatureCheckPs,
sharedUserSetting, disabledPkgSetting,
@@ -257,8 +257,8 @@ final class ReconcilePackageUtils {
}
result.put(installPackageName,
- new ReconciledPackage(request, installArgs, scanResult.mPkgSetting,
- res, request.mPreparedPackages.get(installPackageName), scanResult,
+ new ReconciledPackage(request, installRequest, scanResult.mPkgSetting,
+ request.mPreparedPackages.get(installPackageName), scanResult,
deletePackageAction, allowedSharedLibInfos, signingDetails,
sharedUserSignaturesChanged, removeAppKeySetData));
}
diff --git a/services/core/java/com/android/server/pm/ReconcileRequest.java b/services/core/java/com/android/server/pm/ReconcileRequest.java
index 84b292ff4068..3568c153de91 100644
--- a/services/core/java/com/android/server/pm/ReconcileRequest.java
+++ b/services/core/java/com/android/server/pm/ReconcileRequest.java
@@ -34,20 +34,17 @@ final class ReconcileRequest {
public final Map<String, ScanResult> mScannedPackages;
public final Map<String, AndroidPackage> mAllPackages;
- public final Map<String, InstallArgs> mInstallArgs;
- public final Map<String, PackageInstalledInfo> mInstallResults;
+ public final Map<String, InstallRequest> mInstallRequests;
public final Map<String, PrepareResult> mPreparedPackages;
public final Map<String, Settings.VersionInfo> mVersionInfos;
ReconcileRequest(Map<String, ScanResult> scannedPackages,
- Map<String, InstallArgs> installArgs,
- Map<String, PackageInstalledInfo> installResults,
+ Map<String, InstallRequest> installRequests,
Map<String, PrepareResult> preparedPackages,
Map<String, AndroidPackage> allPackages,
Map<String, Settings.VersionInfo> versionInfos) {
mScannedPackages = scannedPackages;
- mInstallArgs = installArgs;
- mInstallResults = installResults;
+ mInstallRequests = installRequests;
mPreparedPackages = preparedPackages;
mAllPackages = allPackages;
mVersionInfos = versionInfos;
@@ -56,7 +53,7 @@ final class ReconcileRequest {
ReconcileRequest(Map<String, ScanResult> scannedPackages,
Map<String, AndroidPackage> allPackages,
Map<String, Settings.VersionInfo> versionInfos) {
- this(scannedPackages, Collections.emptyMap(), Collections.emptyMap(),
+ this(scannedPackages, Collections.emptyMap(),
Collections.emptyMap(), allPackages, versionInfos);
}
}
diff --git a/services/core/java/com/android/server/pm/ReconciledPackage.java b/services/core/java/com/android/server/pm/ReconciledPackage.java
index 3bb5a1b66338..d4da6c798b15 100644
--- a/services/core/java/com/android/server/pm/ReconciledPackage.java
+++ b/services/core/java/com/android/server/pm/ReconciledPackage.java
@@ -37,9 +37,8 @@ final class ReconciledPackage {
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;
+ @Nullable public final InstallRequest mInstallRequest;
public final DeletePackageAction mDeletePackageAction;
public final List<SharedLibraryInfo> mAllowedSharedLibraryInfos;
public final SigningDetails mSigningDetails;
@@ -48,9 +47,8 @@ final class ReconciledPackage {
public final boolean mRemoveAppKeySetData;
ReconciledPackage(ReconcileRequest request,
- InstallArgs installArgs,
+ InstallRequest installRequest,
PackageSetting pkgSetting,
- PackageInstalledInfo installResult,
PrepareResult prepareResult,
ScanResult scanResult,
DeletePackageAction deletePackageAction,
@@ -59,9 +57,8 @@ final class ReconciledPackage {
boolean sharedUserSignaturesChanged,
boolean removeAppKeySetData) {
mRequest = request;
- mInstallArgs = installArgs;
+ mInstallRequest = installRequest;
mPkgSetting = pkgSetting;
- mInstallResult = installResult;
mPrepareResult = prepareResult;
mScanResult = scanResult;
mDeletePackageAction = deletePackageAction;
diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java
index a02596538627..bbc4fdeb36bb 100644
--- a/services/core/java/com/android/server/pm/RemovePackageHelper.java
+++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java
@@ -215,21 +215,21 @@ final class RemovePackageHelper {
r = null;
// Any package can hold SDK or static shared libraries.
- if (pkg.getSdkLibName() != null) {
+ if (pkg.getSdkLibraryName() != null) {
if (mSharedLibraries.removeSharedLibrary(
- pkg.getSdkLibName(), pkg.getSdkLibVersionMajor())) {
+ pkg.getSdkLibraryName(), pkg.getSdkLibVersionMajor())) {
if (DEBUG_REMOVE && chatty) {
if (r == null) {
r = new StringBuilder(256);
} else {
r.append(' ');
}
- r.append(pkg.getSdkLibName());
+ r.append(pkg.getSdkLibraryName());
}
}
}
- if (pkg.getStaticSharedLibName() != null) {
- if (mSharedLibraries.removeSharedLibrary(pkg.getStaticSharedLibName(),
+ if (pkg.getStaticSharedLibraryName() != null) {
+ if (mSharedLibraries.removeSharedLibrary(pkg.getStaticSharedLibraryName(),
pkg.getStaticSharedLibVersion())) {
if (DEBUG_REMOVE && chatty) {
if (r == null) {
@@ -237,7 +237,7 @@ final class RemovePackageHelper {
} else {
r.append(' ');
}
- r.append(pkg.getStaticSharedLibName());
+ r.append(pkg.getStaticSharedLibraryName());
}
}
}
@@ -271,7 +271,7 @@ final class RemovePackageHelper {
outInfo.mRemovedPackage = packageName;
outInfo.mInstallerPackageName = deletedPs.getInstallSource().installerPackageName;
outInfo.mIsStaticSharedLib = deletedPkg != null
- && deletedPkg.getStaticSharedLibName() != null;
+ && deletedPkg.getStaticSharedLibraryName() != null;
outInfo.populateUsers(deletedPs.queryInstalledUsers(
mUserManagerInternal.getUserIds(), true), deletedPs);
outInfo.mIsExternal = deletedPs.isExternalStorage();
@@ -385,29 +385,29 @@ final class RemovePackageHelper {
}
}
- void cleanUpResources(InstallArgs args) {
+ void cleanUpResources(File codeFile, String[] instructionSets) {
synchronized (mPm.mInstallLock) {
- cleanUpResourcesLI(args);
+ cleanUpResourcesLI(codeFile, instructionSets);
}
}
// Need installer lock especially for dex file removal.
@GuardedBy("mPm.mInstallLock")
- private void cleanUpResourcesLI(InstallArgs args) {
+ private void cleanUpResourcesLI(File codeFile, String[] instructionSets) {
// Try enumerating all code paths before deleting
List<String> allCodePaths = Collections.EMPTY_LIST;
- if (args.mCodeFile != null && args.mCodeFile.exists()) {
+ if (codeFile != null && codeFile.exists()) {
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
- input.reset(), args.mCodeFile, /* flags */ 0);
+ input.reset(), codeFile, /* flags */ 0);
if (result.isSuccess()) {
// Ignore error; we tried our best
allCodePaths = result.getResult().getAllApkPaths();
}
}
- removeCodePathLI(args.mCodeFile);
- removeDexFilesLI(allCodePaths, args.mInstructionSets);
+ removeCodePathLI(codeFile);
+ removeDexFilesLI(allCodePaths, instructionSets);
}
@GuardedBy("mPm.mInstallLock")
diff --git a/services/core/java/com/android/server/pm/ResolveIntentHelper.java b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
index c2fd637cfbd0..fada5778b634 100644
--- a/services/core/java/com/android/server/pm/ResolveIntentHelper.java
+++ b/services/core/java/com/android/server/pm/ResolveIntentHelper.java
@@ -52,6 +52,7 @@ import android.util.Slog;
import com.android.internal.app.ResolverActivity;
import com.android.internal.util.ArrayUtils;
+import com.android.server.am.ActivityManagerService;
import com.android.server.compat.PlatformCompat;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
@@ -100,6 +101,38 @@ final class ResolveIntentHelper {
mInstantAppInstallerActivitySupplier = instantAppInstallerActivitySupplier;
}
+ private static void filterNonExportedComponents(Intent intent, int filterCallingUid,
+ List<ResolveInfo> query, PlatformCompat platformCompat, Computer computer) {
+ if (query == null
+ || intent.getPackage() != null
+ || intent.getComponent() != null
+ || ActivityManager.canAccessUnexportedComponents(filterCallingUid)) {
+ return;
+ }
+ AndroidPackage caller = computer.getPackage(filterCallingUid);
+ String callerPackage = caller == null ? "Not specified" : caller.getPackageName();
+ for (int i = query.size() - 1; i >= 0; i--) {
+ if (!query.get(i).getComponentInfo().exported) {
+ if (!platformCompat.isChangeEnabledByUid(
+ ActivityManagerService.IMPLICIT_INTENTS_ONLY_MATCH_EXPORTED_COMPONENTS,
+ filterCallingUid)) {
+ Slog.w(TAG, "Non-exported component not filtered out "
+ + "(will be filtered out once the app targets U+)- intent: "
+ + intent.getAction() + ", component: "
+ + query.get(i).getComponentInfo()
+ .getComponentName().flattenToShortString()
+ + ", starter: " + callerPackage);
+ return;
+ }
+ Slog.w(TAG, "Non-exported component filtered out - intent: "
+ + intent.getAction() + ", component: "
+ + query.get(i).getComponentInfo().getComponentName().flattenToShortString()
+ + ", starter: " + callerPackage);
+ query.remove(i);
+ }
+ }
+ }
+
/**
* Normally instant apps can only be resolved when they're visible to the caller.
* However, if {@code resolveForStart} is {@code true}, all instant apps are visible
@@ -109,6 +142,20 @@ final class ResolveIntentHelper {
@PackageManager.ResolveInfoFlagsBits long flags,
@PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
boolean resolveForStart, int filterCallingUid) {
+ return resolveIntentInternal(computer, intent, resolvedType, flags,
+ privateResolveFlags, userId, resolveForStart, filterCallingUid, false);
+ }
+
+ /**
+ * Normally instant apps can only be resolved when they're visible to the caller.
+ * However, if {@code resolveForStart} is {@code true}, all instant apps are visible
+ * since we need to allow the system to start any installed application.
+ * Allows picking exported components only.
+ */
+ public ResolveInfo resolveIntentInternal(Computer computer, Intent intent, String resolvedType,
+ @PackageManager.ResolveInfoFlagsBits long flags,
+ @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags, int userId,
+ boolean resolveForStart, int filterCallingUid, boolean exportedComponentsOnly) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
@@ -124,6 +171,10 @@ final class ResolveIntentHelper {
final List<ResolveInfo> query = computer.queryIntentActivitiesInternal(intent,
resolvedType, flags, privateResolveFlags, filterCallingUid, userId,
resolveForStart, true /*allowDynamicSplits*/);
+ if (exportedComponentsOnly) {
+ filterNonExportedComponents(intent, filterCallingUid, query,
+ mPlatformCompat, computer);
+ }
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
final boolean queryMayBeFiltered =
diff --git a/services/core/java/com/android/server/pm/ScanPackageUtils.java b/services/core/java/com/android/server/pm/ScanPackageUtils.java
index 9bd8e12c4d92..bce6834b8b1b 100644
--- a/services/core/java/com/android/server/pm/ScanPackageUtils.java
+++ b/services/core/java/com/android/server/pm/ScanPackageUtils.java
@@ -445,11 +445,11 @@ final class ScanPackageUtils {
}
SharedLibraryInfo sdkLibraryInfo = null;
- if (!TextUtils.isEmpty(parsedPackage.getSdkLibName())) {
+ if (!TextUtils.isEmpty(parsedPackage.getSdkLibraryName())) {
sdkLibraryInfo = AndroidPackageUtils.createSharedLibraryForSdk(parsedPackage);
}
SharedLibraryInfo staticSharedLibraryInfo = null;
- if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {
+ if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibraryName())) {
staticSharedLibraryInfo =
AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 9037f042641f..0558fbdb6361 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4667,17 +4667,17 @@ public final class Settings implements Watchable, Snappable {
pw.println(libraryNames.get(i));
}
}
- if (pkg.getStaticSharedLibName() != null) {
+ if (pkg.getStaticSharedLibraryName() != null) {
pw.print(prefix); pw.println(" static library:");
pw.print(prefix); pw.print(" ");
- pw.print("name:"); pw.print(pkg.getStaticSharedLibName());
+ pw.print("name:"); pw.print(pkg.getStaticSharedLibraryName());
pw.print(" version:"); pw.println(pkg.getStaticSharedLibVersion());
}
- if (pkg.getSdkLibName() != null) {
+ if (pkg.getSdkLibraryName() != null) {
pw.print(prefix); pw.println(" SDK library:");
pw.print(prefix); pw.print(" ");
- pw.print("name:"); pw.print(pkg.getSdkLibName());
+ pw.print("name:"); pw.print(pkg.getSdkLibraryName());
pw.print(" versionMajor:"); pw.println(pkg.getSdkLibVersionMajor());
}
@@ -5503,6 +5503,7 @@ public final class Settings implements Watchable, Snappable {
+ "set before trying to update the fingerprint.");
}
mFingerprints.put(userId, mExtendedFingerprint);
+ mPermissionUpgradeNeeded.put(userId, false);
writeStateForUserAsync(userId);
}
}
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 5905741d5db2..094e748685e5 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -400,7 +400,7 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
@Nullable
private SharedLibraryInfo getLatestStaticSharedLibraVersionLPr(@NonNull AndroidPackage pkg) {
WatchedLongSparseArray<SharedLibraryInfo> versionedLib = mSharedLibraries.get(
- pkg.getStaticSharedLibName());
+ pkg.getStaticSharedLibraryName());
if (versionedLib == null) {
return null;
}
@@ -457,15 +457,15 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
// - Package manager is in a state where package isn't scanned yet. This will
// get called again after scanning to fix the dependencies.
if (AndroidPackageUtils.isLibrary(pkg)) {
- if (pkg.getSdkLibName() != null) {
+ if (pkg.getSdkLibraryName() != null) {
SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
- pkg.getSdkLibName(), pkg.getSdkLibVersionMajor());
+ pkg.getSdkLibraryName(), pkg.getSdkLibVersionMajor());
if (definedLibrary != null) {
action.accept(definedLibrary, libInfo);
}
- } else if (pkg.getStaticSharedLibName() != null) {
+ } else if (pkg.getStaticSharedLibraryName() != null) {
SharedLibraryInfo definedLibrary = getSharedLibraryInfo(
- pkg.getStaticSharedLibName(), pkg.getStaticSharedLibVersion());
+ pkg.getStaticSharedLibraryName(), pkg.getStaticSharedLibVersion());
if (definedLibrary != null) {
action.accept(definedLibrary, libInfo);
}
@@ -691,9 +691,9 @@ public final class SharedLibrariesImpl implements SharedLibrariesRead, Watchable
&& !hasString(pkg.getUsesLibraries(), changingPkg.getLibraryNames())
&& !hasString(pkg.getUsesOptionalLibraries(), changingPkg.getLibraryNames())
&& !ArrayUtils.contains(pkg.getUsesStaticLibraries(),
- changingPkg.getStaticSharedLibName())
+ changingPkg.getStaticSharedLibraryName())
&& !ArrayUtils.contains(pkg.getUsesSdkLibraries(),
- changingPkg.getSdkLibName())) {
+ changingPkg.getSdkLibraryName())) {
continue;
}
if (resultList == null) {
diff --git a/services/core/java/com/android/server/pm/SharedLibraryUtils.java b/services/core/java/com/android/server/pm/SharedLibraryUtils.java
index 274870dad6f0..2c287916d9f7 100644
--- a/services/core/java/com/android/server/pm/SharedLibraryUtils.java
+++ b/services/core/java/com/android/server/pm/SharedLibraryUtils.java
@@ -20,6 +20,7 @@ import android.annotation.Nullable;
import android.content.pm.SharedLibraryInfo;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SharedLibraryWrapper;
import com.android.server.utils.WatchedLongSparseArray;
import java.util.ArrayList;
@@ -79,8 +80,8 @@ final class SharedLibraryUtils {
if (!pkgSetting.getTransientState().getUsesLibraryInfos().isEmpty()) {
ArrayList<SharedLibraryInfo> retValue = new ArrayList<>();
Set<String> collectedNames = new HashSet<>();
- for (SharedLibraryInfo info : pkgSetting.getTransientState().getUsesLibraryInfos()) {
- findSharedLibrariesRecursive(info, retValue, collectedNames);
+ for (SharedLibraryWrapper info : pkgSetting.getTransientState().getUsesLibraryInfos()) {
+ findSharedLibrariesRecursive(info.getInfo(), retValue, collectedNames);
}
return retValue;
} else {
diff --git a/services/core/java/com/android/server/pm/SuspendPackageHelper.java b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
index df7e3755e463..51bb412fe712 100644
--- a/services/core/java/com/android/server/pm/SuspendPackageHelper.java
+++ b/services/core/java/com/android/server/pm/SuspendPackageHelper.java
@@ -108,12 +108,12 @@ public final class SuspendPackageHelper {
final SuspendParams newSuspendParams =
new SuspendParams(dialogInfo, appExtras, launcherExtras);
- final List<String> changedPackagesList = new ArrayList<>(packageNames.length);
- final IntArray changedUids = new IntArray(packageNames.length);
- final IntArray modifiedUids = new IntArray(packageNames.length);
final List<String> unmodifiablePackages = new ArrayList<>(packageNames.length);
- ArraySet<String> modifiedPackages = new ArraySet<>();
+ final List<String> notifyPackagesList = new ArrayList<>(packageNames.length);
+ final IntArray notifyUids = new IntArray(packageNames.length);
+ final ArraySet<String> changedPackagesList = new ArraySet<>(packageNames.length);
+ final IntArray changedUids = new IntArray(packageNames.length);
final boolean[] canSuspend = suspended
? canSuspendPackageForUser(snapshot, packageNames, userId, callingUid) : null;
@@ -140,21 +140,17 @@ public final class SuspendPackageHelper {
final WatchedArrayMap<String, SuspendParams> suspendParamsMap =
packageState.getUserStateOrDefault(userId).getSuspendParams();
- if (suspended) {
- if (suspendParamsMap != null && suspendParamsMap.containsKey(packageName)) {
- final SuspendParams suspendParams = suspendParamsMap.get(packageName);
- // Skip if there's no changes
- if (suspendParams != null
- && Objects.equals(suspendParams.getDialogInfo(), dialogInfo)
- && Objects.equals(suspendParams.getAppExtras(), appExtras)
- && Objects.equals(suspendParams.getLauncherExtras(),
- launcherExtras)) {
- // Carried over API behavior, must notify change even if no change
- changedPackagesList.add(packageName);
- changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
- continue;
- }
- }
+
+ SuspendParams oldSuspendParams = suspendParamsMap == null
+ ? null : suspendParamsMap.get(packageName);
+ boolean changed = !Objects.equals(oldSuspendParams, newSuspendParams);
+
+ if (suspended && !changed) {
+ // Carried over API behavior, must notify change even if no change
+ notifyPackagesList.add(packageName);
+ notifyUids.add(
+ UserHandle.getUid(userId, packageState.getAppId()));
+ continue;
}
// If only the callingPackage is suspending this package,
@@ -163,18 +159,21 @@ public final class SuspendPackageHelper {
&& CollectionUtils.size(suspendParamsMap) == 1
&& suspendParamsMap.containsKey(callingPackage);
if (suspended || packageUnsuspended) {
+ // Always notify of a suspend call + notify when fully unsuspended
+ notifyPackagesList.add(packageName);
+ notifyUids.add(UserHandle.getUid(userId, packageState.getAppId()));
+ }
+
+ if (changed) {
changedPackagesList.add(packageName);
changedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
}
-
- modifiedPackages.add(packageName);
- modifiedUids.add(UserHandle.getUid(userId, packageState.getAppId()));
}
mPm.commitPackageStateMutation(null, mutator -> {
- final int size = modifiedPackages.size();
+ final int size = changedPackagesList.size();
for (int index = 0; index < size; index++) {
- final String packageName = modifiedPackages.valueAt(index);
+ final String packageName = changedPackagesList.valueAt(index);
final PackageUserStateWrite userState = mutator.forPackage(packageName)
.userState(userId);
if (suspended) {
@@ -185,19 +184,20 @@ public final class SuspendPackageHelper {
}
});
- if (!changedPackagesList.isEmpty()) {
- final String[] changedPackages = changedPackagesList.toArray(new String[0]);
+ if (!notifyPackagesList.isEmpty()) {
+ final String[] changedPackages =
+ notifyPackagesList.toArray(new String[0]);
sendPackagesSuspendedForUser(
suspended ? Intent.ACTION_PACKAGES_SUSPENDED
: Intent.ACTION_PACKAGES_UNSUSPENDED,
- changedPackages, changedUids.toArray(), userId);
+ changedPackages, notifyUids.toArray(), userId);
sendMyPackageSuspendedOrUnsuspended(changedPackages, suspended, userId);
mPm.scheduleWritePackageRestrictions(userId);
}
// Send the suspension changed broadcast to ensure suspension state is not stale.
- if (!modifiedPackages.isEmpty()) {
+ if (!changedPackagesList.isEmpty()) {
sendPackagesSuspendedForUser(Intent.ACTION_PACKAGES_SUSPENSION_CHANGED,
- modifiedPackages.toArray(new String[0]), modifiedUids.toArray(), userId);
+ changedPackagesList.toArray(new String[0]), changedUids.toArray(), userId);
}
return unmodifiablePackages.toArray(new String[0]);
}
@@ -548,7 +548,7 @@ public final class SuspendPackageHelper {
if (pkg.isSdkLibrary()) {
Slog.w(TAG, "Cannot suspend package: " + packageName
+ " providing SDK library: "
- + pkg.getSdkLibName());
+ + pkg.getSdkLibraryName());
continue;
}
// Cannot suspend static shared libs as they are considered
@@ -557,7 +557,7 @@ public final class SuspendPackageHelper {
if (pkg.isStaticSharedLibrary()) {
Slog.w(TAG, "Cannot suspend package: " + packageName
+ " providing static shared library: "
- + pkg.getStaticSharedLibName());
+ + pkg.getStaticSharedLibraryName());
continue;
}
}
diff --git a/services/core/java/com/android/server/pm/UserManagerInternal.java b/services/core/java/com/android/server/pm/UserManagerInternal.java
index 4f5fd023d470..b9770254fb46 100644
--- a/services/core/java/com/android/server/pm/UserManagerInternal.java
+++ b/services/core/java/com/android/server/pm/UserManagerInternal.java
@@ -46,15 +46,6 @@ public abstract class UserManagerInternal {
public @interface OwnerType {
}
- // TODO(b/245963156): move to Display.java (and @hide) if we decide to support profiles on MUMD
- /**
- * Used only when starting a profile (on systems that
- * {@link android.os.UserManager#isUsersOnSecondaryDisplaysSupported() support users running on
- * secondary displays}), to indicate the profile should be started in the same display as its
- * parent user.
- */
- public static final int PARENT_DISPLAY = -2;
-
public interface UserRestrictionsListener {
/**
* Called when a user restriction changes.
@@ -328,8 +319,10 @@ public abstract class UserManagerInternal {
* <p>On most devices this call will be a no-op, but it will be used on devices that support
* multiple users on multiple displays (like automotives with passenger displays).
*
- * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} when a user is
- * started and it doesn't validate if the display exists.
+ * <p><b>NOTE: </b>this method doesn't validate if the display exists, it's up to the caller to
+ * check it. In fact, one of the intended clients for this method is
+ * {@code DisplayManagerService}, which will call it when a virtual display is created (another
+ * client is {@code UserController}, which will call it when a user is started).
*
*/
public abstract void assignUserToDisplay(@UserIdInt int userId, int displayId);
@@ -340,8 +333,8 @@ public abstract class UserManagerInternal {
* <p>On most devices this call will be a no-op, but it will be used on devices that support
* multiple users on multiple displays (like automotives with passenger displays).
*
- * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} when a user is
- * stopped.
+ * <p><b>NOTE: </b>this method is meant to be used only by {@code UserController} (when a user
+ * is stopped) and {@code DisplayManagerService} (when a virtual display is destroyed).
*/
public abstract void unassignUserFromDisplay(@UserIdInt int userId);
@@ -361,11 +354,14 @@ public abstract class UserManagerInternal {
* Returns the display id assigned to the user, or {@code Display.INVALID_DISPLAY} if the
* user is not assigned to any display.
*
- * <p>The current foreground user is associated with the
+ * <p>The current foreground user and its running profiles are associated with the
* {@link android.view.Display#DEFAULT_DISPLAY default display}, while other users would only be
- * assigned to a display if they were started with
- * {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}. If the user is a profile
- * and is running, it's assigned to its parent display.
+ * assigned to a display if a call to {@link #assignUserToDisplay(int, int)} is made for such
+ * user / display combination (for example, if the user was started with
+ * {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}, {@code UserController}
+ * would make such call).
+ *
+ * <p>If the user is a profile and is running, it's assigned to its parent display.
*/
public abstract int getDisplayAssignedToUser(@UserIdInt int userId);
@@ -375,8 +371,11 @@ public abstract class UserManagerInternal {
* associated with the display.
*
* <p>The {@link android.view.Display#DEFAULT_DISPLAY default display} is always assigned to
- * the current foreground user, while other displays would be associated with the user that was
- * started with {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}.
+ * the current foreground user, while other displays would only be associated with users through
+ * a explicit {@link #assignUserToDisplay(int, int)} call with that user / display combination
+ * (for example, if the user was started with
+ * {@code ActivityManager.startUserInBackgroundOnSecondaryDisplay()}, {@code UserController}
+ * would make such call).
*/
public abstract @UserIdInt int getUserAssignedToDisplay(int displayId);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 07ec80bcd603..ff87be99ca5a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1785,18 +1785,10 @@ public class UserManagerService extends IUserManager.Stub {
@VisibleForTesting
int getUserAssignedToDisplay(int displayId) {
- if (displayId == Display.DEFAULT_DISPLAY) {
+ if (displayId == Display.DEFAULT_DISPLAY || !mUsersOnSecondaryDisplaysEnabled) {
return getCurrentUserId();
}
- if (!mUsersOnSecondaryDisplaysEnabled) {
- int currentUserId = getCurrentUserId();
- Slogf.w(LOG_TAG, "getUsersAssignedToDisplay(%d) called with non-DEFAULT_DISPLAY on "
- + "system that doesn't support that; returning current user (%d)", displayId,
- currentUserId);
- return currentUserId;
- }
-
synchronized (mUsersOnSecondaryDisplays) {
for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
if (mUsersOnSecondaryDisplays.valueAt(i) != displayId) {
@@ -2056,8 +2048,8 @@ public class UserManagerService extends IUserManager.Stub {
}
}
- @Override
- public boolean isUserSwitcherEnabled(@UserIdInt int mUserId) {
+ @VisibleForTesting
+ boolean isUserSwitcherEnabled(@UserIdInt int mUserId) {
boolean multiUserSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.USER_SWITCHER_ENABLED,
Resources.getSystem().getBoolean(com.android.internal
@@ -2070,6 +2062,33 @@ public class UserManagerService extends IUserManager.Stub {
}
@Override
+ public boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable,
+ @UserIdInt int mUserId) {
+ if (!isUserSwitcherEnabled(mUserId)) {
+ return false;
+ }
+ // The feature is enabled. But is it worth showing?
+ return showEvenIfNotActionable
+ || !hasUserRestriction(UserManager.DISALLOW_ADD_USER, mUserId) // Can add new user
+ || areThereMultipleSwitchableUsers(); // There are switchable users
+ }
+
+ /** Returns true if there is more than one user that can be switched to. */
+ private boolean areThereMultipleSwitchableUsers() {
+ List<UserInfo> aliveUsers = getUsers(true, true, true);
+ boolean isAnyAliveUser = false;
+ for (UserInfo userInfo : aliveUsers) {
+ if (userInfo.supportsSwitchToByUser()) {
+ if (isAnyAliveUser) {
+ return true;
+ }
+ isAnyAliveUser = true;
+ }
+ }
+ return false;
+ }
+
+ @Override
public boolean isRestricted(@UserIdInt int userId) {
if (userId != UserHandle.getCallingUserId()) {
checkCreateUsersPermission("query isRestricted for user " + userId);
@@ -6803,13 +6822,25 @@ public class UserManagerService extends IUserManager.Stub {
Slogf.d(LOG_TAG, "assignUserToDisplay(%d, %d)", userId, displayId);
}
+ // NOTE: Using Boolean instead of boolean as it will be re-used below
+ Boolean isProfile = null;
if (displayId == Display.DEFAULT_DISPLAY) {
- // Don't need to do anything because methods (such as isUserVisible()) already know
- // that the current user (and their profiles) is assigned to the default display.
- if (DBG_MUMD) {
- Slogf.d(LOG_TAG, "ignoring on default display");
+ if (mUsersOnSecondaryDisplaysEnabled) {
+ // Profiles are only supported in the default display, but it cannot return yet
+ // as it needs to check if the parent is also assigned to the DEFAULT_DISPLAY
+ // (this is done indirectly below when it checks that the profile parent is the
+ // current user, as the current user is always assigned to the DEFAULT_DISPLAY).
+ isProfile = isProfileUnchecked(userId);
+ }
+ if (isProfile == null || !isProfile) {
+ // Don't need to do anything because methods (such as isUserVisible()) already
+ // know that the current user (and their profiles) is assigned to the default
+ // display.
+ if (DBG_MUMD) {
+ Slogf.d(LOG_TAG, "ignoring on default display");
+ }
+ return;
}
- return;
}
if (!mUsersOnSecondaryDisplaysEnabled) {
@@ -6827,37 +6858,42 @@ public class UserManagerService extends IUserManager.Stub {
Preconditions.checkArgument(userId != currentUserId,
"Cannot assign current user (%d) to other displays", currentUserId);
+ if (isProfile == null) {
+ isProfile = isProfileUnchecked(userId);
+ }
synchronized (mUsersOnSecondaryDisplays) {
- if (isProfileUnchecked(userId)) {
- // Profile can only start in the same display as parent
- Preconditions.checkArgument(displayId == UserManagerInternal.PARENT_DISPLAY,
- "Profile user can only be started in the same display as parent");
+ if (isProfile) {
+ // Profile can only start in the same display as parent. And for simplicity,
+ // that display must be the DEFAULT_DISPLAY.
+ Preconditions.checkArgument(displayId == Display.DEFAULT_DISPLAY,
+ "Profile user can only be started in the default display");
int parentUserId = getProfileParentId(userId);
- int parentDisplayId = mUsersOnSecondaryDisplays.get(parentUserId);
+ Preconditions.checkArgument(parentUserId == currentUserId,
+ "Only profile of current user can be assigned to a display");
if (DBG_MUMD) {
- Slogf.d(LOG_TAG, "Adding profile user %d -> display %d", userId,
- parentDisplayId);
+ Slogf.d(LOG_TAG, "Ignoring profile user %d on default display", userId);
}
- mUsersOnSecondaryDisplays.put(userId, parentDisplayId);
return;
}
// Check if display is available
for (int i = 0; i < mUsersOnSecondaryDisplays.size(); i++) {
- // Make sure display is not used by other users...
- // TODO(b/240736142); currently, if a user was started in a display, it
- // would need to be stopped first, so "switching" a user on secondary
- // diplay requires 2 non-atomic operations (stop and start). Once this logic
- // is refactored, it should be atomic.
- if (mUsersOnSecondaryDisplays.valueAt(i) == displayId) {
- throw new IllegalStateException("Cannot assign " + userId + " to "
- + "display " + displayId + " as it's already assigned to "
- + "user " + mUsersOnSecondaryDisplays.keyAt(i));
+ int assignedUserId = mUsersOnSecondaryDisplays.keyAt(i);
+ int assignedDisplayId = mUsersOnSecondaryDisplays.valueAt(i);
+ if (DBG_MUMD) {
+ Slogf.d(LOG_TAG, "%d: assignedUserId=%d, assignedDisplayId=%d",
+ i, assignedUserId, assignedDisplayId);
+ }
+ if (displayId == assignedDisplayId) {
+ throw new IllegalStateException("Cannot assign user " + userId + " to "
+ + "display " + displayId + " because such display is already "
+ + "assigned to user " + assignedUserId);
+ }
+ if (userId == assignedUserId) {
+ throw new IllegalStateException("Cannot assign user " + userId + " to "
+ + "display " + displayId + " because such user is as already "
+ + "assigned to display " + assignedDisplayId);
}
- // TODO(b/239982558) also check that user is not already assigned to other
- // display (including 0). That would be harder to tested under CTS though
- // (for example, would need to add a new AM method to start user in bg on
- // main display), so it's better to test on unit tests
}
if (DBG_MUMD) {
@@ -7078,4 +7114,5 @@ public class UserManagerService extends IUserManager.Stub {
}
return mAmInternal;
}
+
}
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 108414528199..bc3d7a6b6778 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -81,6 +81,7 @@ import com.android.server.pm.pkg.parsing.ParsingUtils;
import libcore.util.EmptyArray;
import java.io.File;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -472,7 +473,11 @@ public class PackageInfoUtils {
PackageStateUnserialized pkgState = pkgSetting.getTransientState();
info.hiddenUntilInstalled = pkgState.isHiddenUntilInstalled();
List<String> usesLibraryFiles = pkgState.getUsesLibraryFiles();
- List<SharedLibraryInfo> usesLibraryInfos = pkgState.getUsesLibraryInfos();
+ var usesLibraries = pkgState.getUsesLibraryInfos();
+ var usesLibraryInfos = new ArrayList<SharedLibraryInfo>();
+ for (int index = 0; index < usesLibraries.size(); index++) {
+ usesLibraryInfos.add(usesLibraries.get(index).getInfo());
+ }
info.sharedLibraryFiles = usesLibraryFiles.isEmpty()
? null : usesLibraryFiles.toArray(new String[0]);
info.sharedLibraryInfos = usesLibraryInfos.isEmpty() ? null : usesLibraryInfos;
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 6caddaf430dc..f5ba3f61a00c 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -143,15 +143,6 @@ public class PackageParser2 implements AutoCloseable {
@AnyThread
public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
throws PackageManagerException {
- return parsePackage(packageFile, flags, useCaches, /* frameworkSplits= */ null);
- }
-
- /**
- * TODO(b/135203078): Document new package parsing
- */
- @AnyThread
- public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches,
- List<File> frameworkSplits) throws PackageManagerException {
var files = packageFile.listFiles();
// Apk directory is directly nested under the current directory
if (ArrayUtils.size(files) == 1 && files[0].isDirectory()) {
@@ -167,8 +158,7 @@ public class PackageParser2 implements AutoCloseable {
long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
ParseInput input = mSharedResult.get().reset();
- ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags,
- frameworkSplits);
+ ParseResult<ParsingPackage> result = parsingUtils.parsePackage(input, packageFile, flags);
if (result.isError()) {
throw new PackageManagerException(result.getErrorCode(), result.getErrorMessage(),
result.getException());
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 f6585f64c07f..ca8ba6c32591 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
@@ -91,7 +91,7 @@ public class AndroidPackageUtils {
public static SharedLibraryInfo createSharedLibraryForSdk(AndroidPackage pkg) {
return new SharedLibraryInfo(null, pkg.getPackageName(),
AndroidPackageUtils.getAllCodePaths(pkg),
- pkg.getSdkLibName(),
+ pkg.getSdkLibraryName(),
pkg.getSdkLibVersionMajor(),
SharedLibraryInfo.TYPE_SDK_PACKAGE,
new VersionedPackage(pkg.getManifestPackageName(),
@@ -102,7 +102,7 @@ public class AndroidPackageUtils {
public static SharedLibraryInfo createSharedLibraryForStatic(AndroidPackage pkg) {
return new SharedLibraryInfo(null, pkg.getPackageName(),
AndroidPackageUtils.getAllCodePaths(pkg),
- pkg.getStaticSharedLibName(),
+ pkg.getStaticSharedLibraryName(),
pkg.getStaticSharedLibVersion(),
SharedLibraryInfo.TYPE_STATIC,
new VersionedPackage(pkg.getManifestPackageName(),
@@ -230,7 +230,7 @@ public class AndroidPackageUtils {
public static boolean isLibrary(AndroidPackage pkg) {
// TODO(b/135203078): Can parsing just enforce these always match?
- return pkg.getSdkLibName() != null || pkg.getStaticSharedLibName() != null
+ return pkg.getSdkLibraryName() != null || pkg.getStaticSharedLibraryName() != null
|| !pkg.getLibraryNames().isEmpty();
}
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 70aca99f04e5..a43b9796dc76 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
@@ -57,6 +57,8 @@ import com.android.internal.util.Parcelling;
import com.android.internal.util.Parcelling.BuiltIn.ForInternedString;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.pkg.AndroidPackage;
+import com.android.server.pm.pkg.AndroidPackageSplit;
+import com.android.server.pm.pkg.AndroidPackageSplitImpl;
import com.android.server.pm.pkg.SELinuxUtil;
import com.android.server.pm.pkg.component.ComponentMutateUtils;
import com.android.server.pm.pkg.component.ParsedActivity;
@@ -90,6 +92,7 @@ import libcore.util.EmptyArray;
import java.io.File;
import java.security.PublicKey;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@@ -235,11 +238,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
private Map<String, String> overlayables = emptyMap();
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
- private String sdkLibName;
+ private String sdkLibraryName;
private int sdkLibVersionMajor;
@Nullable
@DataClass.ParcelWith(ForInternedString.class)
- private String staticSharedLibName;
+ private String staticSharedLibraryName;
private long staticSharedLibVersion;
@NonNull
@DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringList.class)
@@ -385,20 +388,22 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@Nullable
@DataClass.ParcelWith(Parcelling.BuiltIn.ForBoolean.class)
private Boolean requestRawExternalStorageAccess;
- // TODO(chiuwinson): Non-null
- @Nullable
- private ArraySet<String> mimeGroups;
+ @NonNull
+ @DataClass.ParcelWith(Parcelling.BuiltIn.ForInternedStringSet.class)
+ private Set<String> mimeGroups = emptySet();
// Usually there's code to set enabled to true during parsing, but it's possible to install
// an APK targeting <R that doesn't contain an <application> tag. That code would be skipped
// and never assign this, so initialize this to true for those cases.
private long mBooleans = Booleans.ENABLED;
private long mBooleans2;
- @Nullable
- private Set<String> mKnownActivityEmbeddingCerts;
+ @NonNull
+ private Set<String> mKnownActivityEmbeddingCerts = emptySet();
// Derived fields
private long mLongVersionCode;
private int mLocaleConfigRes;
+ private List<AndroidPackageSplit> mSplits;
+
@NonNull
public static PackageImpl forParsing(@NonNull String packageName, @NonNull String baseCodePath,
@NonNull String codePath, @NonNull TypedArray manifestArray, boolean isCoreApp) {
@@ -549,7 +554,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
if (mimeGroups != null && mimeGroups.size() > 500) {
throw new IllegalStateException("Max limit on number of MIME Groups reached");
}
- mimeGroups = ArrayUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
+ mimeGroups = CollectionUtils.add(mimeGroups, filter.getMimeGroup(groupIndex));
}
}
}
@@ -775,6 +780,51 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
}
@Override
+ public List<AndroidPackageSplit> getSplits() {
+ if (mSplits == null) {
+ var splits = new ArrayList<AndroidPackageSplit>();
+ splits.add(new AndroidPackageSplitImpl(
+ null,
+ getBaseApkPath(),
+ getBaseRevisionCode(),
+ isHasCode() ? ApplicationInfo.FLAG_HAS_CODE : 0,
+ getClassLoaderName()
+ ));
+
+ if (splitNames != null) {
+ for (int index = 0; index < splitNames.length; index++) {
+ splits.add(new AndroidPackageSplitImpl(
+ splitNames[index],
+ splitCodePaths[index],
+ splitRevisionCodes[index],
+ splitFlags[index],
+ splitClassLoaderNames[index]
+ ));
+ }
+ }
+
+ if (splitDependencies != null) {
+ for (int index = 0; index < splitDependencies.size(); index++) {
+ var splitIndex = splitDependencies.keyAt(index);
+ var dependenciesByIndex = splitDependencies.valueAt(index);
+ var dependencies = new ArrayList<AndroidPackageSplit>();
+ for (int dependencyIndex : dependenciesByIndex) {
+ // Legacy holdover, base dependencies are an array of -1 rather than empty
+ if (dependencyIndex >= 0) {
+ dependencies.add(splits.get(dependencyIndex));
+ }
+ }
+ ((AndroidPackageSplitImpl) splits.get(splitIndex))
+ .fillDependencies(Collections.unmodifiableList(dependencies));
+ }
+ }
+
+ mSplits = Collections.unmodifiableList(splits);
+ }
+ return mSplits;
+ }
+
+ @Override
public String toString() {
return "Package{"
+ Integer.toHexString(System.identityHashCode(this))
@@ -935,8 +985,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@NonNull
@Override
public Set<String> getKnownActivityEmbeddingCerts() {
- return mKnownActivityEmbeddingCerts == null ? Collections.emptySet()
- : mKnownActivityEmbeddingCerts;
+ return mKnownActivityEmbeddingCerts;
}
@Override
@@ -1210,8 +1259,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@Nullable
@Override
- public String getSdkLibName() {
- return sdkLibName;
+ public String getSdkLibraryName() {
+ return sdkLibraryName;
}
@Override
@@ -1280,8 +1329,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@Nullable
@Override
- public String getStaticSharedLibName() {
- return staticSharedLibName;
+ public String getStaticSharedLibraryName() {
+ return staticSharedLibraryName;
}
@Override
@@ -1949,8 +1998,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
}
@Override
- public ParsingPackage setKnownActivityEmbeddingCerts(
- @Nullable Set<String> knownEmbeddingCerts) {
+ public ParsingPackage setKnownActivityEmbeddingCerts(@NonNull Set<String> knownEmbeddingCerts) {
mKnownActivityEmbeddingCerts = knownEmbeddingCerts;
return this;
}
@@ -2220,8 +2268,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
}
@Override
- public PackageImpl setSdkLibName(String sdkLibName) {
- this.sdkLibName = TextUtils.safeIntern(sdkLibName);
+ public PackageImpl setSdkLibraryName(String sdkLibraryName) {
+ this.sdkLibraryName = TextUtils.safeIntern(sdkLibraryName);
return this;
}
@@ -2263,8 +2311,8 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
}
@Override
- public PackageImpl setStaticSharedLibName(String staticSharedLibName) {
- this.staticSharedLibName = TextUtils.safeIntern(staticSharedLibName);
+ public PackageImpl setStaticSharedLibraryName(String staticSharedLibraryName) {
+ this.staticSharedLibraryName = TextUtils.safeIntern(staticSharedLibraryName);
return this;
}
@@ -2516,7 +2564,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
appInfo.setVersionCode(mLongVersionCode);
appInfo.setAppClassNamesByProcess(buildAppClassNamesByProcess());
appInfo.setLocaleConfigRes(mLocaleConfigRes);
- if (mKnownActivityEmbeddingCerts != null) {
+ if (!mKnownActivityEmbeddingCerts.isEmpty()) {
appInfo.setKnownActivityEmbeddingCerts(mKnownActivityEmbeddingCerts);
}
@@ -2593,11 +2641,11 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
@Override
public AndroidPackageInternal hideAsFinal() {
- // TODO(b/135203078): Lock as immutable
if (mStorageUuid == null) {
assignDerivedFields();
}
assignDerivedFields2();
+ makeImmutable();
return this;
}
@@ -2613,6 +2661,48 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
baseAppDataDir + Environment.DIR_USER_DE + systemUserSuffix);
}
+ private void makeImmutable() {
+ usesLibraries = Collections.unmodifiableList(usesLibraries);
+ usesOptionalLibraries = Collections.unmodifiableList(usesOptionalLibraries);
+ usesNativeLibraries = Collections.unmodifiableList(usesNativeLibraries);
+ usesOptionalNativeLibraries = Collections.unmodifiableList(usesOptionalNativeLibraries);
+ originalPackages = Collections.unmodifiableList(originalPackages);
+ adoptPermissions = Collections.unmodifiableList(adoptPermissions);
+ requestedPermissions = Collections.unmodifiableList(requestedPermissions);
+ protectedBroadcasts = Collections.unmodifiableList(protectedBroadcasts);
+ apexSystemServices = Collections.unmodifiableList(apexSystemServices);
+
+ activities = Collections.unmodifiableList(activities);
+ receivers = Collections.unmodifiableList(receivers);
+ services = Collections.unmodifiableList(services);
+ providers = Collections.unmodifiableList(providers);
+ permissions = Collections.unmodifiableList(permissions);
+ permissionGroups = Collections.unmodifiableList(permissionGroups);
+ instrumentations = Collections.unmodifiableList(instrumentations);
+
+ overlayables = Collections.unmodifiableMap(overlayables);
+ libraryNames = Collections.unmodifiableList(libraryNames);
+ usesStaticLibraries = Collections.unmodifiableList(usesStaticLibraries);
+ usesSdkLibraries = Collections.unmodifiableList(usesSdkLibraries);
+ configPreferences = Collections.unmodifiableList(configPreferences);
+ reqFeatures = Collections.unmodifiableList(reqFeatures);
+ featureGroups = Collections.unmodifiableList(featureGroups);
+ usesPermissions = Collections.unmodifiableList(usesPermissions);
+ usesSdkLibraries = Collections.unmodifiableList(usesSdkLibraries);
+ implicitPermissions = Collections.unmodifiableList(implicitPermissions);
+ upgradeKeySets = Collections.unmodifiableSet(upgradeKeySets);
+ keySetMapping = Collections.unmodifiableMap(keySetMapping);
+ attributions = Collections.unmodifiableList(attributions);
+ preferredActivityFilters = Collections.unmodifiableList(preferredActivityFilters);
+ processes = Collections.unmodifiableMap(processes);
+ mProperties = Collections.unmodifiableMap(mProperties);
+ queriesIntents = Collections.unmodifiableList(queriesIntents);
+ queriesPackages = Collections.unmodifiableList(queriesPackages);
+ queriesProviders = Collections.unmodifiableSet(queriesProviders);
+ mimeGroups = Collections.unmodifiableSet(mimeGroups);
+ mKnownActivityEmbeddingCerts = Collections.unmodifiableSet(mKnownActivityEmbeddingCerts);
+ }
+
@Override
public long getLongVersionCode() {
return PackageInfo.composeLongVersionCode(versionCodeMajor, versionCode);
@@ -2937,9 +3027,9 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
dest.writeString(this.overlayCategory);
dest.writeInt(this.overlayPriority);
sForInternedStringValueMap.parcel(this.overlayables, dest, flags);
- sForInternedString.parcel(this.sdkLibName, dest, flags);
+ sForInternedString.parcel(this.sdkLibraryName, dest, flags);
dest.writeInt(this.sdkLibVersionMajor);
- sForInternedString.parcel(this.staticSharedLibName, dest, flags);
+ sForInternedString.parcel(this.staticSharedLibraryName, dest, flags);
dest.writeLong(this.staticSharedLibVersion);
sForInternedStringList.parcel(this.libraryNames, dest, flags);
sForInternedStringList.parcel(this.usesLibraries, dest, flags);
@@ -3041,7 +3131,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
dest.writeIntArray(this.splitRevisionCodes);
sForBoolean.parcel(this.resizeableActivity, dest, flags);
dest.writeInt(this.autoRevokePermissions);
- dest.writeArraySet(this.mimeGroups);
+ sForInternedStringSet.parcel(this.mimeGroups, dest, flags);
dest.writeInt(this.gwpAsanMode);
dest.writeSparseIntArray(this.minExtensionVersions);
dest.writeMap(this.mProperties);
@@ -3087,9 +3177,9 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
this.overlayCategory = in.readString();
this.overlayPriority = in.readInt();
this.overlayables = sForInternedStringValueMap.unparcel(in);
- this.sdkLibName = sForInternedString.unparcel(in);
+ this.sdkLibraryName = sForInternedString.unparcel(in);
this.sdkLibVersionMajor = in.readInt();
- this.staticSharedLibName = sForInternedString.unparcel(in);
+ this.staticSharedLibraryName = sForInternedString.unparcel(in);
this.staticSharedLibVersion = in.readLong();
this.libraryNames = sForInternedStringList.unparcel(in);
this.usesLibraries = sForInternedStringList.unparcel(in);
@@ -3201,7 +3291,7 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
this.resizeableActivity = sForBoolean.unparcel(in);
this.autoRevokePermissions = in.readInt();
- this.mimeGroups = (ArraySet<String>) in.readArraySet(boot);
+ this.mimeGroups = sForInternedStringSet.unparcel(in);
this.gwpAsanMode = in.readInt();
this.minExtensionVersions = in.readSparseIntArray();
this.mProperties = in.readHashMap(boot);
@@ -3224,6 +3314,9 @@ public class PackageImpl implements ParsedPackage, AndroidPackageInternal,
assignDerivedFields();
assignDerivedFields2();
+
+ // Do not call makeImmutable here as cached parsing will need
+ // to mutate this instance before it's finalized.
}
@NonNull
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 073776f14e68..37abeacf7a3b 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -781,7 +781,7 @@ final class DefaultPermissionGrantPolicy {
for (String voiceInteractPackageName : voiceInteractPackageNames) {
grantPermissionsToSystemPackage(pm, voiceInteractPackageName, userId,
CONTACTS_PERMISSIONS, CALENDAR_PERMISSIONS, MICROPHONE_PERMISSIONS,
- PHONE_PERMISSIONS, SMS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS,
+ PHONE_PERMISSIONS, SMS_PERMISSIONS, COARSE_BACKGROUND_LOCATION_PERMISSIONS,
NEARBY_DEVICES_PERMISSIONS, NOTIFICATION_PERMISSIONS);
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
index 6078d4a3a348..5108fcd452f1 100644
--- a/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackage.java
@@ -18,7 +18,6 @@ package com.android.server.pm.pkg;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -34,6 +33,7 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.SigningDetails;
import android.os.Bundle;
+import android.processor.immutability.Immutable;
import android.util.ArraySet;
import android.util.Pair;
import android.util.SparseArray;
@@ -58,18 +58,80 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-/**
- * Explicit interface used for consumers like mainline who need a {@link SystemApi @SystemApi} form
- * of {@link AndroidPackage}.
- *
- * @hide
- */
+/** @hide */
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
public interface AndroidPackage {
/**
+ * Library names this package is declared as, for use by other packages with "uses-library".
+ *
+ * @see R.styleable#AndroidManifestLibrary
+ */
+ @NonNull
+ List<String> getLibraryNames();
+
+ /**
+ * @see R.styleable#AndroidManifestSdkLibrary_name
+ */
+ @Nullable
+ String getSdkLibraryName();
+
+ /**
+ * @return List of all splits for a package. Note that base.apk is considered a
+ * split and will be provided as index 0 of the list.
+ */
+ @NonNull
+ List<AndroidPackageSplit> getSplits();
+
+ /**
+ * @see R.styleable#AndroidManifestStaticLibrary_name
+ */
+ @Nullable
+ String getStaticSharedLibraryName();
+
+ /**
+ * @see ApplicationInfo#targetSdkVersion
+ * @see R.styleable#AndroidManifestUsesSdk_targetSdkVersion
+ */
+ int getTargetSdkVersion();
+
+ /**
+ * @see ApplicationInfo#FLAG_DEBUGGABLE
+ */
+ boolean isDebuggable();
+
+ /**
+ * @see ApplicationInfo#PRIVATE_FLAG_ISOLATED_SPLIT_LOADING
+ */
+ boolean isIsolatedSplitLoading();
+
+ /**
+ * @see ApplicationInfo#PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY
+ */
+ boolean isSignedWithPlatformKey();
+
+ /**
+ * @see ApplicationInfo#PRIVATE_FLAG_USE_EMBEDDED_DEX
+ */
+ boolean isUseEmbeddedDex();
+
+ /**
+ * @see ApplicationInfo#PRIVATE_FLAG_USES_NON_SDK_API
+ */
+ boolean isUsesNonSdkApi();
+
+ /**
+ * @see ApplicationInfo#FLAG_VM_SAFE_MODE
+ */
+ boolean isVmSafeMode();
+
+ // Methods below this comment are not yet exposed as API
+
+ /**
* @see ApplicationInfo#areAttributionsUserVisible()
* @see R.styleable#AndroidManifestApplication_attributionsAreUserVisible
+ * @hide
*/
@Nullable
boolean areAttributionsUserVisible();
@@ -85,7 +147,9 @@ public interface AndroidPackage {
*
* @see ActivityInfo
* @see PackageInfo#activities
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<ParsedActivity> getActivities();
@@ -94,23 +158,29 @@ public interface AndroidPackage {
* ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
*
* @see R.styleable#AndroidManifestOriginalPackage_name
+ * @hide
*/
@NonNull
List<String> getAdoptPermissions();
/**
* @see R.styleable#AndroidManifestApexSystemService
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<ParsedApexSystemService> getApexSystemServices();
/**
* @see ApplicationInfo#appComponentFactory
* @see R.styleable#AndroidManifestApplication_appComponentFactory
+ * @hide
*/
@Nullable
String getAppComponentFactory();
+ /** @hide */
+ @Immutable.Ignore
@NonNull
List<ParsedAttribution> getAttributions();
@@ -118,12 +188,14 @@ public interface AndroidPackage {
* @see ApplicationInfo#AUTO_REVOKE_ALLOWED
* @see ApplicationInfo#AUTO_REVOKE_DISCOURAGED
* @see ApplicationInfo#AUTO_REVOKE_DISALLOWED
+ * @hide
*/
int getAutoRevokePermissions();
/**
* @see ApplicationInfo#backupAgentName
* @see R.styleable#AndroidManifestApplication_backupAgent
+ * @hide
*/
@Nullable
String getBackupAgentName();
@@ -131,30 +203,35 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#banner
* @see R.styleable#AndroidManifestApplication_banner
+ * @hide
*/
int getBanner();
/**
* @see ApplicationInfo#sourceDir
* @see ApplicationInfo#getBaseCodePath
+ * @hide
*/
@NonNull
String getBaseApkPath();
/**
* @see PackageInfo#baseRevisionCode
+ * @hide
*/
int getBaseRevisionCode();
/**
* @see ApplicationInfo#category
* @see R.styleable#AndroidManifestApplication_appCategory
+ * @hide
*/
int getCategory();
/**
* @see ApplicationInfo#classLoaderName
* @see R.styleable#AndroidManifestApplication_classLoader
+ * @hide
*/
@Nullable
String getClassLoaderName();
@@ -162,6 +239,7 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#className
* @see R.styleable#AndroidManifestApplication_name
+ * @hide
*/
@Nullable
String getClassName();
@@ -169,18 +247,21 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#compatibleWidthLimitDp
* @see R.styleable#AndroidManifestSupportsScreens_compatibleWidthLimitDp
+ * @hide
*/
int getCompatibleWidthLimitDp();
/**
* @see ApplicationInfo#compileSdkVersion
* @see R.styleable#AndroidManifest_compileSdkVersion
+ * @hide
*/
int getCompileSdkVersion();
/**
* @see ApplicationInfo#compileSdkVersionCodename
* @see R.styleable#AndroidManifest_compileSdkVersionCodename
+ * @hide
*/
@Nullable
String getCompileSdkVersionCodeName();
@@ -188,38 +269,46 @@ public interface AndroidPackage {
/**
* @see PackageInfo#configPreferences
* @see R.styleable#AndroidManifestUsesConfiguration
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<ConfigurationInfo> getConfigPreferences();
/**
* @see ApplicationInfo#dataExtractionRulesRes
* @see R.styleable#AndroidManifestApplication_dataExtractionRules
+ * @hide
*/
int getDataExtractionRules();
/**
* @see ApplicationInfo#descriptionRes
* @see R.styleable#AndroidManifestApplication_description
+ * @hide
*/
int getDescriptionRes();
/**
* @see PackageInfo#featureGroups
* @see R.styleable#AndroidManifestUsesFeature
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<FeatureGroupInfo> getFeatureGroups();
/**
* @see ApplicationInfo#fullBackupContent
* @see R.styleable#AndroidManifestApplication_fullBackupContent
+ * @hide
*/
int getFullBackupContent();
/**
* @see ApplicationInfo#getGwpAsanMode()
* @see R.styleable#AndroidManifestApplication_gwpAsanMode
+ * @hide
*/
@ApplicationInfo.GwpAsanMode
int getGwpAsanMode();
@@ -227,12 +316,14 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#iconRes
* @see R.styleable#AndroidManifestApplication_icon
+ * @hide
*/
int getIconRes();
/**
* Permissions requested but not in the manifest. These may have been split or migrated from
* previous versions/definitions.
+ * @hide
*/
@NonNull
List<String> getImplicitPermissions();
@@ -240,13 +331,16 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#installLocation
* @see R.styleable#AndroidManifest_installLocation
+ * @hide
*/
int getInstallLocation();
/**
* @see InstrumentationInfo
* @see PackageInfo#instrumentation
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<ParsedInstrumentation> getInstrumentations();
@@ -256,13 +350,16 @@ public interface AndroidPackage {
*
* @see R.styleable#AndroidManifestKeySet
* @see R.styleable#AndroidManifestPublicKey
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
Map<String, ArraySet<PublicKey>> getKeySetMapping();
/**
* @see ApplicationInfo#mKnownActivityEmbeddingCerts
* @see R.styleable#AndroidManifestApplication_knownActivityEmbeddingCerts
+ * @hide
*/
@SuppressWarnings("JavadocReference")
@NonNull
@@ -271,44 +368,42 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#labelRes
* @see R.styleable#AndroidManifestApplication_label
+ * @hide
*/
int getLabelRes();
/**
* @see ApplicationInfo#largestWidthLimitDp
* @see R.styleable#AndroidManifestSupportsScreens_largestWidthLimitDp
+ * @hide
*/
int getLargestWidthLimitDp();
/**
- * Library names this package is declared as, for use by other packages with "uses-library".
- *
- * @see R.styleable#AndroidManifestLibrary
- */
- @NonNull
- List<String> getLibraryNames();
-
- /**
* The resource ID used to provide the application's locales configuration.
*
* @see R.styleable#AndroidManifestApplication_localeConfig
+ * @hide
*/
int getLocaleConfigRes();
/**
* @see ApplicationInfo#logo
* @see R.styleable#AndroidManifestApplication_logo
+ * @hide
*/
int getLogo();
/**
* @see PackageInfo#getLongVersionCode()
+ * @hide
*/
long getLongVersionCode();
/**
* @see ApplicationInfo#manageSpaceActivityName
* @see R.styleable#AndroidManifestApplication_manageSpaceActivity
+ * @hide
*/
@Nullable
String getManageSpaceActivityName();
@@ -316,6 +411,7 @@ public interface AndroidPackage {
/**
* The package name as declared in the manifest, since the package can be renamed. For example,
* static shared libs use synthetic package names.
+ * @hide
*/
@NonNull
String getManifestPackageName();
@@ -323,63 +419,76 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#maxAspectRatio
* @see R.styleable#AndroidManifestApplication_maxAspectRatio
+ * @hide
*/
float getMaxAspectRatio();
/**
* @see R.styleable#AndroidManifestUsesSdk_maxSdkVersion
+ * @hide
*/
int getMaxSdkVersion();
/**
* @see ApplicationInfo#getMemtagMode()
* @see R.styleable#AndroidManifestApplication_memtagMode
+ * @hide
*/
@ApplicationInfo.MemtagMode
int getMemtagMode();
/**
* TODO(b/135203078): Make all the Bundles immutable (and non-null by shared empty reference?)
+ * @hide
*/
+ @Immutable.Ignore
@Nullable
Bundle getMetaData();
+ /** @hide */
@Nullable
Set<String> getMimeGroups();
/**
* @see ApplicationInfo#minAspectRatio
* @see R.styleable#AndroidManifestApplication_minAspectRatio
+ * @hide
*/
float getMinAspectRatio();
/**
* @see R.styleable#AndroidManifestExtensionSdk
+ * @hide
*/
+ @Immutable.Ignore
@Nullable
SparseIntArray getMinExtensionVersions();
/**
* @see ApplicationInfo#minSdkVersion
* @see R.styleable#AndroidManifestUsesSdk_minSdkVersion
+ * @hide
*/
int getMinSdkVersion();
/**
* @see ApplicationInfo#getNativeHeapZeroInitialized()
* @see R.styleable#AndroidManifestApplication_nativeHeapZeroInitialized
+ * @hide
*/
@ApplicationInfo.NativeHeapZeroInitialized
int getNativeHeapZeroInitialized();
/**
* @see ApplicationInfo#nativeLibraryDir
+ * @hide
*/
@Nullable
String getNativeLibraryDir();
/**
* @see ApplicationInfo#nativeLibraryRootDir
+ * @hide
*/
@Nullable
String getNativeLibraryRootDir();
@@ -387,6 +496,7 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#networkSecurityConfigRes
* @see R.styleable#AndroidManifestApplication_networkSecurityConfig
+ * @hide
*/
int getNetworkSecurityConfigRes();
@@ -396,6 +506,7 @@ public interface AndroidPackage {
*
* @see ApplicationInfo#nonLocalizedLabel
* @see R.styleable#AndroidManifestApplication_label
+ * @hide
*/
@Nullable
CharSequence getNonLocalizedLabel();
@@ -405,6 +516,7 @@ public interface AndroidPackage {
* available.
*
* @see R.styleable#AndroidManifestOriginalPackage}
+ * @hide
*/
@NonNull
List<String> getOriginalPackages();
@@ -412,6 +524,7 @@ public interface AndroidPackage {
/**
* @see PackageInfo#overlayCategory
* @see R.styleable#AndroidManifestResourceOverlay_category
+ * @hide
*/
@Nullable
String getOverlayCategory();
@@ -419,12 +532,14 @@ public interface AndroidPackage {
/**
* @see PackageInfo#overlayPriority
* @see R.styleable#AndroidManifestResourceOverlay_priority
+ * @hide
*/
int getOverlayPriority();
/**
* @see PackageInfo#overlayTarget
* @see R.styleable#AndroidManifestResourceOverlay_targetPackage
+ * @hide
*/
@Nullable
String getOverlayTarget();
@@ -432,24 +547,28 @@ public interface AndroidPackage {
/**
* @see PackageInfo#targetOverlayableName
* @see R.styleable#AndroidManifestResourceOverlay_targetName
+ * @hide
*/
@Nullable
String getOverlayTargetOverlayableName();
/**
* Map of overlayable name to actor name.
+ * @hide
*/
@NonNull
Map<String, String> getOverlayables();
/**
* @see PackageInfo#packageName
+ * @hide
*/
String getPackageName();
/**
* @see ApplicationInfo#scanSourceDir
* @see ApplicationInfo#getCodePath
+ * @hide
*/
@NonNull
String getPath();
@@ -457,20 +576,25 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#permission
* @see R.styleable#AndroidManifestApplication_permission
+ * @hide
*/
@Nullable
String getPermission();
/**
* @see android.content.pm.PermissionGroupInfo
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<ParsedPermissionGroup> getPermissionGroups();
/**
* @see PermissionInfo
* @see PackageInfo#permissions
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<ParsedPermission> getPermissions();
@@ -479,26 +603,33 @@ public interface AndroidPackage {
* <p>
* Map of component className to intent info inside that component. TODO(b/135203078): Is this
* actually used/working?
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<Pair<String, ParsedIntentInfo>> getPreferredActivityFilters();
/**
* @see ApplicationInfo#processName
* @see R.styleable#AndroidManifestApplication_process
+ * @hide
*/
@NonNull
String getProcessName();
/**
* @see android.content.pm.ProcessInfo
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
Map<String, ParsedProcess> getProcesses();
/**
* Returns the properties set on the application
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
Map<String, PackageManager.Property> getProperties();
@@ -506,6 +637,7 @@ public interface AndroidPackage {
* System protected broadcasts.
*
* @see R.styleable#AndroidManifestProtectedBroadcast
+ * @hide
*/
@NonNull
List<String> getProtectedBroadcasts();
@@ -521,7 +653,9 @@ public interface AndroidPackage {
*
* @see ProviderInfo
* @see PackageInfo#providers
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<ParsedProvider> getProviders();
@@ -529,7 +663,9 @@ public interface AndroidPackage {
* Intents that this package may query or require and thus requires visibility into.
*
* @see R.styleable#AndroidManifestQueriesIntent
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<Intent> getQueriesIntents();
@@ -537,6 +673,7 @@ public interface AndroidPackage {
* Other packages that this package may query or require and thus requires visibility into.
*
* @see R.styleable#AndroidManifestQueriesPackage
+ * @hide
*/
@NonNull
List<String> getQueriesPackages();
@@ -545,6 +682,7 @@ public interface AndroidPackage {
* Authorities that this package may query or require and thus requires visibility into.
*
* @see R.styleable#AndroidManifestQueriesProvider
+ * @hide
*/
@NonNull
Set<String> getQueriesProviders();
@@ -565,14 +703,18 @@ public interface AndroidPackage {
*
* @see ActivityInfo
* @see PackageInfo#receivers
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<ParsedActivity> getReceivers();
/**
* @see PackageInfo#reqFeatures
* @see R.styleable#AndroidManifestUsesFeature
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<FeatureInfo> getRequestedFeatures();
@@ -583,6 +725,7 @@ public interface AndroidPackage {
*
* @see PackageInfo#requestedPermissions
* @see R.styleable#AndroidManifestUsesPermission
+ * @hide
*/
@NonNull
List<String> getRequestedPermissions();
@@ -590,6 +733,7 @@ public interface AndroidPackage {
/**
* @see PackageInfo#requiredAccountType
* @see R.styleable#AndroidManifestApplication_requiredAccountType
+ * @hide
*/
@Nullable
String getRequiredAccountType();
@@ -597,6 +741,7 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#requiresSmallestWidthDp
* @see R.styleable#AndroidManifestSupportsScreens_requiresSmallestWidthDp
+ * @hide
*/
int getRequiresSmallestWidthDp();
@@ -606,6 +751,7 @@ public interface AndroidPackage {
*
* @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE
* @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE
+ * @hide
*/
@Nullable
Boolean getResizeableActivity();
@@ -614,7 +760,9 @@ public interface AndroidPackage {
* SHA-512 hash of the only APK that can be used to update a system package.
*
* @see R.styleable#AndroidManifestRestrictUpdate
+ * @hide
*/
+ @Immutable.Ignore
@Nullable
byte[] getRestrictUpdateHash();
@@ -623,6 +771,7 @@ public interface AndroidPackage {
*
* @see PackageInfo#restrictedAccountType
* @see R.styleable#AndroidManifestApplication_restrictedAccountType
+ * @hide
*/
@Nullable
String getRestrictedAccountType();
@@ -630,22 +779,19 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#roundIconRes
* @see R.styleable#AndroidManifestApplication_roundIcon
+ * @hide
*/
int getRoundIconRes();
/**
- * @see R.styleable#AndroidManifestSdkLibrary_name
- */
- @Nullable
- String getSdkLibName();
-
- /**
* @see R.styleable#AndroidManifestSdkLibrary_versionMajor
+ * @hide
*/
int getSdkLibVersionMajor();
/**
* @see ApplicationInfo#secondaryNativeLibraryDir
+ * @hide
*/
@Nullable
String getSecondaryNativeLibraryDir();
@@ -661,13 +807,16 @@ public interface AndroidPackage {
*
* @see ServiceInfo
* @see PackageInfo#services
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
List<ParsedService> getServices();
/**
* @see PackageInfo#sharedUserId
* @see R.styleable#AndroidManifest_sharedUserId
+ * @hide
*/
@Nullable
String getSharedUserId();
@@ -675,39 +824,50 @@ public interface AndroidPackage {
/**
* @see PackageInfo#sharedUserLabel
* @see R.styleable#AndroidManifest_sharedUserLabel
+ * @hide
*/
int getSharedUserLabel();
/**
* The signature data of all APKs in this package, which must be exactly the same across the
* base and splits.
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
SigningDetails getSigningDetails();
/**
* @see ApplicationInfo#splitClassLoaderNames
* @see R.styleable#AndroidManifestApplication_classLoader
+ * @hide
*/
+ @Immutable.Ignore
@Nullable
String[] getSplitClassLoaderNames();
/**
* @see ApplicationInfo#splitSourceDirs
* @see ApplicationInfo#getSplitCodePaths
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
String[] getSplitCodePaths();
/**
* @see ApplicationInfo#splitDependencies
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
SparseArray<int[]> getSplitDependencies();
/**
* Flags of any split APKs; ordered by parsed splitName
+ * @hide
*/
+ @Immutable.Ignore
@Nullable
int[] getSplitFlags();
@@ -716,42 +876,37 @@ public interface AndroidPackage {
*
* @see ApplicationInfo#splitNames
* @see PackageInfo#splitNames
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
String[] getSplitNames();
/**
* @see PackageInfo#splitRevisionCodes
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
int[] getSplitRevisionCodes();
/**
- * @see R.styleable#AndroidManifestStaticLibrary_name
- */
- @Nullable
- String getStaticSharedLibName();
-
- /**
* @see R.styleable#AndroidManifestStaticLibrary_version
+ * @hide
*/
long getStaticSharedLibVersion();
/**
* @see ApplicationInfo#targetSandboxVersion
* @see R.styleable#AndroidManifest_targetSandboxVersion
+ * @hide
*/
int getTargetSandboxVersion();
/**
- * @see ApplicationInfo#targetSdkVersion
- * @see R.styleable#AndroidManifestUsesSdk_targetSdkVersion
- */
- int getTargetSdkVersion();
-
- /**
* @see ApplicationInfo#taskAffinity
* @see R.styleable#AndroidManifestApplication_taskAffinity
+ * @hide
*/
@Nullable
String getTaskAffinity();
@@ -759,12 +914,14 @@ public interface AndroidPackage {
/**
* @see ApplicationInfo#theme
* @see R.styleable#AndroidManifestApplication_theme
+ * @hide
*/
int getTheme();
/**
* @see ApplicationInfo#uiOptions
* @see R.styleable#AndroidManifestApplication_uiOptions
+ * @hide
*/
int getUiOptions();
@@ -773,6 +930,7 @@ public interface AndroidPackage {
* {@link android.os.UserHandle#SYSTEM}.
*
* @deprecated Use {@link PackageState#getAppId()} instead.
+ * @hide
*/
@Deprecated
int getUid();
@@ -782,18 +940,21 @@ public interface AndroidPackage {
* ParsingPackageUtils#TAG_KEY_SETS}.
*
* @see R.styleable#AndroidManifestUpgradeKeySet
+ * @hide
*/
@NonNull
Set<String> getUpgradeKeySets();
/**
* @see R.styleable#AndroidManifestUsesLibrary
+ * @hide
*/
@NonNull
List<String> getUsesLibraries();
/**
* @see R.styleable#AndroidManifestUsesNativeLibrary
+ * @hide
*/
@NonNull
List<String> getUsesNativeLibraries();
@@ -804,6 +965,7 @@ public interface AndroidPackage {
* absence manually.
*
* @see R.styleable#AndroidManifestUsesLibrary
+ * @hide
*/
@NonNull
List<String> getUsesOptionalLibraries();
@@ -814,10 +976,13 @@ public interface AndroidPackage {
* handle absence manually.
*
* @see R.styleable#AndroidManifestUsesNativeLibrary
+ * @hide
*/
@NonNull
List<String> getUsesOptionalNativeLibraries();
+ /** @hide */
+ @Immutable.Ignore
@NonNull
List<ParsedUsesPermission> getUsesPermissions();
@@ -825,19 +990,24 @@ public interface AndroidPackage {
* TODO(b/135203078): Move SDK library stuff to an inner data class
*
* @see R.styleable#AndroidManifestUsesSdkLibrary
+ * @hide
*/
@NonNull
List<String> getUsesSdkLibraries();
/**
* @see R.styleable#AndroidManifestUsesSdkLibrary_certDigest
+ * @hide
*/
+ @Immutable.Ignore
@Nullable
String[][] getUsesSdkLibrariesCertDigests();
/**
* @see R.styleable#AndroidManifestUsesSdkLibrary_versionMajor
+ * @hide
*/
+ @Immutable.Ignore
@Nullable
long[] getUsesSdkLibrariesVersionsMajor();
@@ -845,78 +1015,95 @@ public interface AndroidPackage {
* TODO(b/135203078): Move static library stuff to an inner data class
*
* @see R.styleable#AndroidManifestUsesStaticLibrary
+ * @hide
*/
@NonNull
List<String> getUsesStaticLibraries();
/**
* @see R.styleable#AndroidManifestUsesStaticLibrary_certDigest
+ * @hide
*/
+ @Immutable.Ignore
@Nullable
String[][] getUsesStaticLibrariesCertDigests();
/**
* @see R.styleable#AndroidManifestUsesStaticLibrary_version
+ * @hide
*/
+ @Immutable.Ignore
@Nullable
long[] getUsesStaticLibrariesVersions();
/**
* @see PackageInfo#versionName
+ * @hide
*/
@Nullable
String getVersionName();
/**
* @see ApplicationInfo#volumeUuid
+ * @hide
*/
@Nullable
String getVolumeUuid();
+ /** @hide */
@Nullable
String getZygotePreloadName();
+ /** @hide */
boolean hasPreserveLegacyExternalStorage();
/**
* @see ApplicationInfo#PRIVATE_FLAG_EXT_REQUEST_FOREGROUND_SERVICE_EXEMPTION
* @see R.styleable#AndroidManifestApplication_requestForegroundServiceExemption
+ * @hide
*/
boolean hasRequestForegroundServiceExemption();
/**
* @see ApplicationInfo#getRequestRawExternalStorageAccess()
* @see R.styleable#AndroidManifestApplication_requestRawExternalStorageAccess
+ * @hide
*/
Boolean hasRequestRawExternalStorageAccess();
/**
* @see ApplicationInfo#PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE
+ * @hide
*/
boolean isAllowAudioPlaybackCapture();
/**
* @see ApplicationInfo#FLAG_ALLOW_BACKUP
+ * @hide
*/
boolean isAllowBackup();
/**
* @see ApplicationInfo#FLAG_ALLOW_CLEAR_USER_DATA
+ * @hide
*/
boolean isAllowClearUserData();
/**
* @see ApplicationInfo#PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE
+ * @hide
*/
boolean isAllowClearUserDataOnFailedRestore();
/**
* @see ApplicationInfo#PRIVATE_FLAG_ALLOW_NATIVE_HEAP_POINTER_TAGGING
+ * @hide
*/
boolean isAllowNativeHeapPointerTagging();
/**
* @see ApplicationInfo#FLAG_ALLOW_TASK_REPARENTING
+ * @hide
*/
boolean isAllowTaskReparenting();
@@ -926,115 +1113,126 @@ public interface AndroidPackage {
*
* @see R.styleable#AndroidManifestSupportsScreens_anyDensity
* @see ApplicationInfo#FLAG_SUPPORTS_SCREEN_DENSITIES
+ * @hide
*/
boolean isAnyDensity();
+ /** @hide */
boolean isApex();
/**
* @see ApplicationInfo#PRIVATE_FLAG_BACKUP_IN_FOREGROUND
+ * @hide
*/
boolean isBackupInForeground();
/**
* @see ApplicationInfo#FLAG_HARDWARE_ACCELERATED
+ * @hide
*/
boolean isBaseHardwareAccelerated();
/**
* @see ApplicationInfo#PRIVATE_FLAG_CANT_SAVE_STATE
+ * @hide
*/
boolean isCantSaveState();
/**
* @see PackageInfo#coreApp
+ * @hide
*/
boolean isCoreApp();
/**
* @see ApplicationInfo#crossProfile
+ * @hide
*/
boolean isCrossProfile();
/**
- * @see ApplicationInfo#FLAG_DEBUGGABLE
- */
- boolean isDebuggable();
-
- /**
* @see ApplicationInfo#PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE
+ * @hide
*/
boolean isDefaultToDeviceProtectedStorage();
/**
* @see ApplicationInfo#PRIVATE_FLAG_DIRECT_BOOT_AWARE
+ * @hide
*/
boolean isDirectBootAware();
/**
* @see ApplicationInfo#enabled
* @see R.styleable#AndroidManifestApplication_enabled
+ * @hide
*/
boolean isEnabled();
/**
* @see ApplicationInfo#FLAG_EXTERNAL_STORAGE
+ * @hide
*/
boolean isExternalStorage();
/**
* @see ApplicationInfo#FLAG_EXTRACT_NATIVE_LIBS
+ * @hide
*/
boolean isExtractNativeLibs();
/**
* @see ApplicationInfo#FLAG_FACTORY_TEST
+ * @hide
*/
boolean isFactoryTest();
/**
* @see R.styleable#AndroidManifestApplication_forceQueryable
+ * @hide
*/
boolean isForceQueryable();
/**
* @see ApplicationInfo#FLAG_FULL_BACKUP_ONLY
+ * @hide
*/
boolean isFullBackupOnly();
/**
* @see ApplicationInfo#FLAG_IS_GAME
+ * @hide
*/
@Deprecated
boolean isGame();
/**
* @see ApplicationInfo#FLAG_HAS_CODE
+ * @hide
*/
boolean isHasCode();
/**
* @see ApplicationInfo#PRIVATE_FLAG_HAS_DOMAIN_URLS
+ * @hide
*/
boolean isHasDomainUrls();
/**
* @see ApplicationInfo#PRIVATE_FLAG_HAS_FRAGILE_USER_DATA
+ * @hide
*/
boolean isHasFragileUserData();
/**
- * @see ApplicationInfo#PRIVATE_FLAG_ISOLATED_SPLIT_LOADING
- */
- boolean isIsolatedSplitLoading();
-
- /**
* @see ApplicationInfo#FLAG_KILL_AFTER_RESTORE
+ * @hide
*/
boolean isKillAfterRestore();
/**
* @see ApplicationInfo#FLAG_LARGE_HEAP
+ * @hide
*/
boolean isLargeHeap();
@@ -1043,83 +1241,99 @@ public interface AndroidPackage {
* smaller than the current SDK version.
*
* @see R.styleable#AndroidManifest_sharedUserMaxSdkVersion
+ * @hide
*/
boolean isLeavingSharedUid();
/**
* @see ApplicationInfo#FLAG_MULTIARCH
+ * @hide
*/
boolean isMultiArch();
/**
* @see ApplicationInfo#nativeLibraryRootRequiresIsa
+ * @hide
*/
boolean isNativeLibraryRootRequiresIsa();
/**
* @see ApplicationInfo#PRIVATE_FLAG_ODM
+ * @hide
*/
boolean isOdm();
/**
* @see ApplicationInfo#PRIVATE_FLAG_OEM
+ * @hide
*/
boolean isOem();
/**
* @see R.styleable#AndroidManifestApplication_enableOnBackInvokedCallback
+ * @hide
*/
boolean isOnBackInvokedCallbackEnabled();
/**
* @see ApplicationInfo#PRIVATE_FLAG_IS_RESOURCE_OVERLAY
* @see ApplicationInfo#isResourceOverlay()
+ * @hide
*/
boolean isOverlay();
/**
* @see PackageInfo#mOverlayIsStatic
+ * @hide
*/
boolean isOverlayIsStatic();
/**
* @see ApplicationInfo#PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE
+ * @hide
*/
boolean isPartiallyDirectBootAware();
/**
* @see ApplicationInfo#FLAG_PERSISTENT
+ * @hide
*/
boolean isPersistent();
/**
* @see ApplicationInfo#PRIVATE_FLAG_PRIVILEGED
+ * @hide
*/
boolean isPrivileged();
/**
* @see ApplicationInfo#PRIVATE_FLAG_PRODUCT
+ * @hide
*/
boolean isProduct();
/**
* @see ApplicationInfo#PRIVATE_FLAG_EXT_PROFILEABLE
+ * @hide
*/
boolean isProfileable();
/**
* @see ApplicationInfo#PRIVATE_FLAG_PROFILEABLE_BY_SHELL
+ * @hide
*/
boolean isProfileableByShell();
/**
* @see ApplicationInfo#PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE
+ * @hide
*/
boolean isRequestLegacyExternalStorage();
/**
* @see PackageInfo#requiredForAllUsers
* @see R.styleable#AndroidManifestApplication_requiredForAllUsers
+ * @hide
*/
boolean isRequiredForAllUsers();
@@ -1128,6 +1342,7 @@ public interface AndroidPackage {
* when the application's user data is cleared.
*
* @see R.styleable#AndroidManifestApplication_resetEnabledSettingsOnAppDataCleared
+ * @hide
*/
boolean isResetEnabledSettingsOnAppDataCleared();
@@ -1137,36 +1352,37 @@ public interface AndroidPackage {
*
* @see R.styleable#AndroidManifestSupportsScreens_resizeable
* @see ApplicationInfo#FLAG_RESIZEABLE_FOR_SCREENS
+ * @hide
*/
boolean isResizeable();
/**
* @see ApplicationInfo#PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION
+ * @hide
*/
boolean isResizeableActivityViaSdkVersion();
/**
* @see ApplicationInfo#FLAG_RESTORE_ANY_VERSION
+ * @hide
*/
boolean isRestoreAnyVersion();
/**
* True means that this package/app contains an SDK library.
+ * @hide
*/
boolean isSdkLibrary();
/**
- * @see ApplicationInfo#PRIVATE_FLAG_SIGNED_WITH_PLATFORM_KEY
- */
- boolean isSignedWithPlatformKey();
-
- /**
* @see ApplicationInfo#PRIVATE_FLAG_STATIC_SHARED_LIBRARY
+ * @hide
*/
boolean isStaticSharedLibrary();
/**
* @see PackageInfo#isStub
+ * @hide
*/
boolean isStub();
@@ -1176,6 +1392,7 @@ public interface AndroidPackage {
*
* @see R.styleable#AndroidManifestSupportsScreens_xlargeScreens
* @see ApplicationInfo#FLAG_SUPPORTS_XLARGE_SCREENS
+ * @hide
*/
boolean isSupportsExtraLargeScreens();
@@ -1185,6 +1402,7 @@ public interface AndroidPackage {
*
* @see R.styleable#AndroidManifestSupportsScreens_largeScreens
* @see ApplicationInfo#FLAG_SUPPORTS_LARGE_SCREENS
+ * @hide
*/
boolean isSupportsLargeScreens();
@@ -1193,11 +1411,13 @@ public interface AndroidPackage {
*
* @see R.styleable#AndroidManifestSupportsScreens_normalScreens
* @see ApplicationInfo#FLAG_SUPPORTS_NORMAL_SCREENS
+ * @hide
*/
boolean isSupportsNormalScreens();
/**
* @see ApplicationInfo#FLAG_SUPPORTS_RTL
+ * @hide
*/
boolean isSupportsRtl();
@@ -1207,21 +1427,25 @@ public interface AndroidPackage {
*
* @see R.styleable#AndroidManifestSupportsScreens_smallScreens
* @see ApplicationInfo#FLAG_SUPPORTS_SMALL_SCREENS
+ * @hide
*/
boolean isSupportsSmallScreens();
/**
* @see ApplicationInfo#FLAG_SYSTEM
+ * @hide
*/
boolean isSystem();
/**
* @see ApplicationInfo#PRIVATE_FLAG_SYSTEM_EXT
+ * @hide
*/
boolean isSystemExt();
/**
* @see ApplicationInfo#FLAG_TEST_ONLY
+ * @hide
*/
boolean isTestOnly();
@@ -1231,26 +1455,19 @@ public interface AndroidPackage {
* cpuAbiOverride is also set.
*
* @see R.attr#use32bitAbi
+ * @hide
*/
boolean isUse32BitAbi();
/**
- * @see ApplicationInfo#PRIVATE_FLAG_USE_EMBEDDED_DEX
- */
- boolean isUseEmbeddedDex();
-
- /**
* @see ApplicationInfo#FLAG_USES_CLEARTEXT_TRAFFIC
+ * @hide
*/
boolean isUsesCleartextTraffic();
/**
- * @see ApplicationInfo#PRIVATE_FLAG_USES_NON_SDK_API
- */
- boolean isUsesNonSdkApi();
-
- /**
* @see ApplicationInfo#PRIVATE_FLAG_VENDOR
+ * @hide
*/
boolean isVendor();
@@ -1260,11 +1477,7 @@ public interface AndroidPackage {
* @see R.styleable#AndroidManifestActivity_visibleToInstantApps
* @see R.styleable#AndroidManifestProvider_visibleToInstantApps
* @see R.styleable#AndroidManifestService_visibleToInstantApps
+ * @hide
*/
boolean isVisibleToInstantApps();
-
- /**
- * @see ApplicationInfo#FLAG_VM_SAFE_MODE
- */
- boolean isVmSafeMode();
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt b/services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
index 1b8a44bbe78e..a17ecc3d98aa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/LaunchBubbleHelper.kt
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackageSplit.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open 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,19 +14,31 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.helpers
+package com.android.server.pm.pkg;
-import android.app.Instrumentation
-import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.wm.shell.flicker.testapp.Components
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.processor.immutability.Immutable;
-class LaunchBubbleHelper(instrumentation: Instrumentation) : BaseAppHelper(
- instrumentation,
- Components.LaunchBubbleActivity.LABEL,
- Components.LaunchBubbleActivity.COMPONENT.toFlickerComponent()
-) {
+import java.util.List;
- companion object {
- const val TIMEOUT_MS = 3_000L
- }
+/** @hide */
+@Immutable
+public interface AndroidPackageSplit {
+
+ @Nullable
+ String getName();
+
+ @NonNull
+ String getPath();
+
+ int getRevisionCode();
+
+ boolean isHasCode();
+
+ @Nullable
+ String getClassLoaderName();
+
+ @NonNull
+ List<AndroidPackageSplit> getDependencies();
}
diff --git a/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java b/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java
new file mode 100644
index 000000000000..9aac8a851f05
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/AndroidPackageSplitImpl.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.ApplicationInfo;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+public class AndroidPackageSplitImpl implements AndroidPackageSplit {
+
+ @Nullable
+ private final String mName;
+ @NonNull
+ private final String mPath;
+ private final int mRevisionCode;
+ private final int mFlags;
+ @Nullable
+ private final String mClassLoaderName;
+
+ @NonNull
+ private List<AndroidPackageSplit> mDependencies = Collections.emptyList();
+
+ public AndroidPackageSplitImpl(@Nullable String name, @NonNull String path, int revisionCode,
+ int flags, @Nullable String classLoaderName) {
+ mName = name;
+ mPath = path;
+ mRevisionCode = revisionCode;
+ mFlags = flags;
+ mClassLoaderName = classLoaderName;
+ }
+
+ public void fillDependencies(@NonNull List<AndroidPackageSplit> splits) {
+ if (!mDependencies.isEmpty()) {
+ throw new IllegalStateException("Cannot fill split dependencies more than once");
+ }
+ mDependencies = splits;
+ }
+
+ @Nullable
+ @Override
+ public String getName() {
+ return mName;
+ }
+
+ @NonNull
+ @Override
+ public String getPath() {
+ return mPath;
+ }
+
+ @Override
+ public int getRevisionCode() {
+ return mRevisionCode;
+ }
+
+ @Override
+ public boolean isHasCode() {
+ return (mFlags & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ }
+
+ @Nullable
+ @Override
+ public String getClassLoaderName() {
+ return mClassLoaderName;
+ }
+
+ @NonNull
+ @Override
+ public List<AndroidPackageSplit> getDependencies() {
+ return mDependencies;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof AndroidPackageSplitImpl)) return false;
+ AndroidPackageSplitImpl that = (AndroidPackageSplitImpl) o;
+ var fieldsEqual = mRevisionCode == that.mRevisionCode && mFlags == that.mFlags && Objects.equals(
+ mName, that.mName) && Objects.equals(mPath, that.mPath)
+ && Objects.equals(mClassLoaderName, that.mClassLoaderName);
+
+ if (!fieldsEqual) return false;
+ if (mDependencies.size() != that.mDependencies.size()) return false;
+
+ // Should be impossible, but to avoid circular dependencies,
+ // only search 1 level deep using split name
+ for (int index = 0; index < mDependencies.size(); index++) {
+ if (!Objects.equals(mDependencies.get(index).getName(),
+ that.mDependencies.get(index).getName())) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ // Should be impossible, but to avoid circular dependencies,
+ // only search 1 level deep using split name
+ var dependenciesHash = Objects.hash(mName, mPath, mRevisionCode, mFlags, mClassLoaderName);
+ for (int index = 0; index < mDependencies.size(); index++) {
+ var name = mDependencies.get(index).getName();
+ dependenciesHash = 31 * dependenciesHash + (name == null ? 0 : name.hashCode());
+ }
+ return dependenciesHash;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageState.java b/services/core/java/com/android/server/pm/pkg/PackageState.java
index f0e386ce1d29..a6e60166f69a 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageState.java
@@ -21,72 +21,133 @@ import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.UserIdInt;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningInfo;
+import android.os.UserHandle;
+import android.processor.immutability.Immutable;
import android.util.SparseArray;
-import com.android.server.pm.PackageSetting;
-import com.android.server.pm.Settings;
-
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
- * The API surface for a {@link PackageSetting}. Methods are expected to return immutable objects.
- * This may mean copying data on each invocation until related classes are refactored to be
- * immutable.
- * <p>
- * Note that until immutability or read-only caching is enabled, {@link PackageSetting} cannot be
- * returned directly, so {@link PackageStateImpl} is used to temporarily copy the data. This is a
- * relatively expensive operation since it has to create an object for every package, but it's much
- * lighter than the alternative of generating {@link PackageInfo} objects.
- * <p>
- * TODO: Documentation TODO: Currently missing, should be exposed as API?
- * <ul>
- * <li>keySetData</li>
- * <li>installSource</li>
- * <li>incrementalStates</li>
- * </ul>
+ * The exposed system server process API for package data, shared with the internal
+ * PackageManagerService implementation. All returned data is guaranteed immutable.
*
* @hide
*/
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
public interface PackageState {
+ /*
+ * Until immutability or read-only caching is enabled, {@link PackageSetting} cannot be
+ * returned directly, so {@link PackageStateImpl} is used to temporarily copy the data.
+ * This is a relatively expensive operation since it has to create an object for every package,
+ * but it's much lighter than the alternative of generating {@link PackageInfo} objects.
+ * <p>
+ * TODO: Documentation
+ * TODO: Currently missing, should be exposed as API?
+ * - keySetData
+ * - installSource
+ * - incrementalStates
+ */
+
+ // Non-doc comment invisible to API consumers:
+ // Guidelines:
+ // - All return values should prefer non-null, immutable interfaces with only exposed getters
+ // - Unless null itself communicates something important
+ // - If the type is a Java collection type, it must be wrapped with unmodifiable
+ // - All type names must be non-suffixed, with any internal types being refactored to suffix
+ // with _Internal as necessary
+ // - No exposure of raw values that are overridden during parsing, such as CPU ABI
+ // - Mirroring another available system or public API is not enough justification to violate
+ // these guidelines
+
/**
* This can be null whenever a physical APK on device is missing. This can be the result of
* removing an external storage device where the APK resides.
- * <p>
- * This will result in the system reading the {@link PackageSetting} from disk, but without
- * being able to parse the base APK's AndroidManifest.xml to read all of its metadata. The data
- * that is written and read in {@link Settings} includes a minimal set of metadata needed to
- * perform other checks in the system.
- * <p>
+ * <p/>
+ * This will result in the system reading the state from disk, but without being able to parse
+ * the base APK's AndroidManifest.xml to read all of its metadata. The only available data that
+ * is written and read is the minimal set required to perform other checks in the system.
+ * <p/>
* This is important in order to enforce uniqueness within the system, as the package, even if
* on a removed storage device, is still considered installed. Another package of the same
* application ID or declaring the same permissions or similar cannot be installed.
- * <p>
+ * <p/>
* Re-attaching the storage device to make the APK available should allow the user to use the
* app once the device reboots or otherwise re-scans it.
+ * <p/>
+ * This can also occur in an device OTA situation where the package is no longer parsable on
+ * an updated SDK version, causing it to be rejected, but the state associated with it retained,
+ * similarly to if the package had been uninstalled with the --keep-data option.
*/
@Nullable
AndroidPackage getAndroidPackage();
/**
* The non-user-specific UID, or the UID if the user ID is
- * {@link android.os.UserHandle#USER_SYSTEM}.
+ * {@link android.os.UserHandle#SYSTEM}.
*/
int getAppId();
/**
+ * @see AndroidPackage#getPackageName()
+ */
+ @NonNull
+ String getPackageName();
+
+ /**
+ * @see ApplicationInfo#primaryCpuAbi
+ */
+ @Nullable
+ String getPrimaryCpuAbi();
+
+ /**
+ * @see ApplicationInfo#secondaryCpuAbi
+ */
+ @Nullable
+ String getSecondaryCpuAbi();
+
+ /**
+ * @see AndroidPackage#isPrivileged()
+ */
+ boolean isPrivileged();
+
+ /**
+ * @see AndroidPackage#isSystem()
+ */
+ boolean isSystem();
+
+ /**
+ * Whether this app is on the /data partition having been upgraded from a preinstalled app on a
+ * system partition.
+ */
+ boolean isUpdatedSystemApp();
+
+ /**
+ * @return State for a user or {@link PackageUserState#DEFAULT} if the state doesn't exist.
+ */
+ @NonNull
+ PackageUserState getStateForUser(@NonNull UserHandle user);
+
+ /**
+ * @see R.styleable#AndroidManifestUsesLibrary
+ */
+ @NonNull
+ List<SharedLibrary> getUsesLibraries();
+
+ // Methods below this comment are not yet exposed as API
+
+ /**
* Value set through {@link PackageManager#setApplicationCategoryHint(String, int)}. Only
* applied if the application itself does not declare a category.
*
* @see AndroidPackage#getCategory()
+ * @hide
*/
int getCategoryOverride();
@@ -94,6 +155,8 @@ public interface PackageState {
* The install time CPU override, if any. This value is written at install time
* and doesn't change during the life of an install. If non-null,
* {@link #getPrimaryCpuAbi()} will also contain the same value.
+ *
+ * @hide
*/
@Nullable
String getCpuAbiOverride();
@@ -101,6 +164,8 @@ public interface PackageState {
/**
* In epoch milliseconds. The last modified time of the file directory which houses the app
* APKs. Only updated on package update; does not track realtime modifications.
+ *
+ * @hide
*/
long getLastModifiedTime();
@@ -108,7 +173,10 @@ public interface PackageState {
* An aggregation across the framework of the last time an app was used for a particular reason.
* Keys are indexes into the array represented by {@link PackageManager.NotifyReason}, values
* are in epoch milliseconds.
+ *
+ * @hide
*/
+ @Immutable.Ignore
@Size(PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT)
@NonNull
long[] getLastPackageUsageTime();
@@ -116,12 +184,15 @@ public interface PackageState {
/**
* In epoch milliseconds. The timestamp of the last time the package on device went through
* an update package installation.
+ *
+ * @hide
*/
long getLastUpdateTime();
/**
* Cached here in case the physical code directory on device is unmounted.
* @see AndroidPackage#getLongVersionCode()
+ * @hide
*/
long getVersionCode();
@@ -129,37 +200,22 @@ public interface PackageState {
* Maps mime group name to the set of Mime types in a group. Mime groups declared by app are
* populated with empty sets at construction. Mime groups can not be created/removed at runtime,
* thus keys in this map should not change.
+ *
+ * @hide
*/
@NonNull
Map<String, Set<String>> getMimeGroups();
/**
- * @see AndroidPackage#getPackageName()
- */
- @NonNull
- String getPackageName();
-
- /**
- * TODO: Rename this to getCodePath
* @see AndroidPackage#getPath()
+ * @hide
*/
@NonNull
File getPath();
/**
- * @see ApplicationInfo#primaryCpuAbi
- */
- @Nullable
- String getPrimaryCpuAbi();
-
- /**
- * @see ApplicationInfo#secondaryCpuAbi
- */
- @Nullable
- String getSecondaryCpuAbi();
-
- /**
* Whether the package shares the same user ID as other packages
+ * @hide
*/
boolean hasSharedUser();
@@ -169,18 +225,24 @@ public interface PackageState {
*
* @return the app ID of the shared user that this package is a part of, or -1 if it's not part
* of a shared user.
+ * @hide
*/
int getSharedUserAppId();
+ /** @hide */
+ @Immutable.Ignore
@NonNull
SigningInfo getSigningInfo();
+ /** @hide */
+ @Immutable.Ignore
@NonNull
SparseArray<? extends PackageUserState> getUserStates();
/**
* @return the result of {@link #getUserStates()}.get(userId) or
* {@link PackageUserState#DEFAULT} if the state doesn't exist.
+ * @hide
*/
@NonNull
default PackageUserState getUserStateOrDefault(@UserIdInt int userId) {
@@ -192,54 +254,61 @@ public interface PackageState {
* The actual files resolved for each shared library.
*
* @see R.styleable#AndroidManifestUsesLibrary
+ * @hide
*/
@NonNull
List<String> getUsesLibraryFiles();
/**
- * @see R.styleable#AndroidManifestUsesLibrary
- */
- @NonNull
- List<SharedLibraryInfo> getUsesLibraryInfos();
-
- /**
* @see R.styleable#AndroidManifestUsesSdkLibrary
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
String[] getUsesSdkLibraries();
/**
* @see R.styleable#AndroidManifestUsesSdkLibrary_versionMajor
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
long[] getUsesSdkLibrariesVersionsMajor();
/**
* @see R.styleable#AndroidManifestUsesStaticLibrary
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
String[] getUsesStaticLibraries();
/**
* @see R.styleable#AndroidManifestUsesStaticLibrary_version
+ * @hide
*/
+ @Immutable.Ignore
@NonNull
long[] getUsesStaticLibrariesVersions();
/**
* @see AndroidPackage#getVolumeUuid()
+ * @hide
*/
@Nullable
String getVolumeUuid();
/**
* @see AndroidPackage#isExternalStorage()
+ * @hide
*/
boolean isExternalStorage();
/**
* Whether a package was installed --force-queryable such that it is always queryable by any
* package, regardless of their manifest content.
+ *
+ * @hide
*/
boolean isForceQueryableOverride();
@@ -248,67 +317,62 @@ public interface PackageState {
*
* @see PackageManager#MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS
* @see PackageManager#setSystemAppState
+ * @hide
*/
boolean isHiddenUntilInstalled();
/**
* @see com.android.server.pm.permission.UserPermissionState
+ * @hide
*/
boolean isInstallPermissionsFixed();
/**
* @see AndroidPackage#isOdm()
+ * @hide
*/
boolean isOdm();
/**
* @see AndroidPackage#isOem()
+ * @hide
*/
boolean isOem();
/**
- * @see AndroidPackage#isPrivileged()
- */
- boolean isPrivileged();
-
- /**
* @see AndroidPackage#isProduct()
+ * @hide
*/
boolean isProduct();
/**
* @see ApplicationInfo#PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER
+ * @hide
*/
boolean isRequiredForSystemUser();
/**
- * @see AndroidPackage#isSystem()
- */
- boolean isSystem();
-
- /**
* @see AndroidPackage#isSystemExt()
+ * @hide
*/
boolean isSystemExt();
/**
* Whether or not an update is available. Ostensibly only for instant apps.
+ * @hide
*/
boolean isUpdateAvailable();
/**
- * Whether this app is on the /data partition having been upgraded from a preinstalled app on a
- * system partition.
- */
- boolean isUpdatedSystemApp();
-
- /**
* Whether this app is packaged in an updated apex.
+ *
+ * @hide
*/
boolean isApkInUpdatedApex();
/**
* @see AndroidPackage#isVendor()
+ * @hide
*/
boolean isVendor();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index eac084228b81..c6ce40e39604 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -21,9 +21,9 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningInfo;
import android.content.pm.overlay.OverlayPaths;
+import android.os.UserHandle;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -33,6 +33,7 @@ import com.android.server.pm.PackageSetting;
import com.android.server.pm.Settings;
import java.io.File;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -139,7 +140,7 @@ public class PackageStateImpl implements PackageState {
@NonNull
private final long[] mUsesStaticLibrariesVersions;
@NonNull
- private final List<SharedLibraryInfo> mUsesLibraryInfos;
+ private final List<SharedLibrary> mUsesLibraries;
@NonNull
private final List<String> mUsesLibraryFiles;
@NonNull
@@ -170,7 +171,7 @@ public class PackageStateImpl implements PackageState {
mLastModifiedTime = pkgState.getLastModifiedTime();
mLastUpdateTime = pkgState.getLastUpdateTime();
mLongVersionCode = pkgState.getVersionCode();
- mMimeGroups = pkgState.getMimeGroups();
+ mMimeGroups = Collections.unmodifiableMap(pkgState.getMimeGroups());
mPath = pkgState.getPath();
mPrimaryCpuAbi = pkgState.getPrimaryCpuAbi();
mSecondaryCpuAbi = pkgState.getSecondaryCpuAbi();
@@ -180,8 +181,8 @@ public class PackageStateImpl implements PackageState {
mUsesSdkLibrariesVersionsMajor = pkgState.getUsesSdkLibrariesVersionsMajor();
mUsesStaticLibraries = pkgState.getUsesStaticLibraries();
mUsesStaticLibrariesVersions = pkgState.getUsesStaticLibrariesVersions();
- mUsesLibraryInfos = pkgState.getUsesLibraryInfos();
- mUsesLibraryFiles = pkgState.getUsesLibraryFiles();
+ mUsesLibraries = Collections.unmodifiableList(pkgState.getUsesLibraries());
+ mUsesLibraryFiles = Collections.unmodifiableList(pkgState.getUsesLibraryFiles());
setBoolean(Booleans.FORCE_QUERYABLE_OVERRIDE, pkgState.isForceQueryableOverride());
setBoolean(Booleans.HIDDEN_UNTIL_INSTALLED, pkgState.isHiddenUntilInstalled());
setBoolean(Booleans.INSTALL_PERMISSIONS_FIXED, pkgState.isInstallPermissionsFixed());
@@ -195,11 +196,18 @@ public class PackageStateImpl implements PackageState {
int userStatesSize = userStates.size();
mUserStates = new SparseArray<>(userStatesSize);
for (int index = 0; index < userStatesSize; index++) {
- mUserStates.put(mUserStates.keyAt(index),
- UserStateImpl.copy(mUserStates.valueAt(index)));
+ mUserStates.put(userStates.keyAt(index),
+ UserStateImpl.copy(userStates.valueAt(index)));
}
}
+ @NonNull
+ @Override
+ public PackageUserState getStateForUser(@NonNull UserHandle user) {
+ PackageUserState userState = getUserStates().get(user.getIdentifier());
+ return userState == null ? PackageUserState.DEFAULT : userState;
+ }
+
@Override
public boolean isExternalStorage() {
return getBoolean(Booleans.EXTERNAL_STORAGE);
@@ -468,8 +476,7 @@ public class PackageStateImpl implements PackageState {
}
@DataClass.Generated.Member
- public @NonNull
- ArraySet<String> getDisabledComponents() {
+ public @NonNull ArraySet<String> getDisabledComponents() {
return mDisabledComponents;
}
@@ -535,10 +542,10 @@ public class PackageStateImpl implements PackageState {
}
@DataClass.Generated(
- time = 1644270981508L,
+ time = 1661977809886L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull java.util.Set<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final long mFirstInstallTime\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final long mFirstInstallTime\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
@@ -659,8 +666,8 @@ public class PackageStateImpl implements PackageState {
}
@DataClass.Generated.Member
- public @NonNull List<SharedLibraryInfo> getUsesLibraryInfos() {
- return mUsesLibraryInfos;
+ public @NonNull List<SharedLibrary> getUsesLibraries() {
+ return mUsesLibraries;
}
@DataClass.Generated.Member
@@ -690,10 +697,10 @@ public class PackageStateImpl implements PackageState {
}
@DataClass.Generated(
- time = 1644270981543L,
+ time = 1661977809932L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackage mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final boolean mHasSharedUser\nprivate final int mSharedUserAppId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> mUsesLibraryInfos\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override int getSharedUserAppId()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final @android.annotation.Nullable com.android.server.pm.pkg.AndroidPackage mAndroidPackage\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mVolumeUuid\nprivate final int mAppId\nprivate final int mCategoryOverride\nprivate final @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate final long mLastModifiedTime\nprivate final long mLastUpdateTime\nprivate final long mLongVersionCode\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mMimeGroups\nprivate final @android.annotation.NonNull java.io.File mPath\nprivate final @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate final @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate final boolean mHasSharedUser\nprivate final int mSharedUserAppId\nprivate final @android.annotation.NonNull java.lang.String[] mUsesSdkLibraries\nprivate final @android.annotation.NonNull long[] mUsesSdkLibrariesVersionsMajor\nprivate final @android.annotation.NonNull java.lang.String[] mUsesStaticLibraries\nprivate final @android.annotation.NonNull long[] mUsesStaticLibrariesVersions\nprivate final @android.annotation.NonNull java.util.List<com.android.server.pm.pkg.SharedLibrary> mUsesLibraries\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mUsesLibraryFiles\nprivate final @android.annotation.NonNull long[] mLastPackageUsageTime\nprivate final @android.annotation.NonNull android.content.pm.SigningInfo mSigningInfo\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserState> mUserStates\npublic static com.android.server.pm.pkg.PackageState copy(com.android.server.pm.pkg.PackageStateInternal)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isExternalStorage()\npublic @java.lang.Override boolean isForceQueryableOverride()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @java.lang.Override boolean isInstallPermissionsFixed()\npublic @java.lang.Override boolean isOdm()\npublic @java.lang.Override boolean isOem()\npublic @java.lang.Override boolean isPrivileged()\npublic @java.lang.Override boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic @java.lang.Override boolean isSystem()\npublic @java.lang.Override boolean isSystemExt()\npublic @java.lang.Override boolean isUpdateAvailable()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic @java.lang.Override boolean isApkInUpdatedApex()\npublic @java.lang.Override boolean isVendor()\npublic @java.lang.Override long getVersionCode()\npublic @java.lang.Override boolean hasSharedUser()\npublic @java.lang.Override int getSharedUserAppId()\nclass PackageStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageState]\nprivate static final int SYSTEM\nprivate static final int EXTERNAL_STORAGE\nprivate static final int PRIVILEGED\nprivate static final int OEM\nprivate static final int VENDOR\nprivate static final int PRODUCT\nprivate static final int SYSTEM_EXT\nprivate static final int REQUIRED_FOR_SYSTEM_USER\nprivate static final int ODM\nprivate static final int FORCE_QUERYABLE_OVERRIDE\nprivate static final int HIDDEN_UNTIL_INSTALLED\nprivate static final int INSTALL_PERMISSIONS_FIXED\nprivate static final int UPDATE_AVAILABLE\nprivate static final int UPDATED_SYSTEM_APP\nprivate static final int APK_IN_UPDATED_APEX\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
index 84799ea29423..8f5795bc47eb 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateInternal.java
@@ -31,6 +31,7 @@ import java.util.UUID;
/**
* Exposes internal types for internal usage of {@link PackageState}.
+ * @hide
*/
public interface PackageStateInternal extends PackageState {
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 2c8b977b2bd6..b22c0386c0c1 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java
@@ -23,12 +23,12 @@ import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.SharedLibraryInfo;
+import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
import com.android.server.pm.PackageSetting;
import java.util.ArrayList;
import java.util.List;
-import java.util.stream.Collectors;
/**
* For use by {@link PackageSetting} to maintain functionality that used to exist in PackageParser.
@@ -38,15 +38,16 @@ import java.util.stream.Collectors;
*
* These fields are also not copied into any cloned PackageSetting, to preserve the old behavior
* where they would be lost implicitly by re-generating the package object.
+ * @hide
*/
@DataClass(genSetters = true, genConstructor = false, genBuilder = false)
-@DataClass.Suppress({"setLastPackageUsageTimeInMills", "setPackageSetting"})
+@DataClass.Suppress({"setLastPackageUsageTimeInMills", "setPackageSetting", "setUsesLibraryInfos"})
public class PackageStateUnserialized {
private boolean hiddenUntilInstalled;
@NonNull
- private List<SharedLibraryInfo> usesLibraryInfos = emptyList();
+ private List<SharedLibraryWrapper> usesLibraryInfos = emptyList();
@NonNull
private List<String> usesLibraryFiles = emptyList();
@@ -69,6 +70,18 @@ public class PackageStateUnserialized {
mPackageSetting = packageSetting;
}
+ @NonNull
+ public PackageStateUnserialized addUsesLibraryInfo(@NonNull SharedLibraryWrapper value) {
+ usesLibraryInfos = CollectionUtils.add(usesLibraryInfos, value);
+ return this;
+ }
+
+ @NonNull
+ public PackageStateUnserialized addUsesLibraryFile(@NonNull String value) {
+ usesLibraryFiles = CollectionUtils.add(usesLibraryFiles, value);
+ return this;
+ }
+
private long[] lazyInitLastPackageUsageTimeInMills() {
return new long[PackageManager.NOTIFY_PACKAGE_USE_REASONS_COUNT];
}
@@ -129,8 +142,16 @@ public class PackageStateUnserialized {
}
public @NonNull List<SharedLibraryInfo> getNonNativeUsesLibraryInfos() {
- return getUsesLibraryInfos().stream()
- .filter((l) -> !l.isNative()).collect(Collectors.toList());
+ var list = new ArrayList<SharedLibraryInfo>();
+ usesLibraryInfos = getUsesLibraryInfos();
+ for (int index = 0; index < usesLibraryInfos.size(); index++) {
+ var library = usesLibraryInfos.get(index);
+ if (!library.isNative()) {
+ list.add(library.getInfo());
+ }
+
+ }
+ return list;
}
public PackageStateUnserialized setHiddenUntilInstalled(boolean value) {
@@ -140,7 +161,11 @@ public class PackageStateUnserialized {
}
public PackageStateUnserialized setUsesLibraryInfos(@NonNull List<SharedLibraryInfo> value) {
- usesLibraryInfos = value;
+ var list = new ArrayList<SharedLibraryWrapper>();
+ for (int index = 0; index < value.size(); index++) {
+ list.add(new SharedLibraryWrapper(value.get(index)));
+ }
+ usesLibraryInfos = list;
mPackageSetting.onChanged();
return this;
}
@@ -202,7 +227,7 @@ public class PackageStateUnserialized {
}
@DataClass.Generated.Member
- public @NonNull List<SharedLibraryInfo> getUsesLibraryInfos() {
+ public @NonNull List<SharedLibraryWrapper> getUsesLibraryInfos() {
return usesLibraryInfos;
}
@@ -251,10 +276,10 @@ public class PackageStateUnserialized {
}
@DataClass.Generated(
- time = 1646203523807L,
+ time = 1661373697219L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateUnserialized.java",
- inputSignatures = "private boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate boolean updatedSystemApp\nprivate boolean apkInApex\nprivate boolean apkInUpdatedApex\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\nprivate @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate final @android.annotation.NonNull com.android.server.pm.PackageSetting mPackageSetting\nprivate long[] lazyInitLastPackageUsageTimeInMills()\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic long getLatestPackageUseTimeInMills()\npublic long getLatestForegroundPackageUseTimeInMills()\npublic void updateFrom(com.android.server.pm.pkg.PackageStateUnserialized)\npublic @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> getNonNativeUsesLibraryInfos()\npublic com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List<android.content.pm.SharedLibraryInfo>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List<java.lang.String>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUpdatedSystemApp(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setApkInApex(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setApkInUpdatedApex(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(long)\npublic com.android.server.pm.pkg.PackageStateUnserialized setOverrideSeInfo(java.lang.String)\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)")
+ inputSignatures = "private boolean hiddenUntilInstalled\nprivate @android.annotation.NonNull java.util.List<com.android.server.pm.pkg.SharedLibraryWrapper> usesLibraryInfos\nprivate @android.annotation.NonNull java.util.List<java.lang.String> usesLibraryFiles\nprivate boolean updatedSystemApp\nprivate boolean apkInApex\nprivate boolean apkInUpdatedApex\nprivate volatile @android.annotation.NonNull long[] lastPackageUsageTimeInMills\nprivate @android.annotation.Nullable java.lang.String overrideSeInfo\nprivate final @android.annotation.NonNull com.android.server.pm.PackageSetting mPackageSetting\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized addUsesLibraryInfo(com.android.server.pm.pkg.SharedLibraryWrapper)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized addUsesLibraryFile(java.lang.String)\nprivate long[] lazyInitLastPackageUsageTimeInMills()\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(int,long)\npublic long getLatestPackageUseTimeInMills()\npublic long getLatestForegroundPackageUseTimeInMills()\npublic void updateFrom(com.android.server.pm.pkg.PackageStateUnserialized)\npublic @android.annotation.NonNull java.util.List<android.content.pm.SharedLibraryInfo> getNonNativeUsesLibraryInfos()\npublic com.android.server.pm.pkg.PackageStateUnserialized setHiddenUntilInstalled(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryInfos(java.util.List<android.content.pm.SharedLibraryInfo>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUsesLibraryFiles(java.util.List<java.lang.String>)\npublic com.android.server.pm.pkg.PackageStateUnserialized setUpdatedSystemApp(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setApkInApex(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setApkInUpdatedApex(boolean)\npublic com.android.server.pm.pkg.PackageStateUnserialized setLastPackageUsageTimeInMills(long)\npublic com.android.server.pm.pkg.PackageStateUnserialized setOverrideSeInfo(java.lang.String)\nclass PackageStateUnserialized extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=true, genConstructor=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
index a883a059de0a..9749cfa6d82b 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateUtils.java
@@ -24,6 +24,7 @@ import android.util.SparseArray;
import com.android.server.pm.pkg.component.ParsedMainComponent;
+/** @hide */
public class PackageStateUtils {
public static boolean isMatch(PackageState packageState, long flags) {
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index e19e555b5fcf..a68e59b2319a 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -21,136 +21,178 @@ import android.annotation.Nullable;
import android.content.pm.PackageManager;
import android.content.pm.overlay.OverlayPaths;
import android.os.UserHandle;
+import android.processor.immutability.Immutable;
import android.util.ArraySet;
import java.util.Map;
+
/**
- * The API surface for {@link PackageUserStateInternal}, for use by in-process mainline consumers.
- *
* The parent of this class is {@link PackageState}, which handles non-user state, exposing this
* interface for per-user state.
*
* @hide
*/
-// TODO(b/173807334): Expose API
//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
public interface PackageUserState {
- /** @hide */
+ /**
+ * @return whether the package is marked as installed
+ */
+ boolean isInstalled();
+
+ // Methods below this comment are not yet exposed as API
+
+ /**
+ * @hide
+ */
@NonNull
PackageUserState DEFAULT = PackageUserStateInternal.DEFAULT;
/**
* Combination of {@link #getOverlayPaths()} and {@link #getSharedLibraryOverlayPaths()}
+ *
* @hide
*/
+ @Immutable.Ignore
@Nullable
OverlayPaths getAllOverlayPaths();
/**
* Credential encrypted /data partition inode.
+ *
+ * @hide
*/
long getCeDataInode();
/**
* Fully qualified class names of components explicitly disabled.
+ *
+ * @hide
*/
@NonNull
ArraySet<String> getDisabledComponents();
+ /**
+ * @hide
+ */
@PackageManager.DistractionRestriction
int getDistractionFlags();
/**
* Fully qualified class names of components explicitly enabled.
+ *
+ * @hide
*/
@NonNull
ArraySet<String> getEnabledComponents();
/**
* Retrieve the effective enabled state of the package itself.
+ *
+ * @hide
*/
@PackageManager.EnabledState
int getEnabledState();
/**
+ * @hide
* @see PackageManager#setHarmfulAppWarning(String, CharSequence)
*/
@Nullable
String getHarmfulAppWarning();
+ /**
+ * @hide
+ */
@PackageManager.InstallReason
int getInstallReason();
/**
* Tracks the last calling package to set a specific enabled state for the package.
+ *
+ * @hide
*/
@Nullable
String getLastDisableAppCaller();
- /** @hide */
+ /**
+ * @hide
+ */
+ @Immutable.Ignore
@Nullable
OverlayPaths getOverlayPaths();
- /** @hide */
+ /**
+ * @hide
+ */
+ @Immutable.Ignore
@NonNull
Map<String, OverlayPaths> getSharedLibraryOverlayPaths();
+ /**
+ * @hide
+ */
@PackageManager.UninstallReason
int getUninstallReason();
/**
* @return whether the given fully qualified class name is explicitly enabled
+ * @hide
*/
boolean isComponentEnabled(@NonNull String componentName);
/**
* @return {@link #isComponentEnabled(String)} but for explicitly disabled
+ * @hide
*/
boolean isComponentDisabled(@NonNull String componentName);
/**
+ * @hide
* @see PackageManager#setApplicationHiddenSettingAsUser(String, boolean, UserHandle)
*/
boolean isHidden();
/**
- * @return whether the package is marked as installed for all users
- */
- boolean isInstalled();
-
- /**
* @return whether the package is marked as an ephemeral app, which restricts permissions,
* features, visibility
+ * @hide
*/
boolean isInstantApp();
/**
* @return whether the package has not been launched since being explicitly stopped
+ * @hide
*/
boolean isNotLaunched();
/**
* @return whether the package has been stopped, which can occur if it's force-stopped, data
* cleared, or just been installed
+ * @hide
*/
boolean isStopped();
/**
* @return whether the package has been suspended, maybe by the device admin, disallowing its
* launch
+ * @hide
*/
boolean isSuspended();
/**
* @return whether the package was installed as a virtual preload, which may be done as part
* of device infrastructure auto installation outside of the initial device image
+ * @hide
*/
boolean isVirtualPreload();
/**
* The "package:type/entry" form of the theme resource ID previously set as the splash screen.
+ *
+ * @hide
* @see android.window.SplashScreen#setSplashScreenTheme(int)
* @see android.content.res.Resources#getResourceName(int)
*/
@@ -161,8 +203,10 @@ public interface PackageUserState {
* In epoch milliseconds. The timestamp of the first install of the app of the particular user
* on the device, surviving past app updates. Different users might have a different first
* install time.
- *
+ * <p/>
* This does not survive full removal of the app (i.e., uninstalls for all users).
+ *
+ * @hide
*/
long getFirstInstallTime();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index daa4545b4a4a..2d2e062961d2 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -30,6 +30,7 @@ import com.android.server.utils.WatchedArraySet;
import java.util.Collections;
import java.util.Map;
+/** @hide */
class PackageUserStateDefault implements PackageUserStateInternal {
@Override
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index da22b1796ac3..a536f90fea6b 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -41,6 +41,7 @@ import java.util.Collections;
import java.util.Map;
import java.util.Objects;
+/** @hide */
@DataClass(genConstructor = false, genBuilder = false, genEqualsHashCode = true)
@DataClass.Suppress({"mOverlayPathsLock", "mOverlayPaths", "mSharedLibraryOverlayPathsLock",
"mSharedLibraryOverlayPaths", "setOverlayPaths", "setCachedOverlayPaths"})
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
index 96225c012999..46cc830130ef 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateInternal.java
@@ -29,6 +29,8 @@ import com.android.server.utils.WatchedArraySet;
* Internal variant of {@link PackageUserState} that includes data not exposed as API. This is
* still read-only and should be used inside system server code when possible over the
* implementation.
+ *
+ * @hide
*/
public interface PackageUserStateInternal extends PackageUserState, FrameworkPackageUserState {
diff --git a/services/core/java/com/android/server/pm/pkg/SharedLibrary.java b/services/core/java/com/android/server/pm/pkg/SharedLibrary.java
new file mode 100644
index 000000000000..20f05f684f6f
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/SharedLibrary.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.VersionedPackage;
+import android.processor.immutability.Immutable;
+
+import java.util.List;
+
+/**
+ * @hide
+ */
+//@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+@Immutable
+public interface SharedLibrary {
+
+ /**
+ * @see SharedLibraryInfo#getPath()
+ */
+ @Nullable
+ String getPath();
+
+ /**
+ * @see SharedLibraryInfo#getPackageName()
+ */
+ @Nullable
+ String getPackageName();
+
+ /**
+ * @see SharedLibraryInfo#getName()
+ */
+ @Nullable
+ String getName();
+
+ /**
+ * @see SharedLibraryInfo#getAllCodePaths()
+ */
+ @NonNull
+ List<String> getAllCodePaths();
+
+ /**
+ * @see SharedLibraryInfo#getLongVersion()
+ */
+ long getVersion();
+
+ /**
+ * @see SharedLibraryInfo#getType()
+ */
+ int getType();
+
+ /**
+ * @see SharedLibraryInfo#isNative()
+ */
+ boolean isNative();
+
+ /**
+ * @see SharedLibraryInfo#getDeclaringPackage()
+ */
+ @Immutable.Policy(exceptions = {Immutable.Policy.Exception.FINAL_CLASSES_WITH_FINAL_FIELDS})
+ @NonNull
+ VersionedPackage getDeclaringPackage();
+
+ /**
+ * @see SharedLibraryInfo#getDependentPackages()
+ */
+ @Immutable.Policy(exceptions = {Immutable.Policy.Exception.FINAL_CLASSES_WITH_FINAL_FIELDS})
+ @NonNull
+ List<VersionedPackage> getDependentPackages();
+
+ /**
+ * @see SharedLibraryInfo#getDependencies()
+ */
+ @NonNull
+ List<SharedLibrary> getDependencies();
+}
diff --git a/services/core/java/com/android/server/pm/pkg/SharedLibraryWrapper.java b/services/core/java/com/android/server/pm/pkg/SharedLibraryWrapper.java
new file mode 100644
index 000000000000..2f1fe1a460d9
--- /dev/null
+++ b/services/core/java/com/android/server/pm/pkg/SharedLibraryWrapper.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.pkg;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.SharedLibraryInfo;
+import android.content.pm.VersionedPackage;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/** @hide */
+public class SharedLibraryWrapper implements SharedLibrary {
+
+ private final SharedLibraryInfo mInfo;
+
+ @Nullable
+ private List<SharedLibrary> cachedDependenciesList;
+
+ public SharedLibraryWrapper(@NonNull SharedLibraryInfo info) {
+ mInfo = info;
+ }
+
+ @NonNull
+ public SharedLibraryInfo getInfo() {
+ return mInfo;
+ }
+
+ @Override
+ public String getPath() {
+ return mInfo.getPath();
+ }
+
+ @Override
+ public String getPackageName() {
+ return mInfo.getPackageName();
+ }
+
+ @Override
+ public String getName() {
+ return mInfo.getName();
+ }
+
+ @Override
+ public List<String> getAllCodePaths() {
+ return Collections.unmodifiableList(mInfo.getAllCodePaths());
+ }
+
+ @Override
+ public long getVersion() {
+ return mInfo.getLongVersion();
+ }
+
+ @Override
+ public int getType() {
+ return mInfo.getType();
+ }
+
+ @Override
+ public boolean isNative() {
+ return mInfo.isNative();
+ }
+
+ @NonNull
+ @Override
+ public VersionedPackage getDeclaringPackage() {
+ return mInfo.getDeclaringPackage();
+ }
+
+ @NonNull
+ @Override
+ public List<VersionedPackage> getDependentPackages() {
+ return Collections.unmodifiableList(mInfo.getDependentPackages());
+ }
+
+ @NonNull
+ @Override
+ public List<SharedLibrary> getDependencies() {
+ if (cachedDependenciesList == null) {
+ var dependencies = mInfo.getDependencies();
+ if (dependencies == null) {
+ cachedDependenciesList = Collections.emptyList();
+ } else {
+ var list = new ArrayList<SharedLibrary>();
+ for (int index = 0; index < dependencies.size(); index++) {
+ list.add(new SharedLibraryWrapper(dependencies.get(index)));
+ }
+ cachedDependenciesList = Collections.unmodifiableList(list);
+ }
+ }
+ return cachedDependenciesList;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
index 55c305ce812f..063f577bce5f 100644
--- a/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
+++ b/services/core/java/com/android/server/pm/pkg/SharedUserApi.java
@@ -27,6 +27,7 @@ import com.android.server.pm.pkg.component.ParsedProcess;
import java.util.List;
+/** @hide */
public interface SharedUserApi {
@NonNull
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java b/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
index ba85e0795c70..c66a5c1b6c92 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedProvider.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.PathPermission;
import android.os.PatternMatcher;
+import android.processor.immutability.Immutable;
import java.util.List;
@@ -34,12 +35,14 @@ public interface ParsedProvider extends ParsedMainComponent {
boolean isMultiProcess();
+ @Immutable.Ignore
@NonNull
List<PathPermission> getPathPermissions();
@Nullable
String getReadPermission();
+ @Immutable.Ignore
@NonNull
List<PatternMatcher> getUriPermissionPatterns();
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
index 1a46e2012448..2626bb405c42 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java
@@ -147,7 +147,7 @@ public interface ParsingPackage {
ParsingPackage setSharedUserId(String sharedUserId);
- ParsingPackage setStaticSharedLibName(String staticSharedLibName);
+ ParsingPackage setStaticSharedLibraryName(String staticSharedLibName);
ParsingPackage setTaskAffinity(String taskAffinity);
@@ -221,7 +221,7 @@ public interface ParsingPackage {
ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
- ParsingPackage setSdkLibName(String sdkLibName);
+ ParsingPackage setSdkLibraryName(String sdkLibName);
ParsingPackage setSdkLibVersionMajor(int sdkLibVersionMajor);
@@ -458,7 +458,7 @@ public interface ParsingPackage {
Boolean getResizeableActivity();
@Nullable
- String getSdkLibName();
+ String getSdkLibraryName();
@NonNull
List<ParsedService> getServices();
@@ -473,7 +473,7 @@ public interface ParsingPackage {
String[] getSplitNames();
@Nullable
- String getStaticSharedLibName();
+ String getStaticSharedLibraryName();
int getTargetSdkVersion();
diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
index 14f787e4fbea..952adda8e238 100644
--- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java
@@ -238,7 +238,6 @@ public class ParsingPackageUtils {
* of required system property within the overlay tag.
*/
public static final int PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY = 1 << 7;
- public static final int PARSE_FRAMEWORK_RES_SPLITS = 1 << 8;
public static final int PARSE_APK_IN_APEX = 1 << 9;
public static final int PARSE_CHATTY = 1 << 31;
@@ -257,7 +256,6 @@ public class ParsingPackageUtils {
PARSE_IS_SYSTEM_DIR,
PARSE_MUST_BE_APK,
PARSE_IGNORE_OVERLAY_REQUIRED_SYSTEM_PROPERTY,
- PARSE_FRAMEWORK_RES_SPLITS
})
@Retention(RetentionPolicy.SOURCE)
public @interface ParseFlags {}
@@ -307,7 +305,7 @@ public class ParsingPackageUtils {
isCoreApp);
}
});
- var parseResult = parser.parsePackage(input, file, parseFlags, /* frameworkSplits= */ null);
+ var parseResult = parser.parsePackage(input, file, parseFlags);
if (parseResult.isError()) {
return input.error(parseResult);
}
@@ -356,14 +354,9 @@ public class ParsingPackageUtils {
* not check whether {@code packageFile} has changed since the last parse, it's up to callers to
* do so.
*/
- public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags,
- List<File> frameworkSplits) {
- if (((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0)
- && frameworkSplits.size() > 0
- && packageFile.getAbsolutePath().endsWith("/framework-res.apk")) {
- return parseClusterPackage(input, packageFile, frameworkSplits, flags);
- } else if (packageFile.isDirectory()) {
- return parseClusterPackage(input, packageFile, /* frameworkSplits= */null, flags);
+ public ParseResult<ParsingPackage> parsePackage(ParseInput input, File packageFile, int flags) {
+ if (packageFile.isDirectory()) {
+ return parseClusterPackage(input, packageFile, flags);
} else {
return parseMonolithicPackage(input, packageFile, flags);
}
@@ -375,28 +368,17 @@ public class ParsingPackageUtils {
* identical package name and version codes, a single base APK, and unique
* split names.
* <p>
- * Can also be passed the framework-res.apk file and a list of split apks coming from apexes
- * (via {@code frameworkSplits}) in which case they will be parsed similar to cluster packages
- * even if they are in different folders. Note that this code path may have other behaviour
- * differences.
- * <p>
* Note that this <em>does not</em> perform signature verification; that must be done separately
* in {@link #getSigningDetails(ParseInput, ParsedPackage, boolean)}.
*/
private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
- List<File> frameworkSplits, int flags) {
- // parseClusterPackageLite should receive no flags (0) for regular splits but we want to
- // pass the flags for framework splits
+ int flags) {
int liteParseFlags = 0;
- if ((flags & PARSE_FRAMEWORK_RES_SPLITS) != 0) {
- liteParseFlags = flags;
- }
if ((flags & PARSE_APK_IN_APEX) != 0) {
liteParseFlags |= PARSE_APK_IN_APEX;
}
final ParseResult<PackageLite> liteResult =
- ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, frameworkSplits,
- liteParseFlags);
+ ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, liteParseFlags);
if (liteResult.isError()) {
return input.error(liteResult);
}
@@ -647,7 +629,7 @@ public class ParsingPackageUtils {
final TypedArray manifestArray = res.obtainAttributes(parser, R.styleable.AndroidManifest);
try {
final boolean isCoreApp = parser.getAttributeBooleanValue(null /*namespace*/,
- "coreApp",false);
+ "coreApp", false);
final ParsingPackage pkg = mCallback.startParsingPackage(
pkgName, apkPath, codePath, manifestArray, isCoreApp);
final ParseResult<ParsingPackage> result =
@@ -2182,8 +2164,8 @@ public class ParsingPackageUtils {
}
}
- if (TextUtils.isEmpty(pkg.getStaticSharedLibName()) && TextUtils.isEmpty(
- pkg.getSdkLibName())) {
+ if (TextUtils.isEmpty(pkg.getStaticSharedLibraryName()) && TextUtils.isEmpty(
+ pkg.getSdkLibraryName())) {
// Add a hidden app detail activity to normal apps which forwards user to App Details
// page.
ParseResult<ParsedActivity> a = generateAppDetailsHiddenActivity(input, pkg);
@@ -2373,12 +2355,12 @@ public class ParsingPackageUtils {
PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
"sharedUserId not allowed in SDK library"
);
- } else if (pkg.getSdkLibName() != null) {
+ } else if (pkg.getSdkLibraryName() != null) {
return input.error("Multiple SDKs for package "
+ pkg.getPackageName());
}
- return input.success(pkg.setSdkLibName(lname.intern())
+ return input.success(pkg.setSdkLibraryName(lname.intern())
.setSdkLibVersionMajor(versionMajor)
.setSdkLibrary(true));
} finally {
@@ -2411,12 +2393,12 @@ public class ParsingPackageUtils {
PackageManager.INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID,
"sharedUserId not allowed in static shared library"
);
- } else if (pkg.getStaticSharedLibName() != null) {
+ } else if (pkg.getStaticSharedLibraryName() != null) {
return input.error("Multiple static-shared libs for package "
+ pkg.getPackageName());
}
- return input.success(pkg.setStaticSharedLibName(lname.intern())
+ return input.success(pkg.setStaticSharedLibraryName(lname.intern())
.setStaticSharedLibVersion(
PackageInfo.composeLongVersionCode(versionMajor, version))
.setStaticSharedLibrary(true));
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 9b7d19a725d1..f8fcaff354ce 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -26,7 +26,6 @@ import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
-import android.hardware.input.InputManagerInternal;
import android.os.Environment;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -39,6 +38,7 @@ import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.input.InputManagerInternal;
import com.android.server.policy.devicestate.config.Conditions;
import com.android.server.policy.devicestate.config.DeviceStateConfig;
import com.android.server.policy.devicestate.config.Flags;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index e332ac765633..07f5bcfb6e15 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -121,7 +121,6 @@ import android.hardware.hdmi.HdmiAudioSystemClient;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiPlaybackClient;
import android.hardware.hdmi.HdmiPlaybackClient.OneTouchPlayCallback;
-import android.hardware.input.InputManagerInternal;
import android.media.AudioManager;
import android.media.AudioManagerInternal;
import android.media.AudioSystem;
@@ -204,6 +203,7 @@ import com.android.server.ExtconUEventObserver;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
import com.android.server.SystemServiceManager;
+import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.KeyCombinationManager.TwoKeysCombinationRule;
import com.android.server.policy.keyguard.KeyguardServiceDelegate;
@@ -1662,7 +1662,7 @@ public class PhoneWindowManager implements WindowManagerPolicy {
private void toggleNotificationPanel() {
IStatusBarService statusBarService = getStatusBarService();
- if (statusBarService != null) {
+ if (isUserSetupComplete() && statusBarService != null) {
try {
statusBarService.togglePanel();
} catch (RemoteException e) {
@@ -3052,6 +3052,13 @@ public class PhoneWindowManager implements WindowManagerPolicy {
}
}
break;
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL:
+ Slog.wtf(TAG, "KEYCODE_STYLUS_BUTTON_* should be handled in"
+ + " interceptKeyBeforeQueueing");
+ return key_consumed;
}
if (isValidGlobalKey(keyCode)
@@ -4104,6 +4111,14 @@ public class PhoneWindowManager implements WindowManagerPolicy {
result &= ~ACTION_PASS_TO_USER;
break;
}
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_PRIMARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_SECONDARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
+ case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
+ // TODO(go/android-stylus-buttons): Handle stylus button presses.
+ result &= ~ACTION_PASS_TO_USER;
+ break;
+ }
}
if (useHapticFeedback) {
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index dad9584c6722..69fb22c28728 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -20,18 +20,20 @@ import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.display.DisplayManagerInternal;
-import android.hardware.input.InputManagerInternal;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
import android.metrics.LogMaker;
import android.net.Uri;
import android.os.BatteryStats;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IWakeLockCallback;
import android.os.Looper;
@@ -59,6 +61,7 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -137,7 +140,9 @@ public class Notifier {
private final NotifierHandler mHandler;
private final Executor mBackgroundExecutor;
private final Intent mScreenOnIntent;
+ private final Bundle mScreenOnOptions;
private final Intent mScreenOffIntent;
+ private final Bundle mScreenOffOptions;
// True if the device should suspend when the screen is off due to proximity.
private final boolean mSuspendWhenScreenOffDueToProximityConfig;
@@ -199,10 +204,14 @@ public class Notifier {
mScreenOnIntent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ mScreenOnOptions = BroadcastOptions.makeRemovingMatchingFilter(
+ new IntentFilter(Intent.ACTION_SCREEN_OFF)).toBundle();
mScreenOffIntent = new Intent(Intent.ACTION_SCREEN_OFF);
mScreenOffIntent.addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY | Intent.FLAG_RECEIVER_FOREGROUND
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+ mScreenOffOptions = BroadcastOptions.makeRemovingMatchingFilter(
+ new IntentFilter(Intent.ACTION_SCREEN_ON)).toBundle();
mSuspendWhenScreenOffDueToProximityConfig = context.getResources().getBoolean(
com.android.internal.R.bool.config_suspendWhenScreenOffDueToProximity);
@@ -788,7 +797,8 @@ public class Notifier {
if (mActivityManagerInternal.isSystemReady()) {
mContext.sendOrderedBroadcastAsUser(mScreenOnIntent, UserHandle.ALL, null,
- mWakeUpBroadcastDone, mHandler, 0, null, null);
+ AppOpsManager.OP_NONE, mScreenOnOptions, mWakeUpBroadcastDone, mHandler,
+ 0, null, null);
} else {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 2, 1);
sendNextBroadcast();
@@ -811,7 +821,8 @@ public class Notifier {
if (mActivityManagerInternal.isSystemReady()) {
mContext.sendOrderedBroadcastAsUser(mScreenOffIntent, UserHandle.ALL, null,
- mGoToSleepBroadcastDone, mHandler, 0, null, null);
+ AppOpsManager.OP_NONE, mScreenOffOptions, mGoToSleepBroadcastDone, mHandler,
+ 0, null, null);
} else {
EventLog.writeEvent(EventLogTags.POWER_SCREEN_BROADCAST_STOP, 3, 1);
sendNextBroadcast();
diff --git a/services/core/java/com/android/server/resources/OWNERS b/services/core/java/com/android/server/resources/OWNERS
new file mode 100644
index 000000000000..7460a14182e5
--- /dev/null
+++ b/services/core/java/com/android/server/resources/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 568761
+
+patb@google.com
+zyy@google.com
diff --git a/services/core/java/com/android/server/resources/ResourcesManagerService.java b/services/core/java/com/android/server/resources/ResourcesManagerService.java
index cc275466a5ff..eec3a0259a3e 100644
--- a/services/core/java/com/android/server/resources/ResourcesManagerService.java
+++ b/services/core/java/com/android/server/resources/ResourcesManagerService.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.content.res.IResourcesManager;
+import android.content.res.ResourceTimer;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
@@ -55,7 +56,7 @@ public class ResourcesManagerService extends SystemService {
@Override
public void onStart() {
- // Intentionally left empty.
+ ResourceTimer.start();
}
private final IBinder mService = new IResourcesManager.Stub() {
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
index 05d92beed11f..b0f03ef48e7e 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalConcurrentCaptureHandler.java
@@ -17,6 +17,7 @@
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;
@@ -289,43 +290,60 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal
* they were pushed.
* <li>Events can be flushed via {@link #flush()}. This will block until all events pushed prior
* to this call have been fully processed.
+ * TODO(b/246584464) Remove and replace with Handler (and other concurrency fixes).
* </ul>
*/
- private static class CallbackThread {
+ private static class CallbackThread implements Runnable {
private final Queue<Runnable> mList = new LinkedList<>();
private int mPushCount = 0;
private int mProcessedCount = 0;
-
+ private boolean mQuitting = false;
+ private final Thread mThread;
/**
* Ctor. Starts the thread.
*/
CallbackThread() {
- new Thread(() -> {
- try {
- while (true) {
- pop().run();
- synchronized (mList) {
- mProcessedCount++;
- mList.notifyAll();
- }
+ mThread = new Thread(this , "STHAL Concurrent Capture Handler Callback");
+ mThread.start();
+ }
+ /**
+ * Consume items in the queue until quit is called.
+ */
+ public void run() {
+ try {
+ while (true) {
+ Runnable toRun = pop();
+ if (toRun == null) {
+ // There are no longer any runnables to run,
+ // and quit() has been called.
+ return;
+ }
+ toRun.run();
+ synchronized (mList) {
+ mProcessedCount++;
+ mList.notifyAll();
}
- } catch (InterruptedException e) {
- // If interrupted, exit.
}
- }).start();
+ } catch (InterruptedException e) {
+ // If interrupted, exit.
+ // Note, this is dangerous wrt to flush.
+ }
}
-
/**
* Push a new runnable to the queue, with no deduping.
+ * If quit has been called, the runnable will not be pushed.
*
* @param runnable The runnable to push.
+ * @return If the runnable was successfully pushed.
*/
- void push(Runnable runnable) {
+ boolean push(Runnable runnable) {
synchronized (mList) {
+ if (mQuitting) return false;
mList.add(runnable);
mPushCount++;
mList.notifyAll();
}
+ return true;
}
/**
@@ -343,11 +361,26 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal
}
}
- private Runnable pop() throws InterruptedException {
+ /**
+ * Quit processing after the queue is cleared.
+ * All subsequent calls to push will fail.
+ * Note, this does not flush.
+ */
+ void quit() {
+ synchronized(mList) {
+ mQuitting = true;
+ mList.notifyAll();
+ }
+ }
+
+ // Returns the next runnable when available.
+ // Returns null iff the list is empty and quit has been called.
+ private @Nullable Runnable pop() throws InterruptedException {
synchronized (mList) {
- while (mList.isEmpty()) {
+ while (mList.isEmpty() && !mQuitting) {
mList.wait();
}
+ if (mList.isEmpty() && mQuitting) return null;
return mList.remove();
}
}
@@ -372,6 +405,7 @@ public class SoundTriggerHalConcurrentCaptureHandler implements ISoundTriggerHal
public void detach() {
mDelegate.detach();
mNotifier.unregisterListener(this);
+ mCallbackThread.quit();
}
////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
index 227424ea1780..b817821b48dc 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
@@ -150,6 +150,8 @@ public class SoundTriggerHalWatchdog implements ISoundTriggerHal {
@Override
public void detach() {
mUnderlying.detach();
+ // We should no longer have any pending calls
+ mTimer.quit();
}
private class Watchdog implements AutoCloseable {
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 dc4bdaaa8158..ce1157e1d80c 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -18,6 +18,7 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.media.permission.Identity;
import android.media.permission.IdentityContext;
import android.media.soundtrigger.ModelParameterRange;
@@ -33,6 +34,8 @@ import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.util.LatencyTracker;
+
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
@@ -65,9 +68,12 @@ import java.util.Objects;
public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInternal, Dumpable {
private static final String TAG = "SoundTriggerMiddlewareLogging";
private final @NonNull ISoundTriggerMiddlewareInternal mDelegate;
+ private final @NonNull Context mContext;
- public SoundTriggerMiddlewareLogging(@NonNull ISoundTriggerMiddlewareInternal delegate) {
+ public SoundTriggerMiddlewareLogging(@NonNull Context context,
+ @NonNull ISoundTriggerMiddlewareInternal delegate) {
mDelegate = delegate;
+ mContext = context;
}
@Override
@@ -298,6 +304,7 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
int captureSession)
throws RemoteException {
try {
+ startKeyphraseEventLatencyTracking(event);
mCallbackDelegate.onPhraseRecognition(modelHandle, event, captureSession);
logVoidReturn("onPhraseRecognition", modelHandle, event);
} catch (Exception e) {
@@ -347,6 +354,26 @@ public class SoundTriggerMiddlewareLogging implements ISoundTriggerMiddlewareInt
logVoidReturnWithObject(this, mOriginatorIdentity, methodName, args);
}
+ /**
+ * Starts the latency tracking log for keyphrase hotword invocation.
+ * The measurement covers from when the SoundTrigger HAL emits an event to when the
+ * {@link android.service.voice.VoiceInteractionSession} system UI view is shown.
+ */
+ private void startKeyphraseEventLatencyTracking(PhraseRecognitionEvent event) {
+ String latencyTrackerTag = null;
+ if (event.phraseExtras.length > 0) {
+ latencyTrackerTag = "KeyphraseId=" + event.phraseExtras[0].id;
+ }
+ LatencyTracker latencyTracker = LatencyTracker.getInstance(mContext);
+ // To avoid adding cancel to all of the different failure modes between here and
+ // showing the system UI, we defensively cancel once.
+ // Either we hit the LatencyTracker timeout of 15 seconds or we defensively cancel
+ // here if any error occurs.
+ latencyTracker.onActionCancel(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION);
+ latencyTracker.onActionStart(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION,
+ latencyTrackerTag);
+ }
+
@Override
public IBinder asBinder() {
return mCallbackDelegate.asBinder();
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 1995e5497e55..807ed14e85ce 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareService.java
@@ -20,14 +20,14 @@ import static android.Manifest.permission.SOUNDTRIGGER_DELEGATE_IDENTITY;
import android.annotation.NonNull;
import android.content.Context;
-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;
import android.media.permission.SafeCloseable;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerMiddlewareService;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
@@ -226,12 +226,13 @@ public class SoundTriggerMiddlewareService extends ISoundTriggerMiddlewareServic
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(getContext(),
+ new SoundTriggerMiddlewarePermission(
+ new SoundTriggerMiddlewareValidation(
+ new SoundTriggerMiddlewareImpl(factories,
+ new AudioSessionProviderImpl())),
+ getContext())), getContext()));
}
}
}
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java b/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java
index bfcc7d840662..fe91e66b5769 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/UptimeTimer.java
@@ -18,9 +18,7 @@ package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
import android.os.Handler;
-import android.os.Looper;
-
-import java.util.concurrent.atomic.AtomicReference;
+import android.os.HandlerThread;
/**
* A simple timer, similar to java.util.Timer, but using the "uptime clock".
@@ -33,58 +31,45 @@ import java.util.concurrent.atomic.AtomicReference;
* task.cancel();
*/
class UptimeTimer {
- private Handler mHandler = null;
+ private final Handler mHandler;
+ private final HandlerThread mHandlerThread;
interface Task {
void cancel();
}
UptimeTimer(String threadName) {
- new Thread(this::threadFunc, threadName).start();
- synchronized (this) {
- while (mHandler == null) {
- try {
- wait();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
+ mHandlerThread = new HandlerThread(threadName);
+ mHandlerThread.start();
+ // Blocks until looper init
+ mHandler = new Handler(mHandlerThread.getLooper());
}
+ // Note, this method is not internally synchronized.
+ // This is safe since Handlers are internally synchronized.
Task createTask(@NonNull Runnable runnable, long uptimeMs) {
- TaskImpl task = new TaskImpl(runnable);
- mHandler.postDelayed(task, uptimeMs);
+ Object token = new Object();
+ TaskImpl task = new TaskImpl(mHandler, token);
+ mHandler.postDelayed(runnable, token, uptimeMs);
return task;
}
- private void threadFunc() {
- Looper.prepare();
- synchronized (this) {
- mHandler = new Handler(Looper.myLooper());
- notifyAll();
- }
- Looper.loop();
+ void quit() {
+ mHandlerThread.quitSafely();
}
- private static class TaskImpl implements Task, Runnable {
- private AtomicReference<Runnable> mRunnable = new AtomicReference<>();
+ private static class TaskImpl implements Task {
+ private final Handler mHandler;
+ private final Object mToken;
- TaskImpl(@NonNull Runnable runnable) {
- mRunnable.set(runnable);
+ public TaskImpl(Handler handler, Object token) {
+ mHandler = handler;
+ mToken = token;
}
@Override
public void cancel() {
- mRunnable.set(null);
+ mHandler.removeCallbacksAndMessages(mToken);
}
-
- @Override
- public void run() {
- Runnable runnable = mRunnable.get();
- if (runnable != null) {
- runnable.run();
- }
- }
- }
+ };
}
diff --git a/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java b/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java
index 1fb7cf5249cc..421f7ce9cf58 100644
--- a/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java
+++ b/services/core/java/com/android/server/timedetector/GnssTimeUpdateService.java
@@ -260,10 +260,11 @@ public final class GnssTimeUpdateService extends Binder {
private void suggestGnssTime(LocationTime locationTime) {
logDebug("suggestGnssTime()");
- long gnssTime = locationTime.getTime();
+ long gnssUnixEpochTimeMillis = locationTime.getUnixEpochTimeMillis();
long elapsedRealtimeMs = locationTime.getElapsedRealtimeNanos() / 1_000_000L;
- TimestampedValue<Long> timeSignal = new TimestampedValue<>(elapsedRealtimeMs, gnssTime);
+ TimestampedValue<Long> timeSignal =
+ new TimestampedValue<>(elapsedRealtimeMs, gnssUnixEpochTimeMillis);
mLastSuggestedGnssTime = timeSignal;
GnssTimeSuggestion timeSuggestion = new GnssTimeSuggestion(timeSignal);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index e21feae65c97..886e8e687e5a 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -93,6 +93,7 @@ import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
@@ -254,6 +255,7 @@ public class TrustManagerService extends SystemService {
return;
}
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ checkNewAgents();
mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
mReceiver.register(mContext);
mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker);
@@ -262,7 +264,7 @@ public class TrustManagerService extends SystemService {
refreshAgentList(UserHandle.USER_ALL);
refreshDeviceLockedForUser(UserHandle.USER_ALL);
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
- maybeEnableFactoryTrustAgents(mLockPatternUtils, UserHandle.USER_SYSTEM);
+ maybeEnableFactoryTrustAgents(UserHandle.USER_SYSTEM);
}
}
@@ -685,7 +687,7 @@ public class TrustManagerService extends SystemService {
*/
public void lockUser(int userId) {
mLockPatternUtils.requireStrongAuth(
- StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId);
+ StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, userId);
try {
WindowManagerGlobal.getWindowManagerService().lockNow(null);
} catch (RemoteException e) {
@@ -1083,7 +1085,7 @@ public class TrustManagerService extends SystemService {
return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
}
- private void maybeEnableFactoryTrustAgents(LockPatternUtils utils, int userId) {
+ private void maybeEnableFactoryTrustAgents(int userId) {
if (0 != Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.TRUST_AGENTS_INITIALIZED, 0, userId)) {
return;
@@ -1100,8 +1102,7 @@ public class TrustManagerService extends SystemService {
} else { // A default agent is not set; perform regular trust agent discovery
for (ResolveInfo resolveInfo : resolveInfos) {
ComponentName componentName = getComponentName(resolveInfo);
- int applicationInfoFlags = resolveInfo.serviceInfo.applicationInfo.flags;
- if ((applicationInfoFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+ if (!isSystemTrustAgent(resolveInfo)) {
Log.i(TAG, "Leaving agent " + componentName + " disabled because package "
+ "is not a system package.");
continue;
@@ -1110,13 +1111,88 @@ public class TrustManagerService extends SystemService {
}
}
- List<ComponentName> previouslyEnabledAgents = utils.getEnabledTrustAgents(userId);
- discoveredAgents.addAll(previouslyEnabledAgents);
- utils.setEnabledTrustAgents(discoveredAgents, userId);
+ enableNewAgents(discoveredAgents, userId);
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, userId);
}
+ private void checkNewAgents() {
+ for (UserInfo userInfo : mUserManager.getAliveUsers()) {
+ checkNewAgentsForUser(userInfo.id);
+ }
+ }
+
+ /**
+ * Checks for any new trust agents that become available after the first boot, add them to the
+ * list of known agents, and enable them if they should be enabled by default.
+ */
+ private void checkNewAgentsForUser(int userId) {
+ // When KNOWN_TRUST_AGENTS_INITIALIZED is not set, only update the known agent list but do
+ // not enable any agent.
+ // These agents will be enabled by #maybeEnableFactoryTrustAgents if this is the first time
+ // that this device boots and TRUST_AGENTS_INITIALIZED is not already set.
+ // Otherwise, these agents may have been manually disabled by the user, and we should not
+ // re-enable them.
+ if (0 == Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, 0, userId)) {
+ initializeKnownAgents(userId);
+ return;
+ }
+
+ List<ComponentName> knownAgents = mLockPatternUtils.getKnownTrustAgents(userId);
+ List<ResolveInfo> agentInfoList = resolveAllowedTrustAgents(mContext.getPackageManager(),
+ userId);
+ ArraySet<ComponentName> newAgents = new ArraySet<>(agentInfoList.size());
+ ArraySet<ComponentName> newSystemAgents = new ArraySet<>(agentInfoList.size());
+
+ for (ResolveInfo agentInfo : agentInfoList) {
+ ComponentName agentComponentName = getComponentName(agentInfo);
+ if (knownAgents.contains(agentComponentName)) {
+ continue;
+ }
+ newAgents.add(agentComponentName);
+ if (isSystemTrustAgent(agentInfo)) {
+ newSystemAgents.add(agentComponentName);
+ }
+ }
+
+ if (newAgents.isEmpty()) {
+ return;
+ }
+
+ ArraySet<ComponentName> updatedKnowAgents = new ArraySet<>(knownAgents);
+ updatedKnowAgents.addAll(newAgents);
+ mLockPatternUtils.setKnownTrustAgents(updatedKnowAgents, userId);
+
+ // Do not auto enable new trust agents when the default agent is set
+ boolean hasDefaultAgent = getDefaultFactoryTrustAgent(mContext) != null;
+ if (!hasDefaultAgent) {
+ enableNewAgents(newSystemAgents, userId);
+ }
+ }
+
+ private void enableNewAgents(Collection<ComponentName> agents, int userId) {
+ if (agents.isEmpty()) {
+ return;
+ }
+
+ ArraySet<ComponentName> agentsToEnable = new ArraySet<>(agents);
+ agentsToEnable.addAll(mLockPatternUtils.getEnabledTrustAgents(userId));
+ mLockPatternUtils.setEnabledTrustAgents(agentsToEnable, userId);
+ }
+
+ private void initializeKnownAgents(int userId) {
+ List<ResolveInfo> agentInfoList = resolveAllowedTrustAgents(mContext.getPackageManager(),
+ userId);
+ ArraySet<ComponentName> agentComponentNames = new ArraySet<>(agentInfoList.size());
+ for (ResolveInfo agentInfo : agentInfoList) {
+ agentComponentNames.add(getComponentName(agentInfo));
+ }
+ mLockPatternUtils.setKnownTrustAgents(agentComponentNames, userId);
+ Settings.Secure.putIntForUser(mContext.getContentResolver(),
+ Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, 1, userId);
+ }
+
/**
* Returns the {@link ComponentName} for the default trust agent, or {@code null} if there
* is no trust agent set.
@@ -1152,6 +1228,10 @@ public class TrustManagerService extends SystemService {
return allowedAgents;
}
+ private static boolean isSystemTrustAgent(ResolveInfo agentInfo) {
+ return (agentInfo.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+
// Agent dispatch and aggregation
private boolean aggregateIsTrusted(int userId) {
@@ -1825,7 +1905,13 @@ public class TrustManagerService extends SystemService {
}
@Override
+ public void onPackageAdded(String packageName, int uid) {
+ checkNewAgentsForUser(UserHandle.getUserId(uid));
+ }
+
+ @Override
public boolean onPackageChanged(String packageName, int uid, String[] components) {
+ checkNewAgentsForUser(UserHandle.getUserId(uid));
// We're interested in all changes, even if just some components get enabled / disabled.
return true;
}
@@ -1860,7 +1946,7 @@ public class TrustManagerService extends SystemService {
action)) {
int userId = getUserId(intent);
if (userId > 0) {
- maybeEnableFactoryTrustAgents(mLockPatternUtils, userId);
+ maybeEnableFactoryTrustAgents(userId);
}
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
int userId = getUserId(intent);
@@ -1997,7 +2083,7 @@ public class TrustManagerService extends SystemService {
if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) {
if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout");
mLockPatternUtils.requireStrongAuth(
- mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, mUserId);
+ mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_TRUSTAGENT_EXPIRED, mUserId);
}
maybeLockScreen(mUserId);
}
diff --git a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
index 948439da4863..2d022aed1442 100644
--- a/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
@@ -189,6 +189,13 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver {
if (!parent.exists()) {
throw new IOException("Failed to create directory " + parent.getCanonicalPath());
}
+
+ // Give executable permissions to parent folders.
+ while (!(parent.equals(updateDir))) {
+ parent.setExecutable(true, false);
+ parent = parent.getParentFile();
+ }
+
// create the temporary file
tmp = File.createTempFile("journal", "", dir);
// mark tmp -rw-r--r--
diff --git a/services/core/java/com/android/server/utils/AlarmQueue.java b/services/core/java/com/android/server/utils/AlarmQueue.java
index 3f4def6d60ab..09ba19508476 100644
--- a/services/core/java/com/android/server/utils/AlarmQueue.java
+++ b/services/core/java/com/android/server/utils/AlarmQueue.java
@@ -34,6 +34,7 @@ import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.function.Predicate;
@@ -52,14 +53,22 @@ public abstract class AlarmQueue<K> implements AlarmManager.OnAlarmListener {
private static final boolean DEBUG = false;
private static final long NOT_SCHEDULED = -1;
+ /**
+ * The threshold used to consider a new trigger time to be significantly different from the
+ * currently used trigger time.
+ */
+ private static final long SIGNIFICANT_TRIGGER_TIME_CHANGE_THRESHOLD_MS = MINUTE_IN_MILLIS;
/**
* Internal priority queue for each key's alarm, ordered by the time the alarm should go off.
* The pair is the key and its associated alarm time (in the elapsed realtime timebase).
*/
private static class AlarmPriorityQueue<Q> extends PriorityQueue<Pair<Q, Long>> {
+ private static final Comparator<Pair<?, Long>> sTimeComparator =
+ (o1, o2) -> Long.compare(o1.second, o2.second);
+
AlarmPriorityQueue() {
- super(1, (o1, o2) -> (int) (o1.second - o2.second));
+ super(1, sTimeComparator);
}
/**
@@ -306,8 +315,10 @@ public abstract class AlarmQueue<K> implements AlarmManager.OnAlarmListener {
// 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.
+ final long timeShiftThresholdMs =
+ Math.min(SIGNIFICANT_TRIGGER_TIME_CHANGE_THRESHOLD_MS, mMinTimeBetweenAlarmsMs);
if (mTriggerTimeElapsed == NOT_SCHEDULED
- || nextTriggerTimeElapsed < mTriggerTimeElapsed - MINUTE_IN_MILLIS
+ || nextTriggerTimeElapsed < mTriggerTimeElapsed - timeShiftThresholdMs
|| mTriggerTimeElapsed < nextTriggerTimeElapsed) {
if (DEBUG) {
Slog.d(TAG, "Scheduling alarm at " + nextTriggerTimeElapsed
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index fcf0a904b808..6012993db916 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -224,8 +224,6 @@ final class VibrationSettings {
public void onSystemReady() {
PowerManagerInternal pm = LocalServices.getService(PowerManagerInternal.class);
- VirtualDeviceManagerInternal vdm = LocalServices.getService(
- VirtualDeviceManagerInternal.class);
AudioManager am = mContext.getSystemService(AudioManager.class);
int ringerMode = am.getRingerModeInternal();
@@ -263,9 +261,11 @@ final class VibrationSettings {
}
});
- if (vdm != null){
- vdm.registerVirtualDisplayListener(mVirtualDeviceListener);
- vdm.registerAppsOnVirtualDeviceListener(mVirtualDeviceListener);
+ VirtualDeviceManagerInternal vdm = LocalServices.getService(
+ VirtualDeviceManagerInternal.class);
+ if (vdm != null) {
+ vdm.registerVirtualDisplayListener(mVirtualDeviceListener);
+ vdm.registerAppsOnVirtualDeviceListener(mVirtualDeviceListener);
}
registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
@@ -791,33 +791,35 @@ final class VibrationSettings {
mAppsOnVirtualDevice.clear();
mAppsOnVirtualDevice.addAll(allRunningUids);
}
-
-
}
/**
* @param uid: uid of the calling app.
* @param displayId: the id of a Display.
* @return Returns true if:
- * 1) the displayId is valid, and it's owned by a virtual device
- * 2) the displayId is invalid, and the calling app (uid) is running on a virtual device.
+ * <ul>
+ * <li> the displayId is valid, and it's owned by a virtual device.</li>
+ * <li> the displayId is invalid, and the calling app (uid) is running on a virtual
+ * device.</li>
+ * </ul>
*/
public boolean isAppOrDisplayOnAnyVirtualDevice(int uid, int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ // The default display is the primary physical display on the phone.
+ return false;
+ }
+
synchronized (mLock) {
- switch (displayId) {
- case Display.DEFAULT_DISPLAY:
- // The default display is the primary physical display on the phone.
- return false;
- case Display.INVALID_DISPLAY:
- // There is no Display object associated with the Context of calling
- // {@link SystemVibratorManager}, checking the calling UID instead.
- return mAppsOnVirtualDevice.contains(uid);
- default:
- // Other valid display IDs representing valid logical displays will be
- // checked
- // against the active virtual displays set built with the registered
- // {@link VirtualDisplayListener}.
- return mVirtualDisplays.contains(displayId);
+ if (displayId == Display.INVALID_DISPLAY) {
+ // There is no Display object associated with the Context of calling
+ // {@link SystemVibratorManager}, checking the calling UID instead.
+ return mAppsOnVirtualDevice.contains(uid);
+ } else {
+ // Other valid display IDs representing valid logical displays will be
+ // checked
+ // against the active virtual displays set built with the registered
+ // {@link VirtualDisplayListener}.
+ return mVirtualDisplays.contains(displayId);
}
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 804689a219b4..f1b011f17070 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -965,13 +965,6 @@ final class AccessibilityController {
availableBounds.op(navBarInsets, Region.Op.DIFFERENCE);
}
- // Count letterbox into nonMagnifiedBounds
- if (windowState.areAppWindowBoundsLetterboxed()) {
- Region letterboxBounds = getLetterboxBounds(windowState);
- nonMagnifiedBounds.op(letterboxBounds, Region.Op.UNION);
- availableBounds.op(letterboxBounds, Region.Op.DIFFERENCE);
- }
-
// Update accounted bounds
Region accountedBounds = mTempRegion2;
accountedBounds.set(mMagnificationRegion);
@@ -1411,20 +1404,6 @@ final class AccessibilityController {
return source != null ? source.getFrame() : EMPTY_RECT;
}
- static Region getLetterboxBounds(WindowState windowState) {
- final ActivityRecord appToken = windowState.mActivityRecord;
- if (appToken == null) {
- return new Region();
- }
- final Rect letterboxInsets = appToken.getLetterboxInsets();
- final Rect nonLetterboxRect = windowState.getBounds();
- nonLetterboxRect.inset(letterboxInsets);
- final Region letterboxBounds = new Region();
- letterboxBounds.set(windowState.getBounds());
- letterboxBounds.op(nonLetterboxRect, Region.Op.DIFFERENCE);
- return letterboxBounds;
- }
-
/**
* This class encapsulates the functionality related to computing the windows
* reported for accessibility purposes. These windows are all windows a sighted
@@ -1678,18 +1657,17 @@ final class AccessibilityController {
a11yWindow.getTouchableRegionInScreen(touchableRegion);
unaccountedSpace.op(touchableRegion, unaccountedSpace,
Region.Op.REVERSE_DIFFERENCE);
- // Account for the space of letterbox.
- final Region letterboxBounds = mTempRegion1;
- if (a11yWindow.setLetterBoxBoundsIfNeeded(letterboxBounds)) {
- unaccountedSpace.op(letterboxBounds,
- unaccountedSpace, Region.Op.REVERSE_DIFFERENCE);
- }
}
}
private static void addPopulatedWindowInfo(AccessibilityWindow a11yWindow,
Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut) {
final WindowInfo window = a11yWindow.getWindowInfo();
+ if (window.token == null) {
+ // The window was used in calculating visible windows but does not have an
+ // associated IWindow token, so exclude it from the list returned to accessibility.
+ return;
+ }
window.regionInScreen.set(regionInScreen);
window.layer = tokenOut.size();
out.add(window);
diff --git a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
index 79ae66250a63..d0c381e9debc 100644
--- a/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
+++ b/services/core/java/com/android/server/wm/AccessibilityWindowsPopulator.java
@@ -153,7 +153,11 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
for (InputWindowHandle window : windowHandles) {
final boolean visible = (window.inputConfig & InputConfig.NOT_VISIBLE) == 0;
final boolean isNotClone = (window.inputConfig & InputConfig.CLONE) == 0;
- if (visible && window.getWindow() != null && isNotClone) {
+ final boolean hasTouchableRegion = !window.touchableRegion.isEmpty();
+ final boolean hasNonEmptyFrame =
+ (window.frameBottom != window.frameTop) && (window.frameLeft
+ != window.frameRight);
+ if (visible && isNotClone && hasTouchableRegion && hasNonEmptyFrame) {
tempVisibleWindows.add(window);
}
}
@@ -321,10 +325,20 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
// the old and new windows at the same index should be the
// same, otherwise something changed.
for (int i = 0; i < windowsCount; i++) {
- final InputWindowHandle newWindow = newWindows.get(i);
- final InputWindowHandle oldWindow = oldWindows.get(i);
+ final IWindow newWindowToken = newWindows.get(i).getWindow();
+ final IWindow oldWindowToken = oldWindows.get(i).getWindow();
+ final boolean hasNewWindowToken = newWindowToken != null;
+ final boolean hasOldWindowToken = oldWindowToken != null;
- if (!newWindow.getWindow().asBinder().equals(oldWindow.getWindow().asBinder())) {
+ // If window token presence has changed then the windows have changed.
+ if (hasNewWindowToken != hasOldWindowToken) {
+ return true;
+ }
+
+ // If both old and new windows had window tokens, but those tokens differ,
+ // then the windows have changed.
+ if (hasNewWindowToken && hasOldWindowToken
+ && !newWindowToken.asBinder().equals(oldWindowToken.asBinder())) {
return true;
}
}
@@ -374,7 +388,8 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
for (int index = inputWindowHandles.size() - 1; index >= 0; index--) {
final Matrix windowTransformMatrix = mTempMatrix2;
final InputWindowHandle windowHandle = inputWindowHandles.get(index);
- final IBinder iBinder = windowHandle.getWindow().asBinder();
+ final IBinder iBinder =
+ windowHandle.getWindow() != null ? windowHandle.getWindow().asBinder() : null;
if (getWindowTransformMatrix(iBinder, windowTransformMatrix)) {
generateMagnificationSpecInverseMatrix(windowHandle, currentMagnificationSpec,
@@ -609,7 +624,6 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
private boolean mIgnoreDuetoRecentsAnimation;
private final Region mTouchableRegionInScreen = new Region();
private final Region mTouchableRegionInWindow = new Region();
- private final Region mLetterBoxBounds = new Region();
private WindowInfo mWindowInfo;
@@ -628,11 +642,11 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
final AccessibilityWindow instance = new AccessibilityWindow();
- instance.mWindow = inputWindowHandle.getWindow();
+ instance.mWindow = window;
instance.mDisplayId = inputWindowHandle.displayId;
instance.mInputConfig = inputWindowHandle.inputConfig;
instance.mType = inputWindowHandle.layoutParamsType;
- instance.mIsPIPMenu = inputWindowHandle.getWindow().asBinder().equals(pipIBinder);
+ instance.mIsPIPMenu = window != null && window.asBinder().equals(pipIBinder);
// TODO (b/199357848): gets the private flag of the window from other way.
instance.mPrivateFlags = windowState != null ? windowState.mAttrs.privateFlags : 0;
@@ -644,11 +658,6 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
instance.mIgnoreDuetoRecentsAnimation = windowState != null && controller != null
&& controller.shouldIgnoreForAccessibility(windowState);
- // TODO (b/199358388) : gets the letterbox bounds of the window from other way.
- if (windowState != null && windowState.areAppWindowBoundsLetterboxed()) {
- getLetterBoxBounds(windowState, instance.mLetterBoxBounds);
- }
-
final Rect windowFrame = new Rect(inputWindowHandle.frameLeft,
inputWindowHandle.frameTop, inputWindowHandle.frameRight,
inputWindowHandle.frameBottom);
@@ -724,21 +733,6 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
}
/**
- * Gets the letter box bounds if activity bounds are letterboxed
- * or letterboxed for display cutout.
- *
- * @return {@code true} there's a letter box bounds.
- */
- public Boolean setLetterBoxBoundsIfNeeded(Region outBounds) {
- if (mLetterBoxBounds.isEmpty()) {
- return false;
- }
-
- outBounds.set(mLetterBoxBounds);
- return true;
- }
-
- /**
* @return true if this window should be magnified.
*/
public boolean shouldMagnify() {
@@ -841,7 +835,7 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
WindowInfo windowInfo = WindowInfo.obtain();
windowInfo.displayId = window.mDisplayId;
windowInfo.type = window.mType;
- windowInfo.token = window.mWindow.asBinder();
+ windowInfo.token = window.mWindow != null ? window.mWindow.asBinder() : null;
windowInfo.hasFlagWatchOutsideTouch = (window.mInputConfig
& InputConfig.WATCH_OUTSIDE_TOUCH) != 0;
// Set it to true to be consistent with the legacy implementation.
@@ -849,18 +843,11 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
return windowInfo;
}
- private static void getLetterBoxBounds(WindowState windowState, Region outRegion) {
- final Rect letterboxInsets = windowState.mActivityRecord.getLetterboxInsets();
- final Rect nonLetterboxRect = windowState.getBounds();
-
- nonLetterboxRect.inset(letterboxInsets);
- outRegion.set(windowState.getBounds());
- outRegion.op(nonLetterboxRect, Region.Op.DIFFERENCE);
- }
-
@Override
public String toString() {
- String builder = "A11yWindow=[" + mWindow.asBinder()
+ String windowToken =
+ mWindow != null ? mWindow.asBinder().toString() : "(no window token)";
+ return "A11yWindow=[" + windowToken
+ ", displayId=" + mDisplayId
+ ", inputConfig=0x" + Integer.toHexString(mInputConfig)
+ ", type=" + mType
@@ -871,12 +858,9 @@ public final class AccessibilityWindowsPopulator extends WindowInfosListener {
+ ", isTrustedOverlay=" + isTrustedOverlay()
+ ", regionInScreen=" + mTouchableRegionInScreen
+ ", touchableRegion=" + mTouchableRegionInWindow
- + ", letterBoxBounds=" + mLetterBoxBounds
+ ", isPIPMenu=" + mIsPIPMenu
+ ", windowInfo=" + mWindowInfo
+ "]";
-
- return builder;
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index f8cbd8b3dd48..08d2e69846d3 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -197,10 +197,16 @@ class ActivityMetricsLogger {
* launched successfully.
*/
static final class LaunchingState {
- /** The device uptime of {@link #notifyActivityLaunching}. */
- private final long mCurrentUpTimeMs = SystemClock.uptimeMillis();
- /** The timestamp of {@link #notifyActivityLaunching}. */
- private long mCurrentTransitionStartTimeNs;
+ /**
+ * The device uptime of {@link #notifyActivityLaunching}. It can be used as a key for
+ * observer to identify which callbacks belong to a launch event.
+ */
+ final long mStartUptimeNs = SystemClock.uptimeNanos();
+ /**
+ * The timestamp of {@link #notifyActivityLaunching}. It is used to provide the time
+ * relative to the wall-time.
+ */
+ final long mStartRealtimeNs = SystemClock.elapsedRealtimeNanos();
/** Non-null when a {@link TransitionInfo} is created for this state. */
private TransitionInfo mAssociatedTransitionInfo;
/** The sequence id for trace. It is used to map the traces before resolving intent. */
@@ -231,9 +237,21 @@ class ActivityMetricsLogger {
if (mAssociatedTransitionInfo == null) {
launchResult = ":failed";
} else {
- launchResult = (abort ? ":canceled:" : mAssociatedTransitionInfo.mProcessSwitch
- ? ":completed:" : ":completed-same-process:")
- + mAssociatedTransitionInfo.mLastLaunchedActivity.packageName;
+ final String status;
+ if (abort) {
+ status = ":canceled:";
+ } else if (!mAssociatedTransitionInfo.mProcessSwitch) {
+ status = ":completed-same-process:";
+ } else {
+ if (endInfo.mTransitionType == TYPE_TRANSITION_HOT_LAUNCH) {
+ status = ":completed-hot:";
+ } else if (endInfo.mTransitionType == TYPE_TRANSITION_WARM_LAUNCH) {
+ status = ":completed-warm:";
+ } else {
+ status = ":completed-cold:";
+ }
+ }
+ launchResult = status + mAssociatedTransitionInfo.mLastLaunchedActivity.packageName;
}
// Put a supplement trace as the description of the async trace with the same id.
Trace.instant(Trace.TRACE_TAG_ACTIVITY_MANAGER, mTraceName + launchResult);
@@ -263,13 +281,7 @@ class ActivityMetricsLogger {
* @see LaunchingState#mAssociatedTransitionInfo
*/
final LaunchingState mLaunchingState;
- /**
- * The timestamp of the first {@link #notifyActivityLaunching}. It can be used as a key for
- * observer to identify which callbacks belong to a launch event.
- */
- final long mTransitionStartTimeNs;
- /** The device uptime in millis when this transition info is created. */
- final long mTransitionDeviceUptimeMs;
+
/** The type can be cold (new process), warm (new activity), or hot (bring to front). */
final int mTransitionType;
/** Whether the process was already running when the transition started. */
@@ -289,11 +301,11 @@ class ActivityMetricsLogger {
@SourceInfo.SourceType int mSourceType;
/** The time from the source event (e.g. touch) to {@link #notifyActivityLaunching}. */
int mSourceEventDelayMs = INVALID_DELAY;
- /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyTransitionStarting}. */
+ /** The time from {@link #notifyActivityLaunching} to {@link #notifyTransitionStarting}. */
int mCurrentTransitionDelayMs;
- /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyStartingWindowDrawn}. */
+ /** The time from {@link #notifyActivityLaunching} to {@link #notifyStartingWindowDrawn}. */
int mStartingWindowDelayMs = INVALID_DELAY;
- /** The time from {@link #mTransitionStartTimeNs} to {@link #notifyBindApplication}. */
+ /** The time from {@link #notifyActivityLaunching} to {@link #notifyBindApplication}. */
int mBindApplicationDelayMs = INVALID_DELAY;
/** Elapsed time from when we launch an activity to when its windows are drawn. */
int mWindowsDrawnDelayMs;
@@ -339,13 +351,11 @@ class ActivityMetricsLogger {
ActivityOptions options, int transitionType, boolean processRunning,
boolean processSwitch, int processState, int processOomAdj) {
mLaunchingState = launchingState;
- mTransitionStartTimeNs = launchingState.mCurrentTransitionStartTimeNs;
mTransitionType = transitionType;
mProcessRunning = processRunning;
mProcessSwitch = processSwitch;
mProcessState = processState;
mProcessOomAdj = processOomAdj;
- mTransitionDeviceUptimeMs = launchingState.mCurrentUpTimeMs;
setLatestLaunchedActivity(r);
// The launching state can be reused by consecutive launch. Its original association
// shouldn't be changed by a separated transition.
@@ -356,8 +366,8 @@ class ActivityMetricsLogger {
final SourceInfo sourceInfo = options.getSourceInfo();
if (sourceInfo != null) {
mSourceType = sourceInfo.type;
- mSourceEventDelayMs =
- (int) (launchingState.mCurrentUpTimeMs - sourceInfo.eventTimeMs);
+ mSourceEventDelayMs = (int) (TimeUnit.NANOSECONDS.toMillis(
+ launchingState.mStartUptimeNs) - sourceInfo.eventTimeMs);
}
}
}
@@ -403,12 +413,13 @@ class ActivityMetricsLogger {
}
int calculateCurrentDelay() {
- return calculateDelay(SystemClock.elapsedRealtimeNanos());
+ return calculateDelay(SystemClock.uptimeNanos());
}
int calculateDelay(long timestampNs) {
// Shouldn't take more than 25 days to launch an app, so int is fine here.
- return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs - mTransitionStartTimeNs);
+ return (int) TimeUnit.NANOSECONDS.toMillis(
+ timestampNs - mLaunchingState.mStartUptimeNs);
}
@Override
@@ -583,7 +594,6 @@ class ActivityMetricsLogger {
*/
LaunchingState notifyActivityLaunching(Intent intent, @Nullable ActivityRecord caller,
int callingUid) {
- final long transitionStartTimeNs = SystemClock.elapsedRealtimeNanos();
TransitionInfo existingInfo = null;
if (callingUid != IGNORE_CALLER) {
// Associate the launching event to an active transition if the caller is found in its
@@ -608,12 +618,10 @@ class ActivityMetricsLogger {
if (existingInfo == null) {
final LaunchingState launchingState = new LaunchingState();
- launchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs;
// Only notify the observer for a new launching event.
- launchObserverNotifyIntentStarted(intent, transitionStartTimeNs);
+ launchObserverNotifyIntentStarted(intent, launchingState.mStartUptimeNs);
return launchingState;
}
- existingInfo.mLaunchingState.mCurrentTransitionStartTimeNs = transitionStartTimeNs;
return existingInfo.mLaunchingState;
}
@@ -716,7 +724,7 @@ class ActivityMetricsLogger {
launchObserverNotifyActivityLaunched(newInfo);
} else {
// As abort for no process switch.
- launchObserverNotifyIntentFailed(newInfo.mTransitionStartTimeNs);
+ launchObserverNotifyIntentFailed(newInfo.mLaunchingState.mStartUptimeNs);
}
scheduleCheckActivityToBeDrawnIfSleeping(launchedActivity);
@@ -749,9 +757,10 @@ class ActivityMetricsLogger {
* to invisible (removed from active transition) or it was already drawn.
*/
@Nullable
- TransitionInfoSnapshot notifyWindowsDrawn(@NonNull ActivityRecord r, long timestampNs) {
+ TransitionInfoSnapshot notifyWindowsDrawn(@NonNull ActivityRecord r) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn " + r);
+ final long timestampNs = SystemClock.uptimeNanos();
final TransitionInfo info = getActiveTransitionInfo(r);
if (info == null || info.mIsDrawn) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn not pending drawn " + info);
@@ -778,7 +787,7 @@ class ActivityMetricsLogger {
}
if (DEBUG_METRICS) Slog.i(TAG, "notifyStartingWindowDrawn " + r);
info.mLoggedStartingWindowDrawn = true;
- info.mStartingWindowDelayMs = info.calculateDelay(SystemClock.elapsedRealtimeNanos());
+ info.mStartingWindowDelayMs = info.calculateCurrentDelay();
}
/**
@@ -790,7 +799,7 @@ class ActivityMetricsLogger {
void notifyTransitionStarting(ArrayMap<WindowContainer, Integer> activityToReason) {
if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting " + activityToReason);
- final long timestampNs = SystemClock.elapsedRealtimeNanos();
+ final long timestampNs = SystemClock.uptimeNanos();
for (int index = activityToReason.size() - 1; index >= 0; index--) {
final WindowContainer<?> wc = activityToReason.keyAt(index);
final ActivityRecord activity = wc.asActivityRecord();
@@ -948,7 +957,7 @@ class ActivityMetricsLogger {
}
if (DEBUG_METRICS) Slog.i(TAG, "abort launch cause=" + cause);
state.stopTrace(true /* abort */, null /* endInfo */);
- launchObserverNotifyIntentFailed(state.mCurrentTransitionStartTimeNs);
+ launchObserverNotifyIntentFailed(state.mStartUptimeNs);
}
/** Aborts tracking of current launch metrics. */
@@ -1013,13 +1022,13 @@ class ActivityMetricsLogger {
// This will avoid any races with other operations that modify the ActivityRecord.
final TransitionInfoSnapshot infoSnapshot = new TransitionInfoSnapshot(info);
if (info.isInterestingToLoggerAndObserver()) {
- final long timestamp = info.mTransitionStartTimeNs;
- final long uptime = info.mTransitionDeviceUptimeMs;
+ final long timestampNs = info.mLaunchingState.mStartRealtimeNs;
+ final long uptimeNs = info.mLaunchingState.mStartUptimeNs;
final int transitionDelay = info.mCurrentTransitionDelayMs;
final int processState = info.mProcessState;
final int processOomAdj = info.mProcessOomAdj;
mLoggerHandler.post(() -> logAppTransition(
- timestamp, uptime, transitionDelay, infoSnapshot, isHibernating,
+ timestampNs, uptimeNs, transitionDelay, infoSnapshot, isHibernating,
processState, processOomAdj));
}
mLoggerHandler.post(() -> logAppDisplayed(infoSnapshot));
@@ -1031,7 +1040,7 @@ class ActivityMetricsLogger {
}
// This gets called on another thread without holding the activity manager lock.
- private void logAppTransition(long transitionStartTimeNs, long transitionDeviceUptimeMs,
+ private void logAppTransition(long transitionStartTimeNs, long transitionDeviceUptimeNs,
int currentTransitionDelayMs, TransitionInfoSnapshot info, boolean isHibernating,
int processState, int processOomAdj) {
final LogMaker builder = new LogMaker(APP_TRANSITION);
@@ -1049,7 +1058,7 @@ class ActivityMetricsLogger {
}
builder.addTaggedData(APP_TRANSITION_IS_EPHEMERAL, isInstantApp ? 1 : 0);
builder.addTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS,
- TimeUnit.MILLISECONDS.toSeconds(transitionDeviceUptimeMs));
+ TimeUnit.NANOSECONDS.toSeconds(transitionDeviceUptimeNs));
builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs);
builder.setSubtype(info.reason);
if (info.startingWindowDelayMs != INVALID_DELAY) {
@@ -1188,10 +1197,11 @@ class ActivityMetricsLogger {
return null;
}
- final long currentTimestampNs = SystemClock.elapsedRealtimeNanos();
+ final long currentTimestampNs = SystemClock.uptimeNanos();
final long startupTimeMs = info.mPendingFullyDrawn != null
? info.mWindowsDrawnDelayMs
- : TimeUnit.NANOSECONDS.toMillis(currentTimestampNs - info.mTransitionStartTimeNs);
+ : TimeUnit.NANOSECONDS.toMillis(
+ currentTimestampNs - info.mLaunchingState.mStartUptimeNs);
final TransitionInfoSnapshot infoSnapshot =
new TransitionInfoSnapshot(info, r, (int) startupTimeMs);
mLoggerHandler.post(() -> logAppFullyDrawn(infoSnapshot));
@@ -1603,7 +1613,7 @@ class ActivityMetricsLogger {
}
info.mLaunchTraceName = "launching: " + info.mLastLaunchedActivity.packageName;
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, info.mLaunchTraceName,
- (int) info.mTransitionStartTimeNs /* cookie */);
+ (int) info.mLaunchingState.mStartRealtimeNs /* cookie */);
}
/** Stops trace for the launch is completed or cancelled. */
@@ -1613,7 +1623,7 @@ class ActivityMetricsLogger {
return;
}
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, info.mLaunchTraceName,
- (int) info.mTransitionStartTimeNs /* cookie */);
+ (int) info.mLaunchingState.mStartRealtimeNs /* cookie */);
info.mLaunchTraceName = null;
}
@@ -1658,7 +1668,7 @@ class ActivityMetricsLogger {
convertTransitionTypeToLaunchObserverTemperature(info.mTransitionType);
// Beginning a launch is timing sensitive and so should be observed as soon as possible.
- mLaunchObserver.onActivityLaunched(info.mTransitionStartTimeNs,
+ mLaunchObserver.onActivityLaunched(info.mLaunchingState.mStartUptimeNs,
info.mLastLaunchedActivity.mActivityComponent, temperature);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
@@ -1670,7 +1680,7 @@ class ActivityMetricsLogger {
private void launchObserverNotifyReportFullyDrawn(TransitionInfo info, long timestampNs) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyReportFullyDrawn");
- mLaunchObserver.onReportFullyDrawn(info.mTransitionStartTimeNs, timestampNs);
+ mLaunchObserver.onReportFullyDrawn(info.mLaunchingState.mStartUptimeNs, timestampNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1682,7 +1692,7 @@ class ActivityMetricsLogger {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyActivityLaunchCancelled");
- mLaunchObserver.onActivityLaunchCancelled(info.mTransitionStartTimeNs);
+ mLaunchObserver.onActivityLaunchCancelled(info.mLaunchingState.mStartUptimeNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
@@ -1695,7 +1705,7 @@ class ActivityMetricsLogger {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"MetricsLogger:launchObserverNotifyActivityLaunchFinished");
- mLaunchObserver.onActivityLaunchFinished(info.mTransitionStartTimeNs,
+ mLaunchObserver.onActivityLaunchFinished(info.mLaunchingState.mStartUptimeNs,
info.mLastLaunchedActivity.mActivityComponent, timestampNs);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 16b5ee54a5ba..9ed77fc77258 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1354,10 +1354,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return true;
}
- void setAppTimeTracker(AppTimeTracker att) {
- appTimeTracker = att;
- }
-
/** Update the saved state of an activity. */
void setSavedState(@Nullable Bundle savedState) {
mIcicle = savedState;
@@ -1522,8 +1518,17 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
this.task = newTask;
if (shouldStartChangeTransition(newParent, oldParent)) {
- // Animate change transition on TaskFragment level to get the correct window crop.
- newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
+ if (mTransitionController.isShellTransitionsEnabled()) {
+ // For Shell transition, call #initializeChangeTransition directly to take the
+ // screenshot at the Activity level. And Shell will be in charge of handling the
+ // surface reparent and crop.
+ initializeChangeTransition(getBounds());
+ } else {
+ // For legacy app transition, we want to take a screenshot of the Activity surface,
+ // but animate the change transition on TaskFragment level to get the correct window
+ // crop.
+ newParent.initializeChangeTransition(getBounds(), getSurfaceControl());
+ }
}
super.onParentChanged(newParent, oldParent);
@@ -1577,11 +1582,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (oldParent != null) {
oldParent.cleanUpActivityReferences(this);
+ // Update isVisibleRequested value of parent TaskFragment and send the callback to the
+ // client side if needed.
+ oldParent.onActivityVisibleRequestedChanged();
}
- if (newParent != null && isState(RESUMED)) {
- newParent.setResumedActivity(this, "onParentChanged");
- mImeInsetsFrozenUntilStartInput = false;
+ if (newParent != null) {
+ // Update isVisibleRequested value of parent TaskFragment and send the callback to the
+ // client side if needed.
+ newParent.onActivityVisibleRequestedChanged();
+ if (isState(RESUMED)) {
+ newParent.setResumedActivity(this, "onParentChanged");
+ mImeInsetsFrozenUntilStartInput = false;
+ }
}
if (rootTask != null && rootTask.topRunningActivity() == this) {
@@ -2746,6 +2759,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
final StartingSurfaceController.StartingSurface surface;
+ final WindowState startingWindow = mStartingWindow;
final boolean animate;
if (mStartingData != null) {
animate = prepareAnimation && mStartingData.needRevealAnimation()
@@ -2770,7 +2784,19 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
- surface.remove(animate);
+ if (animate && mTransitionController.inCollectingTransition(startingWindow)
+ && startingWindow.cancelAndRedraw()) {
+ // Defer remove starting window after transition start.
+ // If splash screen window was in collecting, the client side is unable to draw because
+ // of Session#cancelDraw, which will blocking the remove animation.
+ startingWindow.mSyncTransaction.addTransactionCommittedListener(Runnable::run, () -> {
+ synchronized (mAtmService.mGlobalLock) {
+ surface.remove(true);
+ }
+ });
+ } else {
+ surface.remove(animate);
+ }
}
/**
@@ -5072,6 +5098,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return;
}
mVisibleRequested = visible;
+ final TaskFragment taskFragment = getTaskFragment();
+ if (taskFragment != null) {
+ taskFragment.onActivityVisibleRequestedChanged();
+ }
setInsetsFrozen(!visible);
if (app != null) {
mTaskSupervisor.onProcessActivityStateChanged(app, false /* forceBatch */);
@@ -6527,9 +6557,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
/** Called when the windows associated app window container are drawn. */
- private void onWindowsDrawn(long timestampNs) {
+ private void onWindowsDrawn() {
final TransitionInfoSnapshot info = mTaskSupervisor
- .getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
+ .getActivityMetricsLogger().notifyWindowsDrawn(this);
final boolean validInfo = info != null;
final int windowsDrawnDelayMs = validInfo ? info.windowsDrawnDelayMs : INVALID_DELAY;
final @WaitResult.LaunchState int launchState =
@@ -6656,7 +6686,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
+ numInteresting + " visible=" + numVisible);
if (nowDrawn != mReportedDrawn) {
if (nowDrawn) {
- onWindowsDrawn(SystemClock.elapsedRealtimeNanos());
+ onWindowsDrawn();
}
mReportedDrawn = nowDrawn;
}
@@ -6767,27 +6797,37 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* @return True if input dispatching should be aborted.
*/
public boolean inputDispatchingTimedOut(TimeoutRecord timeoutRecord, int windowPid) {
- ActivityRecord anrActivity;
- WindowProcessController anrApp;
- boolean blameActivityProcess;
- synchronized (mAtmService.mGlobalLock) {
- anrActivity = getWaitingHistoryRecordLocked();
- anrApp = app;
- blameActivityProcess = hasProcess()
- && (app.getPid() == windowPid || windowPid == INVALID_PID);
- }
-
- if (blameActivityProcess) {
- return mAtmService.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
- anrActivity.shortComponentName, anrActivity.info.applicationInfo,
- shortComponentName, app, false, timeoutRecord);
- } else {
- // In this case another process added windows using this activity token. So, we call the
- // generic service input dispatch timed out method so that the right process is blamed.
- long timeoutMillis = mAtmService.mAmInternal.inputDispatchingTimedOut(
- windowPid, false /* aboveSystem */, timeoutRecord);
- return timeoutMillis <= 0;
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+ "ActivityRecord#inputDispatchingTimedOut()");
+ ActivityRecord anrActivity;
+ WindowProcessController anrApp;
+ boolean blameActivityProcess;
+ timeoutRecord.mLatencyTracker.waitingOnGlobalLockStarted();
+ synchronized (mAtmService.mGlobalLock) {
+ timeoutRecord.mLatencyTracker.waitingOnGlobalLockEnded();
+ anrActivity = getWaitingHistoryRecordLocked();
+ anrApp = app;
+ blameActivityProcess = hasProcess()
+ && (app.getPid() == windowPid || windowPid == INVALID_PID);
+ }
+
+ if (blameActivityProcess) {
+ return mAtmService.mAmInternal.inputDispatchingTimedOut(anrApp.mOwner,
+ anrActivity.shortComponentName, anrActivity.info.applicationInfo,
+ shortComponentName, app, false, timeoutRecord);
+ } else {
+ // In this case another process added windows using this activity token.
+ // So, we call the generic service input dispatch timed out method so
+ // that the right process is blamed.
+ long timeoutMillis = mAtmService.mAmInternal.inputDispatchingTimedOut(
+ windowPid, false /* aboveSystem */, timeoutRecord);
+ return timeoutMillis <= 0;
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
+
}
private ActivityRecord getWaitingHistoryRecordLocked() {
@@ -8024,11 +8064,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Horizontal position
int offsetX = 0;
if (parentBounds.width() != screenResolvedBounds.width()) {
- if (screenResolvedBounds.width() >= parentAppBounds.width()) {
- // If resolved bounds overlap with insets, center within app bounds.
- offsetX = getCenterOffset(
- parentAppBounds.width(), screenResolvedBounds.width());
- } else {
+ if (screenResolvedBounds.width() <= parentAppBounds.width()) {
float positionMultiplier =
mLetterboxUiController.getHorizontalPositionMultiplier(
newParentConfiguration);
@@ -8040,11 +8076,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Vertical position
int offsetY = 0;
if (parentBounds.height() != screenResolvedBounds.height()) {
- if (screenResolvedBounds.height() >= parentAppBounds.height()) {
- // If resolved bounds overlap with insets, center within app bounds.
- offsetY = getCenterOffset(
- parentAppBounds.height(), screenResolvedBounds.height());
- } else {
+ if (screenResolvedBounds.height() <= parentAppBounds.height()) {
float positionMultiplier =
mLetterboxUiController.getVerticalPositionMultiplier(
newParentConfiguration);
@@ -8062,6 +8094,15 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
offsetBounds(resolvedConfig, offsetX, offsetY);
}
+ // If the top is aligned with parentAppBounds add the vertical insets back so that the app
+ // content aligns with the status bar
+ if (resolvedConfig.windowConfiguration.getAppBounds().top == parentAppBounds.top) {
+ resolvedConfig.windowConfiguration.getBounds().top = parentBounds.top;
+ if (mSizeCompatBounds != null) {
+ mSizeCompatBounds.top = parentBounds.top;
+ }
+ }
+
// Since bounds has changed, the configuration needs to be computed accordingly.
getTaskFragment().computeConfigResourceOverrides(resolvedConfig, newParentConfiguration);
}
@@ -8439,7 +8480,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Above coordinates are in "@" space, now place "*" and "#" to screen space.
final boolean fillContainer = resolvedBounds.equals(containingBounds);
final int screenPosX = fillContainer ? containerBounds.left : containerAppBounds.left;
- final int screenPosY = fillContainer ? containerBounds.top : containerAppBounds.top;
+ final int screenPosY = fillContainer ? containerBounds.top : containerAppBounds.top;
if (screenPosX != 0 || screenPosY != 0) {
if (mSizeCompatBounds != null) {
@@ -8785,24 +8826,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Also account for the insets (e.g. display cutouts, navigation bar), which will be
// clipped away later in {@link Task#computeConfigResourceOverrides()}, i.e., the out
// bounds are the app bounds restricted by aspect ratio + clippable insets. Otherwise,
- // the app bounds would end up too small.
+ // the app bounds would end up too small. To achieve this we will also add clippable insets
+ // when the corresponding dimension fully fills the parent
+
int right = activityWidth + containingAppBounds.left;
+ int left = containingAppBounds.left;
if (right >= containingAppBounds.right) {
- right += containingBounds.right - containingAppBounds.right;
+ right = containingBounds.right;
+ left = containingBounds.left;
}
int bottom = activityHeight + containingAppBounds.top;
+ int top = containingAppBounds.top;
if (bottom >= containingAppBounds.bottom) {
- bottom += containingBounds.bottom - containingAppBounds.bottom;
- }
- outBounds.set(containingBounds.left, containingBounds.top, right, bottom);
-
- // 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 (e.g. display cutout).
- outBounds.left = containingAppBounds.left;
+ bottom = containingBounds.bottom;
+ top = containingBounds.top;
}
-
+ outBounds.set(left, top, right, bottom);
return true;
}
@@ -9519,7 +9558,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
@Override
boolean showToCurrentUser() {
- return mShowForAllUsers || mWmService.isCurrentProfile(mUserId);
+ return mShowForAllUsers || mWmService.isUserVisible(mUserId);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index bc1c6b2e8f3a..1d70146d5183 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -892,9 +892,10 @@ class ActivityStarter {
final int userId = aInfo != null && aInfo.applicationInfo != null
? UserHandle.getUserId(aInfo.applicationInfo.uid) : 0;
+ final int launchMode = aInfo != null ? aInfo.launchMode : 0;
if (err == ActivityManager.START_SUCCESS) {
Slog.i(TAG, "START u" + userId + " {" + intent.toShortString(true, true, true, false)
- + "} from uid " + callingUid);
+ + "} with " + launchModeToString(launchMode) + " from uid " + callingUid);
}
ActivityRecord sourceRecord = null;
@@ -1847,7 +1848,7 @@ class ActivityStarter {
ActivityRecord targetTopActivity =
targetTask != null ? targetTask.getTopNonFinishingActivity() : null;
boolean passesAsmChecks = newTask
- ? mRootWindowContainer.hasResumedActivity(callerUid)
+ ? mService.mVisibleActivityProcessTracker.hasResumedActivity(callerUid)
: targetTopActivity != null && targetTopActivity.getUid() == callerUid;
if (!passesAsmChecks) {
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8f5d838488e2..83953022dd72 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -68,6 +68,7 @@ import static android.view.WindowManager.TRANSIT_PIP;
import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_LAUNCHER_CLEAR_SNAPSHOT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_DREAM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
@@ -1445,6 +1446,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
boolean canLaunchDreamActivity(String packageName) {
if (!mDreaming || packageName == null) {
+ ProtoLog.e(WM_DEBUG_DREAM, "Cannot launch dream activity due to invalid state. "
+ + "dreaming: %b packageName: %s", mDreaming, packageName);
return false;
}
final DreamManagerInternal dreamManager =
@@ -1460,6 +1463,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (activeDoze != null && packageName.equals(activeDoze.getPackageName())) {
return true;
}
+ ProtoLog.e(WM_DEBUG_DREAM,
+ "Dream packageName does not match active dream. Package %s does not match %s or %s",
+ packageName, String.valueOf(activeDream), String.valueOf(activeDoze));
return false;
}
@@ -5296,7 +5302,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub {
if (app != null && app.getPid() > 0) {
ArrayList<Integer> firstPids = new ArrayList<Integer>();
firstPids.add(app.getPid());
- dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null);
+ dumpStackTraces(tracesFile.getAbsolutePath(), firstPids, null, null, null);
}
File lastTracesFile = null;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index dc91c1597128..b473700c3074 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -139,7 +139,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.ReferrerIntent;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.am.ActivityManagerService;
@@ -744,7 +743,7 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
// (e.g. AMS.startActivityAsUser).
final long token = Binder.clearCallingIdentity();
try {
- return mService.getPackageManagerInternalLocked().resolveIntent(
+ return mService.getPackageManagerInternalLocked().resolveIntentExported(
intent, resolvedType, modifiedFlags, privateResolveFlags, userId, true,
filterCallingUid);
} finally {
@@ -1183,10 +1182,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
if (!displayContent.isPrivate()) {
- // Anyone can launch on a public display.
- ProtoLog.d(WM_DEBUG_TASKS, "Launch on display check: allow launch on public "
- + "display");
- return true;
+ // Checks if the caller can be shown in the given public display.
+ int userId = UserHandle.getUserId(callingUid);
+ int displayId = display.getDisplayId();
+ boolean allowed = mWindowManager.mUmInternal.isUserVisible(userId, displayId);
+ ProtoLog.d(WM_DEBUG_TASKS,
+ "Launch on display check: %s launch for userId=%d on displayId=%d",
+ (allowed ? "allow" : "disallow"), userId, displayId);
+ return allowed;
}
// Check if the caller is the owner of the display.
@@ -1574,17 +1577,12 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
if (rootTask.getWindowingMode() == WINDOWING_MODE_PINNED) {
removePinnedRootTaskInSurfaceTransaction(rootTask);
} else {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityTaskSupervisor::processRemoveTask, this, PooledLambda.__(Task.class));
- rootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
+ rootTask.forAllLeafTasks(task -> {
+ removeTask(task, true /* killProcess */, REMOVE_FROM_RECENTS, "remove-root-task");
+ }, true /* traverseTopToBottom */);
}
}
- private void processRemoveTask(Task task) {
- removeTask(task, true /* killProcess */, REMOVE_FROM_RECENTS, "remove-root-task");
- }
-
/**
* Removes the root task associated with the given {@param task}. If the {@param task} is the
* pinned task, then its child tasks are not explicitly removed when the root task is
@@ -2264,23 +2262,17 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
void scheduleUpdateMultiWindowMode(Task task) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityTaskSupervisor::addToMultiWindowModeChangedList, this,
- PooledLambda.__(ActivityRecord.class));
- task.forAllActivities(c);
- c.recycle();
+ task.forAllActivities(r -> {
+ if (r.attachedToProcess()) {
+ mMultiWindowModeChangedActivities.add(r);
+ }
+ });
if (!mHandler.hasMessages(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG)) {
mHandler.sendEmptyMessage(REPORT_MULTI_WINDOW_MODE_CHANGED_MSG);
}
}
- private void addToMultiWindowModeChangedList(ActivityRecord r) {
- if (r.attachedToProcess()) {
- mMultiWindowModeChangedActivities.add(r);
- }
- }
-
void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Task prevRootTask) {
final Task rootTask = task.getRootTask();
if ((prevRootTask == null || (prevRootTask != rootTask
@@ -2292,11 +2284,14 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
void scheduleUpdatePictureInPictureModeIfNeeded(Task task, Rect targetRootTaskBounds) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- ActivityTaskSupervisor::addToPipModeChangedList, this,
- PooledLambda.__(ActivityRecord.class));
- task.forAllActivities(c);
- c.recycle();
+ task.forAllActivities(r -> {
+ if (!r.attachedToProcess()) return;
+ mPipModeChangedActivities.add(r);
+ // If we are scheduling pip change, then remove this activity from multi-window
+ // change list as the processing of pip change will make sure multi-window changed
+ // message is processed in the right order relative to pip changed.
+ mMultiWindowModeChangedActivities.remove(r);
+ });
mPipModeChangedTargetRootTaskBounds = targetRootTaskBounds;
@@ -2305,16 +2300,6 @@ public class ActivityTaskSupervisor implements RecentTasks.Callbacks {
}
}
- private void addToPipModeChangedList(ActivityRecord r) {
- if (!r.attachedToProcess()) return;
-
- mPipModeChangedActivities.add(r);
- // If we are scheduling pip change, then remove this activity from multi-window
- // change list as the processing of pip change will make sure multi-window changed
- // message is processed in the right order relative to pip changed.
- mMultiWindowModeChangedActivities.remove(r);
- }
-
void wakeUp(String reason) {
mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_APPLICATION,
"android.server.am:TURN_ON:" + reason);
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index 01098dedea6e..df7226012181 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -27,6 +27,7 @@ import android.os.Build;
import android.os.IBinder;
import android.os.Process;
import android.os.SystemClock;
+import android.os.Trace;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
@@ -64,47 +65,55 @@ class AnrController {
void notifyAppUnresponsive(InputApplicationHandle applicationHandle,
TimeoutRecord timeoutRecord) {
- preDumpIfLockTooSlow();
- final ActivityRecord activity;
- synchronized (mService.mGlobalLock) {
- activity = ActivityRecord.forTokenLocked(applicationHandle.token);
- if (activity == null) {
- Slog.e(TAG_WM, "Unknown app appToken:" + applicationHandle.name
- + ". Dropping notifyNoFocusedWindowAnr request");
- return;
- }
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "notifyAppUnresponsive()");
+ preDumpIfLockTooSlow();
+ final ActivityRecord activity;
+ timeoutRecord.mLatencyTracker.waitingOnGlobalLockStarted();
+ synchronized (mService.mGlobalLock) {
+ timeoutRecord.mLatencyTracker.waitingOnGlobalLockEnded();
+ activity = ActivityRecord.forTokenLocked(applicationHandle.token);
+ if (activity == null) {
+ Slog.e(TAG_WM, "Unknown app appToken:" + applicationHandle.name
+ + ". Dropping notifyNoFocusedWindowAnr request");
+ return;
+ }
- // App is unresponsive, but we are actively trying to give focus to a window.
- // Blame the window if possible since the window may not belong to the app.
- DisplayContent display = mService.mRoot.getDisplayContent(activity.getDisplayId());
- IBinder focusToken = display == null ? null : display.getInputMonitor().mInputFocus;
- InputTarget focusTarget = mService.getInputTargetFromToken(focusToken);
+ // App is unresponsive, but we are actively trying to give focus to a window.
+ // Blame the window if possible since the window may not belong to the app.
+ DisplayContent display = mService.mRoot.getDisplayContent(activity.getDisplayId());
+ IBinder focusToken =
+ display == null ? null : display.getInputMonitor().mInputFocus;
+ InputTarget focusTarget = mService.getInputTargetFromToken(focusToken);
- if (focusTarget != null) {
- // Check if we have a recent focus request, newer than the dispatch timeout, then
- // ignore the focus request.
- WindowState targetWindowState = focusTarget.getWindowState();
- boolean requestIsValid = SystemClock.uptimeMillis()
- - display.getInputMonitor().mInputFocusRequestTimeMillis
- >= getInputDispatchingTimeoutMillisLocked(
- targetWindowState.getActivityRecord());
+ if (focusTarget != null) {
+ // Check if we have a recent focus request, newer than the dispatch timeout,
+ // then ignore the focus request.
+ WindowState targetWindowState = focusTarget.getWindowState();
+ boolean requestIsValid = SystemClock.uptimeMillis()
+ - display.getInputMonitor().mInputFocusRequestTimeMillis
+ >= getInputDispatchingTimeoutMillisLocked(
+ targetWindowState.getActivityRecord());
- if (requestIsValid) {
- if (notifyWindowUnresponsive(focusToken, timeoutRecord)) {
- Slog.i(TAG_WM, "Blamed " + focusTarget.getWindowState().getName()
- + " using pending focus request. Focused activity: "
- + activity.getName());
- return;
+ if (requestIsValid) {
+ if (notifyWindowUnresponsive(focusToken, timeoutRecord)) {
+ Slog.i(TAG_WM, "Blamed " + focusTarget.getWindowState().getName()
+ + " using pending focus request. Focused activity: "
+ + activity.getName());
+ return;
+ }
}
}
- }
-
- Slog.i(TAG_WM, "ANR in " + activity.getName() + ". Reason: " + timeoutRecord.mReason);
- dumpAnrStateLocked(activity, null /* windowState */, timeoutRecord.mReason);
- mUnresponsiveAppByDisplay.put(activity.getDisplayId(), activity);
+ Slog.i(TAG_WM, "ANR in " + activity.getName() + ". Reason: "
+ + timeoutRecord.mReason);
+ dumpAnrStateLocked(activity, null /* windowState */, timeoutRecord.mReason);
+ mUnresponsiveAppByDisplay.put(activity.getDisplayId(), activity);
+ }
+ activity.inputDispatchingTimedOut(timeoutRecord, INVALID_PID);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- activity.inputDispatchingTimedOut(timeoutRecord, INVALID_PID);
}
@@ -117,14 +126,20 @@ class AnrController {
*/
void notifyWindowUnresponsive(@NonNull IBinder token, @NonNull OptionalInt pid,
@NonNull TimeoutRecord timeoutRecord) {
- if (notifyWindowUnresponsive(token, timeoutRecord)) {
- return;
- }
- if (!pid.isPresent()) {
- Slog.w(TAG_WM, "Failed to notify that window token=" + token + " was unresponsive.");
- return;
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "notifyWindowUnresponsive()");
+ if (notifyWindowUnresponsive(token, timeoutRecord)) {
+ return;
+ }
+ if (!pid.isPresent()) {
+ Slog.w(TAG_WM, "Failed to notify that window token=" + token
+ + " was unresponsive.");
+ return;
+ }
+ notifyWindowUnresponsive(pid.getAsInt(), timeoutRecord);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
- notifyWindowUnresponsive(pid.getAsInt(), timeoutRecord);
}
/**
@@ -139,7 +154,9 @@ class AnrController {
final int pid;
final boolean aboveSystem;
final ActivityRecord activity;
+ timeoutRecord.mLatencyTracker.waitingOnGlobalLockStarted();
synchronized (mService.mGlobalLock) {
+ timeoutRecord.mLatencyTracker.waitingOnGlobalLockEnded();
InputTarget target = mService.getInputTargetFromToken(inputToken);
if (target == null) {
return false;
@@ -168,7 +185,9 @@ class AnrController {
private void notifyWindowUnresponsive(int pid, TimeoutRecord timeoutRecord) {
Slog.i(TAG_WM,
"ANR in input window owned by pid=" + pid + ". Reason: " + timeoutRecord.mReason);
+ timeoutRecord.mLatencyTracker.waitingOnGlobalLockStarted();
synchronized (mService.mGlobalLock) {
+ timeoutRecord.mLatencyTracker.waitingOnGlobalLockEnded();
dumpAnrStateLocked(null /* activity */, null /* windowState */, timeoutRecord.mReason);
}
@@ -254,68 +273,83 @@ class AnrController {
if (mLastPreDumpTimeMs > 0 && now - mLastPreDumpTimeMs < PRE_DUMP_MIN_INTERVAL_MS) {
return;
}
-
- final boolean[] shouldDumpSf = { true };
- final ArrayMap<String, Runnable> monitors = new ArrayMap<>(2);
- monitors.put(TAG_WM, mService::monitor);
- monitors.put("ActivityManager", mService.mAmInternal::monitor);
- final CountDownLatch latch = new CountDownLatch(monitors.size());
- // The pre-dump will execute if one of the monitors doesn't complete within the timeout.
- for (int i = 0; i < monitors.size(); i++) {
- final String name = monitors.keyAt(i);
- final Runnable monitor = monitors.valueAt(i);
- // Always create new thread to avoid noise of existing threads. Suppose here won't
- // create too many threads because it means that watchdog will be triggered first.
- new Thread() {
- @Override
- public void run() {
- monitor.run();
- latch.countDown();
- final long elapsed = SystemClock.uptimeMillis() - now;
- if (elapsed > PRE_DUMP_MONITOR_TIMEOUT_MS) {
- Slog.i(TAG_WM, "Pre-dump acquired " + name + " in " + elapsed + "ms");
- } else if (TAG_WM.equals(name)) {
- // Window manager is the main client of SurfaceFlinger. If window manager
- // is responsive, the stack traces of SurfaceFlinger may not be important.
- shouldDumpSf[0] = false;
- }
- };
- }.start();
- }
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "preDumpIfLockTooSlow()");
try {
- if (latch.await(PRE_DUMP_MONITOR_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
- return;
+ final boolean[] shouldDumpSf = { true };
+ final ArrayMap<String, Runnable> monitors = new ArrayMap<>(2);
+ monitors.put(TAG_WM, mService::monitor);
+ monitors.put("ActivityManager", mService.mAmInternal::monitor);
+ final CountDownLatch latch = new CountDownLatch(monitors.size());
+ // The pre-dump will execute if one of the monitors doesn't complete within
+ // the timeout.
+ for (int i = 0; i < monitors.size(); i++) {
+ final String name = monitors.keyAt(i);
+ final Runnable monitor = monitors.valueAt(i);
+ // Always create new thread to avoid noise of existing threads. Suppose here won't
+ // create too many threads because it means that watchdog will be triggered first.
+ new Thread() {
+ @Override
+ public void run() {
+ monitor.run();
+ latch.countDown();
+ final long elapsed = SystemClock.uptimeMillis() - now;
+ if (elapsed > PRE_DUMP_MONITOR_TIMEOUT_MS) {
+ Slog.i(TAG_WM, "Pre-dump acquired " + name + " in " + elapsed + "ms");
+ } else if (TAG_WM.equals(name)) {
+ // Window manager is the main client of SurfaceFlinger.
+ // If window manager is responsive, the stack traces
+ // of SurfaceFlinger may not be important.
+ shouldDumpSf[0] = false;
+ }
+ };
+ }.start();
}
- } catch (InterruptedException ignored) { }
- mLastPreDumpTimeMs = now;
- Slog.i(TAG_WM, "Pre-dump for unresponsive");
+ try {
+ if (latch.await(PRE_DUMP_MONITOR_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+ return;
+ }
+ } catch (InterruptedException ignored) { }
+ mLastPreDumpTimeMs = now;
+ Slog.i(TAG_WM, "Pre-dump for unresponsive");
- final ArrayList<Integer> firstPids = new ArrayList<>(1);
- firstPids.add(WindowManagerService.MY_PID);
- ArrayList<Integer> nativePids = null;
- final int[] pids = shouldDumpSf[0]
- ? Process.getPidsForCommands(new String[] { "/system/bin/surfaceflinger" })
- : null;
- if (pids != null) {
- nativePids = new ArrayList<>(1);
- for (int pid : pids) {
- nativePids.add(pid);
+ final ArrayList<Integer> firstPids = new ArrayList<>(1);
+ firstPids.add(WindowManagerService.MY_PID);
+ ArrayList<Integer> nativePids = null;
+ final int[] pids = shouldDumpSf[0]
+ ? Process.getPidsForCommands(new String[] { "/system/bin/surfaceflinger" })
+ : null;
+ if (pids != null) {
+ nativePids = new ArrayList<>(1);
+ for (int pid : pids) {
+ nativePids.add(pid);
+ }
}
- }
- String criticalEvents = CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
- final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
- null /* processCpuTracker */, null /* lastPids */, nativePids,
- null /* logExceptionCreatingFile */, "Pre-dump", criticalEvents);
- if (tracesFile != null) {
- tracesFile.renameTo(new File(tracesFile.getParent(), tracesFile.getName() + "_pre"));
+ String criticalEvents =
+ CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
+ final File tracesFile = ActivityManagerService.dumpStackTraces(firstPids,
+ null /* processCpuTracker */, null /* lastPids */, nativePids,
+ null /* logExceptionCreatingFile */, "Pre-dump", criticalEvents,
+ null/* AnrLatencyTracker */);
+ if (tracesFile != null) {
+ tracesFile.renameTo(
+ new File(tracesFile.getParent(), tracesFile.getName() + "_pre"));
+ }
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
+
}
private void dumpAnrStateLocked(ActivityRecord activity, WindowState windowState,
String reason) {
- mService.saveANRStateLocked(activity, windowState, reason);
- mService.mAtmService.saveANRState(reason);
+ try {
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "dumpAnrStateLocked()");
+ mService.saveANRStateLocked(activity, windowState, reason);
+ mService.mAtmService.saveANRState(reason);
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+ }
}
private boolean isWindowAboveSystem(@NonNull WindowState windowState) {
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 7e62c61572d6..2ea6a3f14f64 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -467,16 +467,6 @@ public class AppTransitionController {
return TRANSIT_OLD_WALLPAPER_OPEN;
}
- // Some devices don't show a wallpaper. In that case we should still trigger wallpaper
- // transitions when animating to/from the home activity
- if (wallpaperTarget == null) {
- if (topOpeningApp != null && topOpeningApp.isActivityTypeHome()) {
- return TRANSIT_OLD_WALLPAPER_OPEN;
- } else if (topClosingApp != null && topClosingApp.isActivityTypeHome()) {
- return TRANSIT_OLD_WALLPAPER_CLOSE;
- }
- }
-
final ArraySet<WindowContainer> openingWcs = getAnimationTargets(
openingApps, closingApps, true /* visible */);
final ArraySet<WindowContainer> closingWcs = getAnimationTargets(
@@ -488,6 +478,11 @@ public class AppTransitionController {
@TransitContainerType int openingType = getTransitContainerType(openingContainer);
@TransitContainerType int closingType = getTransitContainerType(closingContainer);
if (appTransition.containsTransitRequest(TRANSIT_TO_FRONT) && openingType == TYPE_TASK) {
+ if (topOpeningApp != null && topOpeningApp.isActivityTypeHome()) {
+ // If we are opening the home task, we want to play an animation as if
+ // the task on top is being brought to back.
+ return TRANSIT_OLD_TASK_TO_BACK;
+ }
return TRANSIT_OLD_TASK_TO_FRONT;
}
if (appTransition.containsTransitRequest(TRANSIT_TO_BACK) && closingType == TYPE_TASK) {
diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java
index 219092baface..7d9ae87517b0 100644
--- a/services/core/java/com/android/server/wm/AsyncRotationController.java
+++ b/services/core/java/com/android/server/wm/AsyncRotationController.java
@@ -202,10 +202,15 @@ class AsyncRotationController extends FadeAnimationController implements Consume
// target windows. But the windows still need to use sync transaction to keep the appearance
// in previous rotation, so request a no-op sync to keep the state.
for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) {
+ if (mTargetWindowTokens.valueAt(i).canDrawBeforeStartTransaction()) {
+ // Expect a screenshot layer will cover the non seamless windows.
+ continue;
+ }
final WindowToken token = mTargetWindowTokens.keyAt(i);
for (int j = token.getChildCount() - 1; j >= 0; j--) {
// TODO(b/234585256): The consumer should be handleFinishDrawing().
token.getChildAt(j).applyWithNextDraw(t -> {});
+ if (DEBUG) Slog.d(TAG, "Sync draw for " + token.getChildAt(j));
}
}
mIsSyncDrawRequested = true;
@@ -357,7 +362,12 @@ class AsyncRotationController extends FadeAnimationController implements Consume
* or seamless transformation in a rotated display.
*/
boolean shouldFreezeInsetsPosition(WindowState w) {
- return mTransitionOp != OP_LEGACY && w.mTransitionController.inTransition()
+ if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) {
+ // Expect a screenshot layer has covered the screen, so it is fine to let client side
+ // insets animation runner update the position directly.
+ return false;
+ }
+ return mTransitionOp != OP_LEGACY && !mIsStartTransactionCommitted
&& isTargetToken(w.mToken);
}
@@ -478,7 +488,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume
return false;
}
final Operation op = mTargetWindowTokens.get(w.mToken);
- if (op == null) return false;
+ if (op == null || op.canDrawBeforeStartTransaction()) return false;
if (DEBUG) Slog.d(TAG, "handleFinishDrawing " + w);
if (op.mDrawTransaction == null) {
if (w.isClientLocal()) {
@@ -543,5 +553,14 @@ class AsyncRotationController extends FadeAnimationController implements Consume
Operation(@Action int action) {
mAction = action;
}
+
+ /**
+ * Returns {@code true} if the corresponding window can draw its latest content before the
+ * start transaction of rotation transition is applied.
+ */
+ boolean canDrawBeforeStartTransaction() {
+ return TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST
+ && mAction != ACTION_SEAMLESS;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index d9ab971c9a78..a3f5401fd375 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -36,6 +36,7 @@ import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.BackNavigationInfo;
import android.window.OnBackInvokedCallbackInfo;
+import android.window.ScreenCapture;
import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
@@ -422,7 +423,7 @@ class BackNavigationController {
ComponentName activityComponent) {
// Check if we have a screenshot of the previous activity, indexed by its
// component name.
- SurfaceControl.ScreenshotHardwareBuffer backBuffer = task.mBackScreenshots
+ ScreenCapture.ScreenshotHardwareBuffer backBuffer = task.mBackScreenshots
.get(activityComponent.flattenToString());
return backBuffer != null ? backBuffer.getHardwareBuffer() : null;
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index acbf1a4cd8f1..6e23ed966ddc 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -391,8 +391,7 @@ final class ContentRecorder implements WindowContainerListener {
* </p>
*/
private void handleStartRecordingFailed() {
- final boolean shouldExitTaskRecording = mContentRecordingSession != null
- && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK;
+ final boolean shouldExitTaskRecording = isRecordingContentTask();
clearContentRecordingSession();
if (shouldExitTaskRecording) {
// Clean up the cached session first to ensure recording doesn't re-start, since
@@ -478,9 +477,10 @@ final class ContentRecorder implements WindowContainerListener {
ProtoLog.v(WM_DEBUG_CONTENT_RECORDING,
"Recorded task is removed, so stop recording on display %d",
mDisplayContent.getDisplayId());
- Task recordedTask = mRecordedWindowContainer.asTask();
- if (recordedTask == null
- || mContentRecordingSession.getContentToRecord() != RECORD_CONTENT_TASK) {
+
+ Task recordedTask = mRecordedWindowContainer != null
+ ? mRecordedWindowContainer.asTask() : null;
+ if (recordedTask == null || !isRecordingContentTask()) {
return;
}
recordedTask.unregisterWindowContainerListener(this);
@@ -504,4 +504,9 @@ final class ContentRecorder implements WindowContainerListener {
@VisibleForTesting interface MediaProjectionManagerWrapper {
void stopActiveProjection();
}
+
+ private boolean isRecordingContentTask() {
+ return mContentRecordingSession != null
+ && mContentRecordingSession.getContentToRecord() == RECORD_CONTENT_TASK;
+ }
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 690c94afd04f..669cd5d8f812 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -231,6 +231,7 @@ import android.view.WindowManager.DisplayImePolicy;
import android.view.WindowManagerPolicyConstants.PointerEventListener;
import android.window.DisplayWindowPolicyController;
import android.window.IDisplayAreaOrganizer;
+import android.window.ScreenCapture;
import android.window.TransitionRequestInfo;
import com.android.internal.R;
@@ -240,7 +241,6 @@ import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ToBooleanFunction;
import com.android.internal.util.function.TriConsumer;
-import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.inputmethod.InputMethodManagerInternal;
@@ -3074,18 +3074,13 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
*/
boolean pointWithinAppWindow(int x, int y) {
final int[] targetWindowType = {-1};
- final PooledConsumer fn = PooledLambda.obtainConsumer((w, nonArg) -> {
- if (targetWindowType[0] != -1) {
- return;
- }
-
+ forAllWindows(w -> {
if (w.isOnScreen() && w.isVisible() && w.getFrame().contains(x, y)) {
targetWindowType[0] = w.mAttrs.type;
- return;
+ return true;
}
- }, PooledLambda.__(WindowState.class), mTmpRect);
- forAllWindows(fn, true /* traverseTopToBottom */);
- fn.recycle();
+ return false;
+ }, true /* traverseTopToBottom */);
return FIRST_APPLICATION_WINDOW <= targetWindowType[0]
&& targetWindowType[0] <= LAST_APPLICATION_WINDOW;
}
@@ -3111,11 +3106,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
mTmpRect.setEmpty();
mTmpRect2.setEmpty();
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayContent::processTaskForTouchExcludeRegion, this,
- PooledLambda.__(Task.class), focusedTask, delta);
- forAllTasks(c);
- c.recycle();
+ forAllTasks(t -> { processTaskForTouchExcludeRegion(t, focusedTask, delta); });
// If we removed the focused task above, add it back and only leave its
// outside touch area in the exclusion. TapDetector is not interested in
@@ -4163,7 +4154,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
}
ProtoLog.i(WM_DEBUG_IME, "setInputMethodTarget %s", target);
- final boolean layeringTargetChanged = target != mImeLayeringTarget;
+ boolean shouldUpdateImeParent = target != mImeLayeringTarget;
mImeLayeringTarget = target;
// 1. Reparent the IME container window to the target root DA to get the correct bounds and
@@ -4171,10 +4162,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// is not organized (see FEATURE_IME and updateImeParent).
if (target != null && !mImeWindowsContainer.isOrganized()) {
RootDisplayArea targetRoot = target.getRootDisplayArea();
- if (targetRoot != null && targetRoot != mImeWindowsContainer.getRootDisplayArea()) {
- // Reposition the IME container to the target root to get the correct bounds and
- // config.
- targetRoot.placeImeContainer(mImeWindowsContainer);
+ if (targetRoot != null && targetRoot != mImeWindowsContainer.getRootDisplayArea()
+ // Try reparent the IME container to the target root to get the bounds and
+ // config that match the target window.
+ && targetRoot.placeImeContainer(mImeWindowsContainer)) {
+ // Update the IME surface parent since the IME container window has been reparented.
+ shouldUpdateImeParent = true;
// Directly hide the IME window so it doesn't flash immediately after reparenting.
// InsetsController will make IME visible again before animating it.
if (mInputMethodWindow != null) {
@@ -4191,7 +4184,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// 4. Update the IME control target to apply any inset change and animation.
// 5. Reparent the IME container surface to either the input target app, or the IME window
// parent.
- updateImeControlTarget(layeringTargetChanged);
+ updateImeControlTarget(shouldUpdateImeParent);
}
@VisibleForTesting
@@ -4234,7 +4227,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
return mImeTarget;
}
- private SurfaceControl createImeSurface(SurfaceControl.ScreenshotHardwareBuffer b,
+ private SurfaceControl createImeSurface(ScreenCapture.ScreenshotHardwareBuffer b,
Transaction t) {
final HardwareBuffer buffer = b.getHardwareBuffer();
ProtoLog.i(WM_DEBUG_IME, "create IME snapshot for %s, buff width=%s, height=%s",
@@ -4296,7 +4289,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|| mImeSurface.getWidth() != dc.mInputMethodWindow.getFrame().width()
|| mImeSurface.getHeight() != dc.mInputMethodWindow.getFrame().height();
if (task != null && !task.isActivityTypeHomeOrRecents()) {
- SurfaceControl.ScreenshotHardwareBuffer imeBuffer = renewImeSurface
+ ScreenCapture.ScreenshotHardwareBuffer imeBuffer = renewImeSurface
? dc.mWmService.mTaskSnapshotController.snapshotImeFromAttachedTask(task)
: null;
if (imeBuffer != null) {
@@ -4905,12 +4898,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
// Send invalid rect and no width and height since it will screenshot the entire display.
final IBinder displayToken = SurfaceControl.getInternalDisplayToken();
- final SurfaceControl.DisplayCaptureArgs captureArgs =
- new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+ final ScreenCapture.DisplayCaptureArgs captureArgs =
+ new ScreenCapture.DisplayCaptureArgs.Builder(displayToken)
.setUseIdentityTransform(inRotation)
.build();
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureDisplay(captureArgs);
+ final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+ ScreenCapture.captureDisplay(captureArgs);
final Bitmap bitmap = screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
if (bitmap == null) {
Slog.w(TAG_WM, "Failed to take screenshot");
@@ -6184,17 +6177,10 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
/** Update and get all UIDs that are present on the display and have access to it. */
IntArray getPresentUIDs() {
mDisplayAccessUIDs.clear();
- final PooledConsumer c = PooledLambda.obtainConsumer(DisplayContent::addActivityUid,
- PooledLambda.__(ActivityRecord.class), mDisplayAccessUIDs);
- mDisplayContent.forAllActivities(c);
- c.recycle();
+ mDisplayContent.forAllActivities(r -> { mDisplayAccessUIDs.add(r.getUid()); });
return mDisplayAccessUIDs;
}
- private static void addActivityUid(ActivityRecord r, IntArray uids) {
- uids.add(r.getUid());
- }
-
@VisibleForTesting
boolean shouldDestroyContentOnRemove() {
return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
diff --git a/services/core/java/com/android/server/wm/DisplayHashController.java b/services/core/java/com/android/server/wm/DisplayHashController.java
index 543d4ad6b507..1a8b43403b25 100644
--- a/services/core/java/com/android/server/wm/DisplayHashController.java
+++ b/services/core/java/com/android/server/wm/DisplayHashController.java
@@ -53,9 +53,9 @@ import android.service.displayhash.IDisplayHashingService;
import android.util.Size;
import android.util.Slog;
import android.view.MagnificationSpec;
-import android.view.SurfaceControl;
import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
+import android.window.ScreenCapture;
import com.android.internal.annotations.GuardedBy;
@@ -194,7 +194,7 @@ public class DisplayHashController {
return true;
}
- void generateDisplayHash(SurfaceControl.LayerCaptureArgs.Builder args,
+ void generateDisplayHash(ScreenCapture.LayerCaptureArgs.Builder args,
Rect boundsInWindow, String hashAlgorithm, int uid, RemoteCallback callback) {
if (!allowedToGenerateHash(uid)) {
sendDisplayHashError(callback, DISPLAY_HASH_ERROR_TOO_MANY_REQUESTS);
@@ -217,8 +217,8 @@ public class DisplayHashController {
args.setGrayscale(displayHashParams.isGrayscaleBuffer());
- SurfaceControl.ScreenshotHardwareBuffer screenshotHardwareBuffer =
- SurfaceControl.captureLayers(args.build());
+ ScreenCapture.ScreenshotHardwareBuffer screenshotHardwareBuffer =
+ ScreenCapture.captureLayers(args.build());
if (screenshotHardwareBuffer == null
|| screenshotHardwareBuffer.getHardwareBuffer() == null) {
Slog.w(TAG, "Failed to generate DisplayHash. Couldn't capture content");
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index e7806060c14f..ac61cb940e49 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2167,10 +2167,7 @@ public class DisplayPolicy {
* If the decor insets changes, the display configuration may be affected. The caller should
* call {@link DisplayContent#sendNewConfiguration()} if this method returns {@code true}.
*/
- boolean updateDecorInsetsInfoIfNeeded(WindowState win) {
- if (!win.providesNonDecorInsets()) {
- return false;
- }
+ boolean updateDecorInsetsInfo() {
final DisplayFrames displayFrames = mDisplayContent.mDisplayFrames;
final int rotation = displayFrames.mRotation;
final int dw = displayFrames.mWidth;
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index 25ff023a664b..0b28ba2bb146 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -228,8 +228,8 @@ class DragState {
SurfaceControl dragSurface = null;
if (!mDragResult && (ws.mSession.mPid == mPid)) {
// Report unconsumed drop location back to the app that started the drag.
- x = mCurrentX;
- y = mCurrentY;
+ x = ws.translateToWindowX(mCurrentX);
+ y = ws.translateToWindowY(mCurrentY);
if (relinquishDragSurfaceToDragSource()) {
// If requested (and allowed), report the drag surface back to the app
// starting the drag to handle the return animation
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index dcc16ebd88f5..d54f77a73661 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -19,17 +19,16 @@ package com.android.server.wm;
import static com.android.server.wm.IdentifierProto.HASH_CODE;
import static com.android.server.wm.IdentifierProto.TITLE;
-import static com.android.server.wm.WindowContainerProto.IDENTIFIER;
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.WindowStateProto.IDENTIFIER;
import android.annotation.Nullable;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.util.ArrayMap;
-import android.util.proto.ProtoOutputStream;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import android.view.IWindow;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index f3bd1a15cc94..2de8faf8b086 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -64,6 +64,7 @@ import android.view.InsetsState.InternalInsetsType;
import android.view.InternalInsetsAnimationController;
import android.view.SurfaceControl;
import android.view.SyncRtSurfaceTransactionApplier;
+import android.view.WindowInsets;
import android.view.WindowInsets.Type;
import android.view.WindowInsetsAnimation;
import android.view.WindowInsetsAnimation.Bounds;
@@ -398,16 +399,13 @@ class InsetsPolicy {
if (WindowConfiguration.isFloating(windowingMode)
|| (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
- InsetsState newState = new InsetsState();
-
- // Only caption and IME are needed.
- if (state.peekSource(ITYPE_CAPTION_BAR) != null) {
- newState.addSource(state.peekSource(ITYPE_CAPTION_BAR));
- }
- if (windowingMode != WINDOWING_MODE_PINNED && state.peekSource(ITYPE_IME) != null) {
- newState.addSource(state.peekSource(ITYPE_IME));
+ // Keep frames, caption, and IME.
+ int types = WindowInsets.Type.captionBar();
+ if (windowingMode != WINDOWING_MODE_PINNED) {
+ types |= WindowInsets.Type.ime();
}
-
+ InsetsState newState = new InsetsState();
+ newState.set(state, types);
state = newState;
stateCopied = true;
}
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 27550d9e8186..3d006868c79c 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -273,8 +273,8 @@ public class Letterbox {
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
if (e.getAction() == MotionEvent.ACTION_UP) {
- mDoubleTapCallbackX.accept((int) e.getX());
- mDoubleTapCallbackY.accept((int) e.getY());
+ mDoubleTapCallbackX.accept((int) e.getRawX());
+ mDoubleTapCallbackY.accept((int) e.getRawY());
return true;
}
return false;
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index 317c93e63459..ea82417a2389 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -481,7 +481,7 @@ final class LetterboxUiController {
}
private void updateRoundedCorners(WindowState mainWindow) {
- final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
+ final SurfaceControl windowSurface = mainWindow.getSurfaceControl();
if (windowSurface != null && windowSurface.isValid()) {
final Transaction transaction = mActivityRecord.getSyncTransaction();
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 7bb57d827a43..bffab7a4199d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -62,8 +62,6 @@ import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.statusbar.StatusBarManagerInternal;
@@ -116,7 +114,7 @@ public class RecentsAnimationController implements DeathRecipient {
private boolean mWillFinishToHome = false;
private final Runnable mFailsafeRunnable = this::onFailsafe;
- // The recents component app token that is shown behind the visibile tasks
+ // The recents component app token that is shown behind the visible tasks
private ActivityRecord mTargetActivityRecord;
private DisplayContent mDisplayContent;
private int mTargetActivityType;
@@ -403,11 +401,11 @@ public class RecentsAnimationController implements DeathRecipient {
final Task targetRootTask = mDisplayContent.getDefaultTaskDisplayArea()
.getRootTask(WINDOWING_MODE_UNDEFINED, targetActivityType);
if (targetRootTask != null) {
- final PooledConsumer c = PooledLambda.obtainConsumer((t, outList) ->
- { if (!outList.contains(t)) outList.add(t); }, PooledLambda.__(Task.class),
- visibleTasks);
- targetRootTask.forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
+ targetRootTask.forAllLeafTasks(t -> {
+ if (!visibleTasks.contains(t)) {
+ visibleTasks.add(t);
+ }
+ }, true /* traverseTopToBottom */);
}
final int taskCount = visibleTasks.size();
@@ -456,6 +454,22 @@ public class RecentsAnimationController implements DeathRecipient {
}
}
+ /**
+ * Return whether the given window should still be considered interesting for the all-drawn
+ * state. This is only interesting for the target app, which may have child windows that are
+ * not actually visible and should not be considered interesting and waited upon.
+ */
+ protected boolean isInterestingForAllDrawn(WindowState window) {
+ if (isTargetApp(window.getActivityRecord())) {
+ if (window.getWindowType() != TYPE_BASE_APPLICATION
+ && window.getAttrs().alpha == 0f) {
+ // If there is a cihld window that is alpha 0, then ignore that window
+ return false;
+ }
+ }
+ // By default all windows are still interesting for all drawn purposes
+ return true;
+ }
/**
* Whether a task should be filtered from the recents animation. This can be true for tasks
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 1fd66fc2c2bc..d497d8cbf9cd 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -141,15 +141,16 @@ class ResetTargetTaskHelper implements Consumer<Task>, Predicate<ActivityRecord>
return false;
} else {
- mResultActivities.add(r);
if (r.resultTo != null) {
// If this activity is sending a reply to a previous activity, we can't do
// anything with it now until we reach the start of the reply chain.
// NOTE: that we are assuming the result is always to the previous activity,
// which is almost always the case but we really shouldn't count on.
+ mResultActivities.add(r);
return false;
} else if (mTargetTaskFound && allowTaskReparenting && mTargetTask.affinity != null
&& mTargetTask.affinity.equals(r.taskAffinity)) {
+ mResultActivities.add(r);
// This activity has an affinity for our task. Either remove it if we are
// clearing or move it over to our task. Note that we currently punt on the case
// where we are resetting a task that is not at the top but who has activities
diff --git a/services/core/java/com/android/server/wm/RootDisplayArea.java b/services/core/java/com/android/server/wm/RootDisplayArea.java
index d94bb9e89252..092adc37d497 100644
--- a/services/core/java/com/android/server/wm/RootDisplayArea.java
+++ b/services/core/java/com/android/server/wm/RootDisplayArea.java
@@ -22,8 +22,10 @@ import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER;
import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import android.annotation.Nullable;
+import android.util.Slog;
import com.android.server.policy.WindowManagerPolicy;
@@ -76,8 +78,10 @@ class RootDisplayArea extends DisplayArea.Dimmable {
/**
* Places the IME container below this root, so that it's bounds and config will be updated to
* match the root.
+ *
+ * @return {@code true} if the IME container is reparented to this root.
*/
- void placeImeContainer(DisplayArea.Tokens imeContainer) {
+ boolean placeImeContainer(DisplayArea.Tokens imeContainer) {
final RootDisplayArea previousRoot = imeContainer.getRootDisplayArea();
List<Feature> features = mFeatures;
@@ -94,11 +98,23 @@ class RootDisplayArea extends DisplayArea.Dimmable {
previousRoot.updateImeContainerForLayers(null /* imeContainer */);
imeContainer.reparent(imeDisplayAreas.get(0), POSITION_TOP);
updateImeContainerForLayers(imeContainer);
- return;
+ return true;
}
}
- throw new IllegalStateException(
- "There is no FEATURE_IME_PLACEHOLDER in this root to place the IME container");
+
+ // Some device UX may not have the need to update the IME bounds and position for IME target
+ // in a child DisplayArea, so instead of throwing exception, we just allow the IME container
+ // to remain in its previous root.
+ if (!isDescendantOf(previousRoot)) {
+ // When this RootDisplayArea is a descendant of the current RootDisplayArea, it will be
+ // at the APPLICATION_LAYER, and the IME container will always be on top and have bounds
+ // equal or larger than the input target.
+ // If it is not a descendant, the DisplayAreaPolicy owner should make sure the IME is
+ // working correctly. Print a warning in case they are not.
+ Slog.w(TAG_WM, "The IME target is not in the same root as the IME container, but there "
+ + "is no DisplayArea of FEATURE_IME_PLACEHOLDER in the target RootDisplayArea");
+ }
+ return false;
}
/**
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index b3f35487f3a5..b2ec3f369e9e 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1487,8 +1487,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
} else {
final String resolvedType =
homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
- final ResolveInfo info = AppGlobals.getPackageManager()
- .resolveIntent(homeIntent, resolvedType, flags, userId);
+ final ResolveInfo info = mTaskSupervisor.resolveIntent(homeIntent, resolvedType,
+ userId, flags, Binder.getCallingUid());
if (info != null) {
aInfo = info.activityInfo;
}
@@ -1799,10 +1799,6 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedActivity);
}
- boolean hasResumedActivity(int uid) {
- return forAllActivities(ar -> ar.isState(RESUMED) && ar.getUid() == uid);
- }
-
boolean isTopDisplayFocusedRootTask(Task task) {
return task != null && task == getTopDisplayFocusedRootTask();
}
@@ -3106,9 +3102,8 @@ class RootWindowContainer extends WindowContainer<DisplayContent>
}
void finishVoiceTask(IVoiceInteractionSession session) {
- forAllRootTasks(rootTask -> {
- rootTask.finishVoiceTask(session);
- });
+ final IBinder binder = session.asBinder();
+ forAllLeafTasks(t -> t.finishIfVoiceTask(binder), true /* traverseTopToBottom */);
}
/**
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 120fec0fe0e6..a753b5559e07 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -23,18 +23,16 @@ import android.app.ActivityManager.RunningTaskInfo;
import android.os.UserHandle;
import android.util.ArraySet;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
-
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
+import java.util.function.Consumer;
/**
* Class for resolving the set of running tasks in the system.
*/
-class RunningTasks {
+class RunningTasks implements Consumer<Task> {
static final int FLAG_FILTER_ONLY_VISIBLE_RECENTS = 1;
static final int FLAG_ALLOWED = 1 << 1;
@@ -61,7 +59,7 @@ class RunningTasks {
private boolean mKeepIntentExtra;
void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
- WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ WindowContainer<?> root, int callingUid, ArraySet<Integer> profileIds) {
// Return early if there are no tasks to fetch
if (maxNum <= 0) {
return;
@@ -79,10 +77,7 @@ class RunningTasks {
mRecentTasks = recentTasks;
mKeepIntentExtra = (flags & FLAG_KEEP_INTENT_EXTRA) == FLAG_KEEP_INTENT_EXTRA;
- final PooledConsumer c = PooledLambda.obtainConsumer(RunningTasks::processTask, this,
- PooledLambda.__(Task.class));
- root.forAllLeafTasks(c, false);
- c.recycle();
+ root.forAllLeafTasks(this, false /* traverseTopToBottom */);
// Take the first {@param maxNum} tasks and create running task infos for them
final Iterator<Task> iter = mTmpSortedSet.iterator();
@@ -97,7 +92,8 @@ class RunningTasks {
}
}
- private void processTask(Task task) {
+ @Override
+ public void accept(Task task) {
if (task.getTopNonFinishingActivity() == null) {
// Skip if there are no activities in the task
return;
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index fd8b614de9b7..24d4e981e9ce 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -52,6 +52,7 @@ import android.view.SurfaceControl;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Transformation;
+import android.window.ScreenCapture;
import com.android.internal.R;
import com.android.internal.protolog.common.ProtoLog;
@@ -167,7 +168,7 @@ class ScreenRotationAnimation {
final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
try {
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer;
+ final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer;
if (isSizeChanged) {
final DisplayAddress address = displayInfo.address;
if (!(address instanceof DisplayAddress.Physical)) {
@@ -186,22 +187,22 @@ class ScreenRotationAnimation {
// the whole display to include the rounded corner overlays.
setSkipScreenshotForRoundedCornerOverlays(false, t);
mRoundedCornerOverlay = displayContent.findRoundedCornerOverlays();
- final SurfaceControl.DisplayCaptureArgs captureArgs =
- new SurfaceControl.DisplayCaptureArgs.Builder(displayToken)
+ final ScreenCapture.DisplayCaptureArgs captureArgs =
+ new ScreenCapture.DisplayCaptureArgs.Builder(displayToken)
.setSourceCrop(new Rect(0, 0, width, height))
.setAllowProtected(true)
.setCaptureSecureLayers(true)
.build();
- screenshotBuffer = SurfaceControl.captureDisplay(captureArgs);
+ screenshotBuffer = ScreenCapture.captureDisplay(captureArgs);
} else {
- SurfaceControl.LayerCaptureArgs captureArgs =
- new SurfaceControl.LayerCaptureArgs.Builder(
+ ScreenCapture.LayerCaptureArgs captureArgs =
+ new ScreenCapture.LayerCaptureArgs.Builder(
displayContent.getSurfaceControl())
.setCaptureSecureLayers(true)
.setAllowProtected(true)
.setSourceCrop(new Rect(0, 0, width, height))
.build();
- screenshotBuffer = SurfaceControl.captureLayers(captureArgs);
+ screenshotBuffer = ScreenCapture.captureLayers(captureArgs);
}
if (screenshotBuffer == null) {
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index b9739f03bec5..e1a1f5737170 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -291,7 +291,11 @@ class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
public void finishDrawing(IWindow window,
@Nullable SurfaceControl.Transaction postDrawTransaction, int seqId) {
if (DEBUG) Slog.v(TAG_WM, "IWindow finishDrawing called for " + window);
+ if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishDrawing: " + mPackageName);
+ }
mService.finishDrawingWindow(this, window, postDrawTransaction, seqId);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@Override
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index f3670e49f01e..94d4ddeb465c 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -45,6 +45,7 @@ import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
import android.view.animation.Animation;
import android.view.animation.Transformation;
+import android.window.ScreenCapture;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -433,16 +434,16 @@ class SurfaceAnimationRunner {
private void doCreateExtensionSurface(SurfaceControl leash, Rect edgeBounds,
Rect extensionRect, int xPos, int yPos, String layerName,
Transaction startTransaction) {
- SurfaceControl.LayerCaptureArgs captureArgs =
- new SurfaceControl.LayerCaptureArgs.Builder(leash /* surfaceToExtend */)
+ ScreenCapture.LayerCaptureArgs captureArgs =
+ new ScreenCapture.LayerCaptureArgs.Builder(leash /* surfaceToExtend */)
.setSourceCrop(edgeBounds)
.setFrameScale(1)
.setPixelFormat(PixelFormat.RGBA_8888)
.setChildrenOnly(true)
.setAllowProtected(true)
.build();
- final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
- SurfaceControl.captureLayers(captureArgs);
+ final ScreenCapture.ScreenshotHardwareBuffer edgeBuffer =
+ ScreenCapture.captureLayers(captureArgs);
if (edgeBuffer == null) {
// The leash we are trying to screenshot may have been removed by this point, which is
diff --git a/services/core/java/com/android/server/wm/SurfaceFreezer.java b/services/core/java/com/android/server/wm/SurfaceFreezer.java
index a7ef36b01d91..0c36d27603c8 100644
--- a/services/core/java/com/android/server/wm/SurfaceFreezer.java
+++ b/services/core/java/com/android/server/wm/SurfaceFreezer.java
@@ -28,6 +28,7 @@ import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.util.Slog;
import android.view.SurfaceControl;
+import android.window.ScreenCapture;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -89,7 +90,7 @@ class SurfaceFreezer {
freezeTarget = freezeTarget != null ? freezeTarget : mAnimatable.getFreezeSnapshotTarget();
if (freezeTarget != null) {
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer = createSnapshotBufferInner(
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer = createSnapshotBufferInner(
freezeTarget, startBounds);
final HardwareBuffer buffer = screenshotBuffer == null ? null
: screenshotBuffer.getHardwareBuffer();
@@ -183,31 +184,31 @@ class SurfaceFreezer {
return mLeash != null;
}
- private static SurfaceControl.ScreenshotHardwareBuffer createSnapshotBuffer(
+ private static ScreenCapture.ScreenshotHardwareBuffer createSnapshotBuffer(
@NonNull SurfaceControl target, @Nullable Rect bounds) {
Rect cropBounds = null;
if (bounds != null) {
cropBounds = new Rect(bounds);
cropBounds.offsetTo(0, 0);
}
- SurfaceControl.LayerCaptureArgs captureArgs =
- new SurfaceControl.LayerCaptureArgs.Builder(target)
+ ScreenCapture.LayerCaptureArgs captureArgs =
+ new ScreenCapture.LayerCaptureArgs.Builder(target)
.setSourceCrop(cropBounds)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
.build();
- return SurfaceControl.captureLayers(captureArgs);
+ return ScreenCapture.captureLayers(captureArgs);
}
@VisibleForTesting
- SurfaceControl.ScreenshotHardwareBuffer createSnapshotBufferInner(
+ ScreenCapture.ScreenshotHardwareBuffer createSnapshotBufferInner(
SurfaceControl target, Rect bounds) {
return createSnapshotBuffer(target, bounds);
}
@VisibleForTesting
GraphicBuffer createFromHardwareBufferInner(
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer) {
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer) {
return GraphicBuffer.createFromHardwareBuffer(screenshotBuffer.getHardwareBuffer());
}
@@ -220,7 +221,7 @@ class SurfaceFreezer {
* @param screenshotBuffer A thumbnail or placeholder for thumbnail to initialize with.
*/
Snapshot(SurfaceControl.Transaction t,
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) {
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer, SurfaceControl parent) {
GraphicBuffer graphicBuffer = createFromHardwareBufferInner(screenshotBuffer);
mSurfaceControl = mAnimatable.makeAnimationLeash()
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 728102ecc0d6..63c6c35a8e1e 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.app.ActivityManager.isStartResultSuccessful;
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_FORCED;
import static android.app.ActivityTaskManager.RESIZE_MODE_SYSTEM_SCREEN_ROTATION;
@@ -185,6 +186,7 @@ import android.view.WindowManager.TransitionOldType;
import android.window.ITaskOrganizer;
import android.window.PictureInPictureSurfaceTransaction;
import android.window.StartingWindowInfo;
+import android.window.TaskFragmentParentInfo;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -194,7 +196,6 @@ import com.android.internal.app.IVoiceInteractor;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.XmlUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.util.function.pooled.PooledPredicate;
import com.android.server.Watchdog;
@@ -1186,11 +1187,7 @@ class Task extends TaskFragment {
if (oldParent != null) {
final Task oldParentTask = oldParent.asTask();
if (oldParentTask != null) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- Task::cleanUpActivityReferences, oldParentTask,
- PooledLambda.__(ActivityRecord.class));
- forAllActivities(c);
- c.recycle();
+ forAllActivities(oldParentTask::cleanUpActivityReferences);
}
if (oldParent.inPinnedWindowingMode()
@@ -2369,10 +2366,7 @@ class Task extends TaskFragment {
int getDescendantTaskCount() {
final int[] currentCount = {0};
- final PooledConsumer c = PooledLambda.obtainConsumer((t, count) -> { count[0]++; },
- PooledLambda.__(Task.class), currentCount);
- forAllLeafTasks(c, false /* traverseTopToBottom */);
- c.recycle();
+ forAllLeafTasks(t -> currentCount[0]++, false /* traverseTopToBottom */);
return currentCount[0];
}
@@ -2678,6 +2672,7 @@ class Task extends TaskFragment {
if (isRootTask()) {
updateSurfaceBounds();
}
+ sendTaskFragmentParentInfoChangedIfNeeded();
}
boolean isResizeable() {
@@ -2780,10 +2775,7 @@ class Task extends TaskFragment {
&& displayContent.mDividerControllerLocked.isResizing();
if (inFreeformWindowingMode()) {
boolean[] foundTop = { false };
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::getMaxVisibleBounds,
- PooledLambda.__(ActivityRecord.class), out, foundTop);
- forAllActivities(c);
- c.recycle();
+ forAllActivities(a -> { getMaxVisibleBounds(a, out, foundTop); });
if (foundTop[0]) {
return;
}
@@ -2913,7 +2905,7 @@ class Task extends TaskFragment {
@Override
boolean showToCurrentUser() {
return mForceShowForAllUsers || showForAllUsers()
- || mWmService.isCurrentProfile(getTopMostTask().mUserId);
+ || mWmService.isUserVisible(getTopMostTask().mUserId);
}
void setForceShowForAllUsers(boolean forceShowForAllUsers) {
@@ -3319,8 +3311,9 @@ class Task extends TaskFragment {
ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
"applyAnimationUnchecked, control: %s, task: %s, transit: %s",
control, asTask(), AppTransition.appTransitionOldToString(transit));
+ final int size = sources != null ? sources.size() : 0;
control.addTaskToTargets(this, (type, anim) -> {
- for (int i = 0; i < sources.size(); ++i) {
+ for (int i = 0; i < size; ++i) {
sources.get(i).onAnimationFinished(type, anim);
}
});
@@ -3512,6 +3505,33 @@ class Task extends TaskFragment {
return info;
}
+ /**
+ * Returns the {@link TaskFragmentParentInfo} which will send to the client
+ * {@link android.window.TaskFragmentOrganizer}
+ */
+ TaskFragmentParentInfo getTaskFragmentParentInfo() {
+ return new TaskFragmentParentInfo(getConfiguration(), getDisplayId(), isVisibleRequested());
+ }
+
+ @Override
+ void onActivityVisibleRequestedChanged() {
+ if (mVisibleRequested != isVisibleRequested()) {
+ sendTaskFragmentParentInfoChangedIfNeeded();
+ }
+ }
+
+ void sendTaskFragmentParentInfoChangedIfNeeded() {
+ if (!isLeafTask()) {
+ // Only send parent info changed event for leaf task.
+ return;
+ }
+ final TaskFragment childOrganizedTf =
+ getTaskFragment(TaskFragment::isOrganizedTaskFragment);
+ if (childOrganizedTf != null) {
+ childOrganizedTf.sendTaskFragmentParentInfoChanged();
+ }
+ }
+
boolean isTaskId(int taskId) {
return mTaskId == taskId;
}
@@ -4318,14 +4338,6 @@ class Task extends TaskFragment {
}
}
-
- void setActivityWindowingMode(int windowingMode) {
- PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setWindowingMode,
- PooledLambda.__(ActivityRecord.class), windowingMode);
- forAllActivities(c);
- c.recycle();
- }
-
/**
* Sets/unsets the forced-hidden state flag for this task depending on {@param set}.
* @return Whether the force hidden state changed
@@ -5178,26 +5190,19 @@ class Task extends TaskFragment {
return finishedTask;
}
- void finishVoiceTask(IVoiceInteractionSession session) {
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::finishIfVoiceTask,
- PooledLambda.__(Task.class), session.asBinder());
- forAllLeafTasks(c, true /* traverseTopToBottom */);
- c.recycle();
- }
-
- private static void finishIfVoiceTask(Task tr, IBinder binder) {
- if (tr.voiceSession != null && tr.voiceSession.asBinder() == binder) {
- tr.forAllActivities((r) -> {
+ void finishIfVoiceTask(IBinder binder) {
+ if (voiceSession != null && voiceSession.asBinder() == binder) {
+ forAllActivities((r) -> {
if (r.finishing) return;
r.finishIfPossible("finish-voice", false /* oomAdj */);
- tr.mAtmService.updateOomAdj();
+ mAtmService.updateOomAdj();
});
} else {
// Check if any of the activities are using voice
final PooledPredicate f = PooledLambda.obtainPredicate(
Task::finishIfVoiceActivity, PooledLambda.__(ActivityRecord.class),
binder);
- tr.forAllActivities(f);
+ forAllActivities(f);
f.recycle();
}
}
@@ -5343,54 +5348,32 @@ class Task extends TaskFragment {
if (parent != null && foundParentInTask) {
final int callingUid = srec.info.applicationInfo.uid;
- final int parentLaunchMode = parent.info.launchMode;
- final int destIntentFlags = destIntent.getFlags();
- if (parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE ||
- parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TASK ||
- parentLaunchMode == ActivityInfo.LAUNCH_SINGLE_TOP ||
- (destIntentFlags & Intent.FLAG_ACTIVITY_CLEAR_TOP) != 0) {
- boolean abort;
- try {
- abort = !mTaskSupervisor.checkStartAnyActivityPermission(destIntent,
- parent.info, null /* resultWho */, -1 /* requestCode */, srec.getPid(),
- callingUid, srec.info.packageName, null /* callingFeatureId */,
- false /* ignoreTargetSecurity */, false /* launchingInTask */, srec.app,
- null /* resultRecord */, null /* resultRootTask */);
- } catch (SecurityException e) {
- abort = true;
- }
- if (abort) {
- android.util.EventLog.writeEvent(0x534e4554, "238605611", callingUid, "");
- foundParentInTask = false;
- } else {
- parent.deliverNewIntentLocked(callingUid, destIntent, destGrants,
- srec.packageName);
- }
- } else {
- try {
- ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
- destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS,
- srec.mUserId);
- // TODO(b/64750076): Check if calling pid should really be -1.
- final int res = mAtmService.getActivityStartController()
- .obtainStarter(destIntent, "navigateUpTo")
- .setCaller(srec.app.getThread())
- .setActivityInfo(aInfo)
- .setResultTo(parent.token)
- .setCallingPid(-1)
- .setCallingUid(callingUid)
- .setCallingPackage(srec.packageName)
- .setCallingFeatureId(parent.launchedFromFeatureId)
- .setRealCallingPid(-1)
- .setRealCallingUid(callingUid)
- .setComponentSpecified(true)
- .execute();
- foundParentInTask = res == ActivityManager.START_SUCCESS;
- } catch (RemoteException e) {
- foundParentInTask = false;
+ try {
+ ActivityInfo aInfo = AppGlobals.getPackageManager().getActivityInfo(
+ destIntent.getComponent(), ActivityManagerService.STOCK_PM_FLAGS,
+ srec.mUserId);
+ // TODO(b/64750076): Check if calling pid should really be -1.
+ final int res = mAtmService.getActivityStartController()
+ .obtainStarter(destIntent, "navigateUpTo")
+ .setCaller(srec.app.getThread())
+ .setActivityInfo(aInfo)
+ .setResultTo(parent.token)
+ .setIntentGrants(destGrants)
+ .setCallingPid(-1)
+ .setCallingUid(callingUid)
+ .setCallingPackage(srec.packageName)
+ .setCallingFeatureId(parent.launchedFromFeatureId)
+ .setRealCallingPid(-1)
+ .setRealCallingUid(callingUid)
+ .setComponentSpecified(true)
+ .execute();
+ foundParentInTask = isStartResultSuccessful(res);
+ if (res == ActivityManager.START_SUCCESS) {
+ parent.finishIfPossible(resultCode, resultData, resultGrants,
+ "navigate-top", true /* oomAdj */);
}
- parent.finishIfPossible(resultCode, resultData, resultGrants,
- "navigate-top", true /* oomAdj */);
+ } catch (RemoteException e) {
+ foundParentInTask = false;
}
}
Binder.restoreCallingIdentity(origId);
@@ -5438,10 +5421,7 @@ class Task extends TaskFragment {
if (timeTracker != null) {
// The caller wants a time tracker associated with this task.
- final PooledConsumer c = PooledLambda.obtainConsumer(ActivityRecord::setAppTimeTracker,
- PooledLambda.__(ActivityRecord.class), timeTracker);
- tr.forAllActivities(c);
- c.recycle();
+ tr.forAllActivities(a -> { a.appTimeTracker = timeTracker; });
}
try {
@@ -5612,11 +5592,11 @@ class Task extends TaskFragment {
try {
// TODO: Why not just set this on the root task directly vs. on each tasks?
// Update override configurations of all tasks in the root task.
- final PooledConsumer c = PooledLambda.obtainConsumer(
- Task::processTaskResizeBounds, PooledLambda.__(Task.class),
- displayedBounds);
- forAllTasks(c, true /* traverseTopToBottom */);
- c.recycle();
+ forAllTasks(task -> {
+ if (task.isResizeable()) {
+ task.setBounds(displayedBounds);
+ }
+ }, true /* traverseTopToBottom */);
if (!deferResume) {
ensureVisibleActivitiesConfiguration(topRunningActivity(), preserveWindows);
@@ -5627,12 +5607,6 @@ class Task extends TaskFragment {
}
}
- private static void processTaskResizeBounds(Task task, Rect displayedBounds) {
- if (!task.isResizeable()) return;
-
- task.setBounds(displayedBounds);
- }
-
boolean willActivityBeVisible(IBinder token) {
final ActivityRecord r = ActivityRecord.forTokenLocked(token);
if (r == null) {
@@ -5718,22 +5692,15 @@ class Task extends TaskFragment {
// All activities that came from the package must be
// restarted as if there was a config change.
- PooledConsumer c = PooledLambda.obtainConsumer(Task::restartPackage,
- PooledLambda.__(ActivityRecord.class), starting, packageName);
- forAllActivities(c);
- c.recycle();
-
- return starting;
- }
-
- private static void restartPackage(
- ActivityRecord r, ActivityRecord starting, String packageName) {
- if (r.info.packageName.equals(packageName)) {
+ forAllActivities(r -> {
+ if (!r.info.packageName.equals(packageName)) return;
r.forceNewConfig = true;
if (starting != null && r == starting && r.mVisibleRequested) {
r.startFreezingScreenLocked(CONFIG_SCREEN_LAYOUT);
}
- }
+ });
+
+ return starting;
}
Task reuseOrCreateTask(ActivityInfo info, Intent intent, boolean toTop) {
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index f784f7105bfe..4f1a561b14df 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -89,6 +89,7 @@ import android.view.DisplayInfo;
import android.view.RemoteAnimationTarget;
import android.view.SurfaceControl;
import android.window.ITaskFragmentOrganizer;
+import android.window.ScreenCapture;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizerToken;
@@ -297,6 +298,9 @@ class TaskFragment extends WindowContainer<WindowContainer> {
final Point mLastSurfaceSize = new Point();
+ /** The latest updated value when there's a child {@link #onActivityVisibleRequestedChanged} */
+ boolean mVisibleRequested;
+
private final Rect mTmpBounds = new Rect();
private final Rect mTmpFullBounds = new Rect();
/** For calculating screenWidthDp and screenWidthDp, i.e. the area without the system bars. */
@@ -306,7 +310,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
//TODO(b/207481538) Remove once the infrastructure to support per-activity screenshot is
// implemented
- HashMap<String, SurfaceControl.ScreenshotHardwareBuffer> mBackScreenshots = new HashMap<>();
+ HashMap<String, ScreenCapture.ScreenshotHardwareBuffer> mBackScreenshots = new HashMap<>();
private final EnsureActivitiesVisibleHelper mEnsureActivitiesVisibleHelper =
new EnsureActivitiesVisibleHelper(this);
@@ -1595,12 +1599,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
boolean pauseImmediately = false;
boolean shouldAutoPip = false;
if (resuming != null) {
+ // We do not want to trigger auto-PiP upon launch of a translucent activity.
+ final boolean resumingOccludesParent = resuming.occludesParent();
// 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.checkEnterPictureInPictureState(
"shouldAutoPipWhilePausing", userLeaving);
- if (userLeaving && lastResumedCanPip
+ if (userLeaving && resumingOccludesParent && lastResumedCanPip
&& prev.pictureInPictureArgs.isAutoEnterEnabled()) {
shouldAutoPip = true;
} else if (!lastResumedCanPip) {
@@ -1713,7 +1719,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
ProtoLog.v(WM_DEBUG_STATES, "Executing finish of activity: %s", prev);
prev = prev.completeFinishing(false /* updateVisibility */,
"completePausedLocked");
- } else if (prev.hasProcess()) {
+ } else if (prev.attachedToProcess()) {
ProtoLog.v(WM_DEBUG_STATES, "Enqueue pending stop if needed: %s "
+ "wasStopping=%b visibleRequested=%b", prev, wasStopping,
prev.mVisibleRequested);
@@ -1859,7 +1865,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
ProtoLog.v(WM_DEBUG_BACK_PREVIEW, "Screenshotting Activity %s",
r.mActivityComponent.flattenToString());
Rect outBounds = r.getBounds();
- SurfaceControl.ScreenshotHardwareBuffer backBuffer = SurfaceControl.captureLayers(
+ ScreenCapture.ScreenshotHardwareBuffer backBuffer = ScreenCapture.captureLayers(
r.mSurfaceControl,
new Rect(0, 0, outBounds.width(), outBounds.height()),
1f);
@@ -2379,6 +2385,14 @@ class TaskFragment extends WindowContainer<WindowContainer> {
}
}
+ void sendTaskFragmentParentInfoChanged() {
+ final Task parentTask = getParent().asTask();
+ if (mTaskFragmentOrganizer != null && parentTask != null) {
+ mTaskFragmentOrganizerController
+ .onTaskFragmentParentInfoChanged(mTaskFragmentOrganizer, parentTask);
+ }
+ }
+
private void sendTaskFragmentAppeared() {
if (mTaskFragmentOrganizer != null) {
mTaskFragmentOrganizerController.onTaskFragmentAppeared(mTaskFragmentOrganizer, this);
@@ -2414,7 +2428,7 @@ class TaskFragment extends WindowContainer<WindowContainer> {
mRemoteToken.toWindowContainerToken(),
getConfiguration(),
getNonFinishingActivityCount(),
- isVisible(),
+ isVisibleRequested(),
childActivities,
positionInParent,
mClearedTaskForReuse,
@@ -2666,6 +2680,18 @@ class TaskFragment extends WindowContainer<WindowContainer> {
return getWindowingMode() == WINDOWING_MODE_FULLSCREEN || matchParentBounds();
}
+ void onActivityVisibleRequestedChanged() {
+ final boolean isVisibleRequested = isVisibleRequested();
+ if (mVisibleRequested == isVisibleRequested) {
+ return;
+ }
+ mVisibleRequested = isVisibleRequested;
+ final TaskFragment parentTf = getParent().asTaskFragment();
+ if (parentTf != null) {
+ parentTf.onActivityVisibleRequestedChanged();
+ }
+ }
+
String toFullString() {
final StringBuilder sb = new StringBuilder(128);
sb.append(this);
diff --git a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
index 8c037a7390b1..2d5c9897a82c 100644
--- a/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskFragmentOrganizerController.java
@@ -49,6 +49,7 @@ import android.view.WindowManager;
import android.window.ITaskFragmentOrganizer;
import android.window.ITaskFragmentOrganizerController;
import android.window.TaskFragmentInfo;
+import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerTransaction;
@@ -118,10 +119,10 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
private final Map<TaskFragment, Integer> mTaskFragmentTaskIds = new WeakHashMap<>();
/**
- * Map from {@link Task#mTaskId} to the last Task {@link Configuration} sent to the
+ * Map from {@link Task#mTaskId} to the last {@link TaskFragmentParentInfo} sent to the
* organizer.
*/
- private final SparseArray<Configuration> mLastSentTaskFragmentParentConfigs =
+ private final SparseArray<TaskFragmentParentInfo> mLastSentTaskFragmentParentInfos =
new SparseArray<>();
/**
@@ -225,7 +226,7 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
taskId = mTaskFragmentTaskIds.remove(tf);
if (!mTaskFragmentTaskIds.containsValue(taskId)) {
// No more TaskFragment in the Task.
- mLastSentTaskFragmentParentConfigs.remove(taskId);
+ mLastSentTaskFragmentParentInfos.remove(taskId);
}
} else {
// This can happen if the appeared wasn't sent before remove.
@@ -260,25 +261,27 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
@Nullable
- TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(
- @NonNull Task task) {
+ TaskFragmentTransaction.Change prepareTaskFragmentParentInfoChanged(@NonNull Task task) {
final int taskId = task.mTaskId;
// Check if the parent info is different from the last reported parent info.
- final Configuration taskConfig = task.getConfiguration();
- final Configuration lastParentConfig = mLastSentTaskFragmentParentConfigs.get(taskId);
- if (configurationsAreEqualForOrganizer(taskConfig, lastParentConfig)
- && taskConfig.windowConfiguration.getWindowingMode()
- == lastParentConfig.windowConfiguration.getWindowingMode()) {
+ final TaskFragmentParentInfo parentInfo = task.getTaskFragmentParentInfo();
+ final TaskFragmentParentInfo lastParentInfo = mLastSentTaskFragmentParentInfos
+ .get(taskId);
+ final Configuration lastParentConfig = lastParentInfo != null
+ ? lastParentInfo.getConfiguration() : null;
+ if (parentInfo.equalsForTaskFragmentOrganizer(lastParentInfo)
+ && configurationsAreEqualForOrganizer(parentInfo.getConfiguration(),
+ lastParentConfig)) {
return null;
}
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER,
"TaskFragment parent info changed name=%s parentTaskId=%d",
task.getName(), taskId);
- mLastSentTaskFragmentParentConfigs.put(taskId, new Configuration(taskConfig));
+ mLastSentTaskFragmentParentInfos.put(taskId, new TaskFragmentParentInfo(parentInfo));
return new TaskFragmentTransaction.Change(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED)
.setTaskId(taskId)
- .setTaskConfiguration(taskConfig);
+ .setTaskFragmentParentInfo(parentInfo);
}
@NonNull
@@ -646,6 +649,34 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
.build());
}
+ void onTaskFragmentParentInfoChanged(@NonNull ITaskFragmentOrganizer organizer,
+ @NonNull Task task) {
+ validateAndGetState(organizer);
+ final PendingTaskFragmentEvent pendingEvent = getLastPendingParentInfoChangedEvent(
+ organizer, task);
+ if (pendingEvent == null) {
+ addPendingEvent(new PendingTaskFragmentEvent.Builder(
+ PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED, organizer)
+ .setTask(task)
+ .build());
+ }
+ }
+
+ @Nullable
+ private PendingTaskFragmentEvent getLastPendingParentInfoChangedEvent(
+ @NonNull ITaskFragmentOrganizer organizer, @NonNull Task task) {
+ final List<PendingTaskFragmentEvent> events = mPendingTaskFragmentEvents
+ .get(organizer.asBinder());
+ for (int i = events.size() - 1; i >= 0; i--) {
+ final PendingTaskFragmentEvent event = events.get(i);
+ if (task == event.mTask
+ && event.mEventType == PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED) {
+ return event;
+ }
+ }
+ return null;
+ }
+
private void addPendingEvent(@NonNull PendingTaskFragmentEvent event) {
mPendingTaskFragmentEvents.get(event.mTaskFragmentOrg.asBinder()).add(event);
}
@@ -848,7 +879,9 @@ public class TaskFragmentOrganizerController extends ITaskFragmentOrganizerContr
}
private boolean shouldSendEventWhenTaskInvisible(@NonNull PendingTaskFragmentEvent event) {
- if (event.mEventType == PendingTaskFragmentEvent.EVENT_ERROR) {
+ if (event.mEventType == PendingTaskFragmentEvent.EVENT_ERROR
+ // Always send parent info changed to update task visibility
+ || event.mEventType == PendingTaskFragmentEvent.EVENT_PARENT_INFO_CHANGED) {
return true;
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 534616fb7207..9306749f17b9 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -58,6 +58,7 @@ import android.view.ThreadedRenderer;
import android.view.WindowInsets.Type;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowManager.LayoutParams;
+import android.window.ScreenCapture;
import android.window.TaskSnapshot;
import com.android.internal.R;
@@ -389,11 +390,11 @@ class TaskSnapshotController {
}
@Nullable
- SurfaceControl.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
+ ScreenCapture.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
TaskSnapshot.Builder builder) {
Point taskSize = new Point();
Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "createTaskSnapshot");
- final SurfaceControl.ScreenshotHardwareBuffer taskSnapshot = createTaskSnapshot(task,
+ final ScreenCapture.ScreenshotHardwareBuffer taskSnapshot = createTaskSnapshot(task,
mHighResTaskSnapshotScale, builder.getPixelFormat(), taskSize, builder);
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
builder.setTaskSize(taskSize);
@@ -401,7 +402,7 @@ class TaskSnapshotController {
}
@Nullable
- private SurfaceControl.ScreenshotHardwareBuffer createImeSnapshot(@NonNull Task task,
+ private ScreenCapture.ScreenshotHardwareBuffer createImeSnapshot(@NonNull Task task,
int pixelFormat) {
if (task.getSurfaceControl() == null) {
if (DEBUG_SCREENSHOT) {
@@ -410,11 +411,11 @@ class TaskSnapshotController {
return null;
}
final WindowState imeWindow = task.getDisplayContent().mInputMethodWindow;
- SurfaceControl.ScreenshotHardwareBuffer imeBuffer = null;
+ ScreenCapture.ScreenshotHardwareBuffer imeBuffer = null;
if (imeWindow != null && imeWindow.isWinVisibleLw()) {
final Rect bounds = imeWindow.getParentFrame();
bounds.offsetTo(0, 0);
- imeBuffer = SurfaceControl.captureLayersExcluding(imeWindow.getSurfaceControl(),
+ imeBuffer = ScreenCapture.captureLayersExcluding(imeWindow.getSurfaceControl(),
bounds, 1.0f, pixelFormat, null);
}
return imeBuffer;
@@ -425,7 +426,7 @@ class TaskSnapshotController {
* task to keep IME visibility while app transitioning.
*/
@Nullable
- SurfaceControl.ScreenshotHardwareBuffer snapshotImeFromAttachedTask(@NonNull Task task) {
+ ScreenCapture.ScreenshotHardwareBuffer snapshotImeFromAttachedTask(@NonNull Task task) {
// Check if the IME targets task ready to take the corresponding IME snapshot, if not,
// means the task is not yet visible for some reasons and no need to snapshot IME surface.
if (checkIfReadyToSnapshot(task) == null) {
@@ -438,7 +439,7 @@ class TaskSnapshotController {
}
@Nullable
- SurfaceControl.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
+ ScreenCapture.ScreenshotHardwareBuffer createTaskSnapshot(@NonNull Task task,
float scaleFraction, int pixelFormat, Point outTaskSize, TaskSnapshot.Builder builder) {
if (task.getSurfaceControl() == null) {
if (DEBUG_SCREENSHOT) {
@@ -473,8 +474,8 @@ class TaskSnapshotController {
}
builder.setHasImeSurface(!excludeIme && imeWindow != null && imeWindow.isVisible());
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureLayersExcluding(
+ final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+ ScreenCapture.captureLayersExcluding(
task.getSurfaceControl(), mTmpRect, scaleFraction,
pixelFormat, excludeLayers);
if (outTaskSize != null) {
@@ -508,7 +509,7 @@ class TaskSnapshotController {
return null;
}
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
createTaskSnapshot(task, builder);
if (screenshotBuffer == null) {
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 32b753241cab..93d9b0e724ce 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -40,8 +40,10 @@ 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_FILLS_TASK;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
-import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
@@ -77,6 +79,7 @@ import android.view.Display;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.RemoteTransition;
+import android.window.ScreenCapture;
import android.window.TransitionInfo;
import com.android.internal.annotations.VisibleForTesting;
@@ -1838,8 +1841,10 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
flags |= FLAG_WILL_IME_SHOWN;
}
}
+ Task parentTask = null;
final ActivityRecord record = wc.asActivityRecord();
if (record != null) {
+ parentTask = record.getTask();
if (record.mUseTransferredAnimation) {
flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
}
@@ -1847,6 +1852,24 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
flags |= FLAG_IS_VOICE_INTERACTION;
}
}
+ final TaskFragment taskFragment = wc.asTaskFragment();
+ if (taskFragment != null && task == null) {
+ parentTask = taskFragment.getTask();
+ }
+ if (parentTask != null) {
+ if (parentTask.forAllLeafTaskFragments(TaskFragment::isEmbedded)) {
+ // Whether this is in a Task with embedded activity.
+ flags |= FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
+ }
+ if (parentTask.forAllActivities(ActivityRecord::hasStartingWindow)) {
+ // The starting window should cover all windows inside the leaf Task.
+ flags |= FLAG_IS_BEHIND_STARTING_WINDOW;
+ }
+ if (isWindowFillingTask(wc, parentTask)) {
+ // Whether the container fills its parent Task bounds.
+ flags |= FLAG_FILLS_TASK;
+ }
+ }
final DisplayContent dc = wc.asDisplayContent();
if (dc != null) {
flags |= FLAG_IS_DISPLAY;
@@ -1863,11 +1886,24 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
if (occludesKeyguard(wc)) {
flags |= FLAG_OCCLUDES_KEYGUARD;
}
- if (wc.isEmbedded()) {
- flags |= FLAG_IS_EMBEDDED;
- }
return flags;
}
+
+ /** Whether the container fills its parent Task bounds before and after the transition. */
+ private boolean isWindowFillingTask(@NonNull WindowContainer wc, @NonNull Task parentTask) {
+ final Rect taskBounds = parentTask.getBounds();
+ final int taskWidth = taskBounds.width();
+ final int taskHeight = taskBounds.height();
+ final Rect startBounds = mAbsoluteBounds;
+ final Rect endBounds = wc.getBounds();
+ // Treat it as filling the task if it is not visible.
+ final boolean isInvisibleOrFillingTaskBeforeTransition = !mVisible
+ || (taskWidth == startBounds.width() && taskHeight == startBounds.height());
+ final boolean isInVisibleOrFillingTaskAfterTransition = !wc.isVisibleRequested()
+ || (taskWidth == endBounds.width() && taskHeight == endBounds.height());
+ return isInvisibleOrFillingTaskBeforeTransition
+ && isInVisibleOrFillingTaskAfterTransition;
+ }
}
/**
@@ -2105,14 +2141,14 @@ class Transition extends Binder implements BLASTSyncEngine.TransactionReadyListe
Rect cropBounds = new Rect(bounds);
cropBounds.offsetTo(0, 0);
- SurfaceControl.LayerCaptureArgs captureArgs =
- new SurfaceControl.LayerCaptureArgs.Builder(wc.getSurfaceControl())
+ ScreenCapture.LayerCaptureArgs captureArgs =
+ new ScreenCapture.LayerCaptureArgs.Builder(wc.getSurfaceControl())
.setSourceCrop(cropBounds)
.setCaptureSecureLayers(true)
.setAllowProtected(true)
.build();
- SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- SurfaceControl.captureLayers(captureArgs);
+ ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+ ScreenCapture.captureLayers(captureArgs);
final HardwareBuffer buffer = screenshotBuffer == null ? null
: screenshotBuffer.getHardwareBuffer();
if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 221e18699c1c..77d0f378cee1 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -67,7 +67,7 @@ class TransitionController {
/** Which sync method to use for transition syncs. */
static final int SYNC_METHOD =
- android.os.SystemProperties.getBoolean("persist.wm.debug.shell_transit_blast", true)
+ android.os.SystemProperties.getBoolean("persist.wm.debug.shell_transit_blast", false)
? BLASTSyncEngine.METHOD_BLAST : BLASTSyncEngine.METHOD_NONE;
/** The same as legacy APP_TRANSITION_TIMEOUT_MS. */
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index d652b8e0ab32..0fd3e9b4abae 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -53,6 +53,7 @@ import android.view.DisplayInfo;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.Animation;
+import android.window.ScreenCapture;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogImpl;
@@ -931,7 +932,7 @@ class WallpaperController {
final Rect bounds = wallpaperWindowState.getBounds();
bounds.offsetTo(0, 0);
- SurfaceControl.ScreenshotHardwareBuffer wallpaperBuffer = SurfaceControl.captureLayers(
+ ScreenCapture.ScreenshotHardwareBuffer wallpaperBuffer = ScreenCapture.captureLayers(
wallpaperWindowState.getSurfaceControl(), bounds, 1 /* frameScale */);
if (wallpaperBuffer == null) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index bc668526b24c..9763df6b967a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -477,7 +477,10 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
}
mLocalInsetsSourceProviders.remove(insetsTypes[i]);
}
- mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+ // Update insets if this window is attached.
+ if (mDisplayContent != null) {
+ mDisplayContent.getInsetsStateController().updateAboveInsetsState(true);
+ }
}
/**
@@ -622,7 +625,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
setInitialSurfaceControlProperties(makeSurface());
}
- void setInitialSurfaceControlProperties(SurfaceControl.Builder b) {
+ void setInitialSurfaceControlProperties(Builder b) {
setSurfaceControl(b.setCallsite("WindowContainer.setInitialSurfaceControlProperties").build());
if (showSurfaceOnCreation()) {
getSyncTransaction().show(mSurfaceControl);
@@ -652,7 +655,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
mLastSurfacePosition.set(0, 0);
mLastDeltaRotation = Surface.ROTATION_0;
- final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(null)
+ final Builder b = mWmService.makeSurfaceBuilder(null)
.setContainerLayer()
.setName(getName());
@@ -2195,6 +2198,17 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
callback, boundary, includeBoundary, traverseTopToBottom, boundaryFound);
}
+ @Nullable
+ TaskFragment getTaskFragment(Predicate<TaskFragment> callback) {
+ for (int i = mChildren.size() - 1; i >= 0; --i) {
+ final TaskFragment tf = mChildren.get(i).getTaskFragment(callback);
+ if (tf != null) {
+ return tf;
+ }
+ }
+ return null;
+ }
+
WindowState getWindow(Predicate<WindowState> callback) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
final WindowState w = mChildren.get(i).getWindow(callback);
@@ -2437,7 +2451,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
} while (current != null);
}
- SurfaceControl.Builder makeSurface() {
+ Builder makeSurface() {
final WindowContainer p = getParent();
return p.makeChildSurface(this);
}
@@ -2446,7 +2460,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<
* @param child The WindowContainer this child surface is for, or null if the Surface
* is not assosciated with a WindowContainer (e.g. a surface used for Dimming).
*/
- SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+ Builder makeChildSurface(WindowContainer child) {
final WindowContainer p = getParent();
// Give the parent a chance to set properties. In hierarchy v1 we rely
// on this to set full-screen dimensions on all our Surface-less Layers.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 5ad6fbd69d7e..29ab56200e5b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -123,7 +123,10 @@ import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_
import static com.android.server.wm.RootWindowContainer.MATCH_ATTACHED_TASK_OR_RECENT_TASKS;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL;
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.WindowContainer.AnimationFlags.CHILDREN;
+import static com.android.server.wm.WindowContainer.AnimationFlags.PARENTS;
import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
@@ -155,6 +158,7 @@ import android.annotation.IntRange;
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.ActivityThread;
@@ -289,6 +293,7 @@ import android.view.displayhash.DisplayHash;
import android.view.displayhash.VerifiedDisplayHash;
import android.window.ClientWindowFrames;
import android.window.ITaskFpsCallback;
+import android.window.ScreenCapture;
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
@@ -304,8 +309,6 @@ import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.LatencyTracker;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.view.WindowManagerPolicyThread;
import com.android.server.AnimationThread;
import com.android.server.DisplayThread;
@@ -314,6 +317,7 @@ import com.android.server.LocalServices;
import com.android.server.UiThread;
import com.android.server.Watchdog;
import com.android.server.input.InputManagerService;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.policy.WindowManagerPolicy;
import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
import com.android.server.power.ShutdownThread;
@@ -469,10 +473,7 @@ public class WindowManagerService extends IWindowManager.Stub
public void onVrStateChanged(boolean enabled) {
synchronized (mGlobalLock) {
mVrModeEnabled = enabled;
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::onVrStateChangedLw, PooledLambda.__(), enabled);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
+ mRoot.forAllDisplayPolicies(p -> p.onVrStateChangedLw(enabled));
}
}
};
@@ -503,15 +504,9 @@ public class WindowManagerService extends IWindowManager.Stub
};
/**
- * Current user when multi-user is enabled. Don't show windows of
- * non-current user. Also see mCurrentProfileIds.
+ * Current user when multi-user is enabled. Don't show windows of non-current user.
*/
- int mCurrentUserId;
- /**
- * Users that are profiles of the current user. These are also allowed to show windows
- * on the current user.
- */
- int[] mCurrentProfileIds = new int[] {};
+ @UserIdInt int mCurrentUserId;
final Context mContext;
@@ -533,6 +528,7 @@ public class WindowManagerService extends IWindowManager.Stub
final IActivityManager mActivityManager;
final ActivityManagerInternal mAmInternal;
+ final UserManagerInternal mUmInternal;
final AppOpsManager mAppOps;
final PackageManagerInternal mPmInternal;
@@ -899,11 +895,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
mPointerLocationEnabled = enablePointerLocation;
synchronized (mGlobalLock) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::setPointerLocationEnabled, PooledLambda.__(),
- mPointerLocationEnabled);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
+ mRoot.forAllDisplayPolicies(
+ p -> p.setPointerLocationEnabled(mPointerLocationEnabled));
}
}
@@ -1262,6 +1255,7 @@ public class WindowManagerService extends IWindowManager.Stub
mActivityManager = ActivityManager.getService();
mAmInternal = LocalServices.getService(ActivityManagerInternal.class);
+ mUmInternal = LocalServices.getService(UserManagerInternal.class);
mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);
AppOpsManager.OnOpChangedInternalListener opListener =
new AppOpsManager.OnOpChangedInternalListener() {
@@ -1832,8 +1826,12 @@ public class WindowManagerService extends IWindowManager.Stub
ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addWindow: New client %s"
+ ": window=%s Callers=%s", client.asBinder(), win, Debug.getCallers(5));
- if ((win.isVisibleRequestedOrAdding() && displayContent.updateOrientation())
- || displayPolicy.updateDecorInsetsInfoIfNeeded(win)) {
+ boolean needToSendNewConfiguration =
+ win.isVisibleRequestedOrAdding() && displayContent.updateOrientation();
+ if (win.providesNonDecorInsets()) {
+ needToSendNewConfiguration |= displayPolicy.updateDecorInsetsInfo();
+ }
+ if (needToSendNewConfiguration) {
displayContent.sendNewConfiguration();
}
@@ -2302,8 +2300,8 @@ public class WindowManagerService extends IWindowManager.Stub
& WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED) != 0) {
win.mLayoutNeeded = true;
}
- if (layoutChanged) {
- configChanged = displayPolicy.updateDecorInsetsInfoIfNeeded(win);
+ if (layoutChanged && win.providesNonDecorInsets()) {
+ configChanged = displayPolicy.updateDecorInsetsInfo();
}
if (win.mActivityRecord != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
|| (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
@@ -2603,10 +2601,22 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.isWinVisibleLw() && win.mDisplayContent.okToAnimate()) {
String reason = null;
if (winAnimator.applyAnimationLocked(transit, false)) {
+ // This is a WMCore-driven window animation.
reason = "applyAnimation";
focusMayChange = true;
win.mAnimatingExit = true;
- } else if (win.isExitAnimationRunningSelfOrParent()) {
+ } else if (
+ // This is already animating via a WMCore-driven window animation
+ win.isSelfAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION)
+ // Or already animating as part of a legacy app-transition
+ || win.isAnimating(PARENTS | TRANSITION,
+ ANIMATION_TYPE_APP_TRANSITION | ANIMATION_TYPE_RECENTS)
+ // Or already animating as part of a shell-transition.
+ || (win.inTransition()
+ // Filter out non-app windows since transitions don't animate those
+ // (but may still "wait" on them for readiness)
+ && (win.mActivityRecord != null || win.mIsWallpaper))) {
+ // TODO(b/247005789): set mAnimatingExit somewhere in shell-transitions setup.
reason = "animating";
win.mAnimatingExit = true;
} else if (win.mDisplayContent.mWallpaperController.isWallpaperTarget(win)
@@ -3121,10 +3131,7 @@ public class WindowManagerService extends IWindowManager.Stub
@Override
public void onPowerKeyDown(boolean isScreenOn) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::onPowerKeyDown, PooledLambda.__(), isScreenOn);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
+ mRoot.forAllDisplayPolicies(p -> p.onPowerKeyDown(isScreenOn));
}
@Override
@@ -3557,16 +3564,9 @@ public class WindowManagerService extends IWindowManager.Stub
confirm);
}
- public void setCurrentProfileIds(final int[] currentProfileIds) {
- synchronized (mGlobalLock) {
- mCurrentProfileIds = currentProfileIds;
- }
- }
-
- public void setCurrentUser(final int newUserId, final int[] currentProfileIds) {
+ public void setCurrentUser(@UserIdInt int newUserId) {
synchronized (mGlobalLock) {
mCurrentUserId = newUserId;
- mCurrentProfileIds = currentProfileIds;
mPolicy.setCurrentUserLw(newUserId);
mKeyguardDisableHandler.setCurrentUser(newUserId);
@@ -3589,12 +3589,8 @@ public class WindowManagerService extends IWindowManager.Stub
}
/* Called by WindowState */
- boolean isCurrentProfile(int userId) {
- if (userId == mCurrentUserId) return true;
- for (int i = 0; i < mCurrentProfileIds.length; i++) {
- if (mCurrentProfileIds[i] == userId) return true;
- }
- return false;
+ boolean isUserVisible(@UserIdInt int userId) {
+ return mUmInternal.isUserVisible(userId);
}
public void enableScreenAfterBoot() {
@@ -3980,7 +3976,7 @@ public class WindowManagerService extends IWindowManager.Stub
* Generates and returns an up-to-date {@link Bitmap} for the specified taskId.
*
* @param taskId The task ID of the task for which a Bitmap is requested.
- * @param layerCaptureArgsBuilder A {@link SurfaceControl.LayerCaptureArgs.Builder} with
+ * @param layerCaptureArgsBuilder A {@link ScreenCapture.LayerCaptureArgs.Builder} with
* arguments for how to capture the Bitmap. The caller can
* specify any arguments, but this method will ensure that the
* specified task's SurfaceControl is used and the crop is set to
@@ -3990,7 +3986,7 @@ public class WindowManagerService extends IWindowManager.Stub
*/
@Nullable
public Bitmap captureTaskBitmap(int taskId,
- @NonNull SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder) {
+ @NonNull ScreenCapture.LayerCaptureArgs.Builder layerCaptureArgsBuilder) {
if (mTaskSnapshotController.shouldDisableSnapshots()) {
return null;
}
@@ -4009,7 +4005,7 @@ public class WindowManagerService extends IWindowManager.Stub
mTmpRect.offsetTo(0, 0);
final SurfaceControl sc = task.getSurfaceControl();
- final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers(
+ final ScreenCapture.ScreenshotHardwareBuffer buffer = ScreenCapture.captureLayers(
layerCaptureArgsBuilder.setLayer(sc).setSourceCrop(mTmpRect).build());
if (buffer == null) {
Slog.w(TAG, "Could not get screenshot buffer for taskId: " + taskId);
@@ -5810,6 +5806,21 @@ public class WindowManagerService extends IWindowManager.Stub
return -1;
}
+ /**
+ * Return the display Id that has the given uniqueId. Unique ID is defined in
+ * {@link DisplayInfo#uniqueId}.
+ */
+ @Override
+ public int getDisplayIdByUniqueId(String uniqueId) {
+ synchronized (mGlobalLock) {
+ final DisplayContent displayContent = mRoot.getDisplayContent(uniqueId);
+ if (displayContent != null && displayContent.hasAccess(Binder.getCallingUid())) {
+ return displayContent.mDisplayId;
+ }
+ }
+ return -1;
+ }
+
@Override
public void setForcedDisplayDensityForUser(int displayId, int density, int userId) {
if (mContext.checkCallingOrSelfPermission(WRITE_SECURE_SETTINGS)
@@ -8369,10 +8380,7 @@ public class WindowManagerService extends IWindowManager.Stub
void onLockTaskStateChanged(int lockTaskState) {
// TODO: pass in displayId to determine which display the lock task state changed
synchronized (mGlobalLock) {
- final PooledConsumer c = PooledLambda.obtainConsumer(
- DisplayPolicy::onLockTaskStateChangedLw, PooledLambda.__(), lockTaskState);
- mRoot.forAllDisplayPolicies(c);
- c.recycle();
+ mRoot.forAllDisplayPolicies(p -> p.onLockTaskStateChangedLw(lockTaskState));
}
}
@@ -8481,7 +8489,10 @@ public class WindowManagerService extends IWindowManager.Stub
if (mFocusedInputTarget != t && mFocusedInputTarget != null) {
mFocusedInputTarget.handleTapOutsideFocusOutsideSelf();
}
+ // Trigger Activity#onUserLeaveHint() if the order change of task pauses any activities.
+ mAtmService.mTaskSupervisor.mUserLeaving = true;
t.handleTapOutsideFocusInsideSelf();
+ mAtmService.mTaskSupervisor.mUserLeaving = false;
}
@VisibleForTesting
@@ -9085,8 +9096,8 @@ public class WindowManagerService extends IWindowManager.Stub
// be covering it with the same uid. We want to make sure we include content that's
// covering to ensure we get as close as possible to what the user sees
final int uid = session.mUid;
- SurfaceControl.LayerCaptureArgs.Builder args =
- new SurfaceControl.LayerCaptureArgs.Builder(displaySurfaceControl)
+ ScreenCapture.LayerCaptureArgs.Builder args =
+ new ScreenCapture.LayerCaptureArgs.Builder(displaySurfaceControl)
.setUid(uid)
.setSourceCrop(boundsInDisplay);
@@ -9261,4 +9272,46 @@ public class WindowManagerService extends IWindowManager.Stub
"Unexpected letterbox background type: " + letterboxBackgroundType);
}
}
+
+ @Override
+ public void captureDisplay(int displayId, @Nullable ScreenCapture.CaptureArgs captureArgs,
+ ScreenCapture.ScreenCaptureListener listener) {
+ Slog.d(TAG, "captureDisplay");
+ if (!checkCallingPermission(READ_FRAME_BUFFER, "captureDisplay()")) {
+ throw new SecurityException("Requires READ_FRAME_BUFFER permission");
+ }
+
+ ScreenCapture.captureLayers(getCaptureArgs(displayId, captureArgs), listener);
+ }
+
+ @VisibleForTesting
+ ScreenCapture.LayerCaptureArgs getCaptureArgs(int displayId,
+ @Nullable ScreenCapture.CaptureArgs captureArgs) {
+ final SurfaceControl displaySurfaceControl;
+ synchronized (mGlobalLock) {
+ DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+ if (displayContent == null) {
+ throw new IllegalArgumentException("Trying to screenshot and invalid display: "
+ + displayId);
+ }
+
+ displaySurfaceControl = displayContent.getSurfaceControl();
+
+ if (captureArgs == null) {
+ captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+ .build();
+ }
+
+ if (captureArgs.mSourceCrop.isEmpty()) {
+ displayContent.getBounds(mTmpRect);
+ mTmpRect.offsetTo(0, 0);
+ } else {
+ mTmpRect.set(captureArgs.mSourceCrop);
+ }
+ }
+
+ return new ScreenCapture.LayerCaptureArgs.Builder(displaySurfaceControl, captureArgs)
+ .setSourceCrop(mTmpRect)
+ .build();
+ }
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index c22091b4eacb..283830430c1a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -264,8 +264,23 @@ public class WindowManagerShellCommand extends ShellCommand {
private int runDisplayDensity(PrintWriter pw) throws RemoteException {
String densityStr = getNextArg();
+ String option = getNextOption();
+ String arg = getNextArg();
int density;
- final int displayId = getDisplayId(densityStr);
+ int displayId = Display.DEFAULT_DISPLAY;
+ if ("-d".equals(option) && arg != null) {
+ try {
+ displayId = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad number " + e);
+ }
+ } else if ("-u".equals(option) && arg != null) {
+ displayId = mInterface.getDisplayIdByUniqueId(arg);
+ if (displayId == Display.INVALID_DISPLAY) {
+ getErrPrintWriter().println("Error: the uniqueId is invalid ");
+ return -1;
+ }
+ }
if (densityStr == null) {
printInitialDisplayDensity(pw, displayId);
@@ -1312,7 +1327,7 @@ public class WindowManagerShellCommand extends ShellCommand {
pw.println(" size [reset|WxH|WdpxHdp] [-d DISPLAY_ID]");
pw.println(" Return or override display size.");
pw.println(" width and height in pixels unless suffixed with 'dp'.");
- pw.println(" density [reset|DENSITY] [-d DISPLAY_ID]");
+ pw.println(" density [reset|DENSITY] [-d DISPLAY_ID] [-u UNIQUE_ID]");
pw.println(" Return or override display density.");
pw.println(" folded-area [reset|LEFT,TOP,RIGHT,BOTTOM]");
pw.println(" Return or override folded area.");
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 9456f0fcced7..e2f833c9fc5a 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -25,6 +25,7 @@ import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP
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_FINISH_ACTIVITY;
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_REMOVE_INSETS_PROVIDER;
@@ -90,8 +91,6 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.LocalServices;
import com.android.server.pm.LauncherAppsService.LauncherAppsServiceInternal;
@@ -481,7 +480,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
}
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
final int hopSize = hops.size();
- ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
+ final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
t.getChanges().entrySet().iterator();
while (entries.hasNext()) {
@@ -591,16 +590,10 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
} else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
- final PooledConsumer f = PooledLambda.obtainConsumer(
- ActivityRecord::ensureActivityConfiguration,
- PooledLambda.__(ActivityRecord.class), 0,
- true /* preserveWindow */);
- try {
- for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
- haveConfigChanges.valueAt(i).forAllActivities(f);
- }
- } finally {
- f.recycle();
+ for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
+ haveConfigChanges.valueAt(i).forAllActivities(r -> {
+ r.ensureActivityConfiguration(0, PRESERVE_WINDOWS);
+ });
}
}
@@ -715,7 +708,7 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
final int childWindowingMode = c.getActivityWindowingMode();
if (childWindowingMode > -1) {
- tr.setActivityWindowingMode(childWindowingMode);
+ tr.forAllActivities(a -> { a.setWindowingMode(childWindowingMode); });
}
if (t != null) {
@@ -1007,6 +1000,20 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
isInLockTaskMode);
break;
}
+ case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: {
+ final ActivityRecord activity = ActivityRecord.forTokenLocked(hop.getContainer());
+ if (activity == null || activity.finishing) {
+ break;
+ }
+ if (activity.isVisible()) {
+ // Prevent the transition from being executed too early if the activity is
+ // visible.
+ activity.finishIfPossible("finish-activity-op", false /* oomAdj */);
+ } else {
+ activity.destroyIfPossible("finish-activity-op");
+ }
+ break;
+ }
case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"launchTask HierarchyOp");
@@ -1620,6 +1627,9 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub
organizer);
}
break;
+ case HIERARCHY_OP_TYPE_FINISH_ACTIVITY:
+ // Allow finish activity if it has the activity token.
+ break;
default:
// Other types of hierarchy changes are not allowed.
String msg = "Permission Denial: " + func + " from pid="
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index df7b8bb4ec9b..68fabc505761 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -2042,9 +2042,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* it must be drawn before allDrawn can become true.
*/
boolean isInteresting() {
+ final RecentsAnimationController recentsAnimationController =
+ mWmService.getRecentsAnimationController();
return mActivityRecord != null && !mAppDied
&& (!mActivityRecord.isFreezingScreen() || !mAppFreezing)
- && mViewVisibility == View.VISIBLE;
+ && mViewVisibility == View.VISIBLE
+ && (recentsAnimationController == null
+ || recentsAnimationController.isInterestingForAllDrawn(this));
}
/**
@@ -2622,11 +2626,19 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
}
+ // Check if window provides non decor insets before clearing its provided insets.
+ final boolean windowProvidesNonDecorInsets = providesNonDecorInsets();
+
removeImmediately();
// Removing a visible window may affect the display orientation so just update it if
// needed. Also recompute configuration if it provides screen decor insets.
- if ((wasVisible && displayContent.updateOrientation())
- || displayContent.getDisplayPolicy().updateDecorInsetsInfoIfNeeded(this)) {
+ boolean needToSendNewConfiguration = wasVisible && displayContent.updateOrientation();
+ if (windowProvidesNonDecorInsets) {
+ needToSendNewConfiguration |=
+ displayContent.getDisplayPolicy().updateDecorInsetsInfo();
+ }
+
+ if (needToSendNewConfiguration) {
displayContent.sendNewConfiguration();
}
mWmService.updateFocusedWindowLocked(isFocused()
@@ -3467,7 +3479,13 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
"Setting visibility of " + this + ": " + clientVisible);
mClient.dispatchAppVisibility(clientVisible);
} catch (RemoteException e) {
+ // The remote client fails to process the visibility message. That means it is in a
+ // wrong state. E.g. the binder buffer is running out or the binder threads are dead.
+ // The window visibility is out-of-sync that may cause blank content or left over, so
+ // just kill it. And if it is a window of foreground activity, the activity can be
+ // restarted automatically if needed.
Slog.w(TAG, "Exception thrown during dispatchAppVisibility " + this, e);
+ android.os.Process.killProcess(mSession.mPid);
}
}
@@ -3684,7 +3702,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
}
return win.showForAllUsers()
- || mWmService.isCurrentProfile(win.mShowUserId);
+ || mWmService.isUserVisible(win.mShowUserId);
}
private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
@@ -4562,7 +4580,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
float translateToWindowX(float x) {
float winX = x - mWindowFrames.mFrame.left;
if (mGlobalScale != 1f) {
- winX *= mGlobalScale;
+ winX *= mInvGlobalScale;
}
return winX;
}
@@ -4570,7 +4588,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
float translateToWindowY(float y) {
float winY = y - mWindowFrames.mFrame.top;
if (mGlobalScale != 1f) {
- winY *= mGlobalScale;
+ winY *= mInvGlobalScale;
}
return winY;
}
diff --git a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
index 261dda7ab15d..b93b8d866a00 100644
--- a/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
+++ b/services/core/java/com/android/server/wm/utils/RotationAnimationUtils.java
@@ -30,6 +30,7 @@ import android.media.ImageReader;
import android.view.Display;
import android.view.Surface;
import android.view.SurfaceControl;
+import android.window.ScreenCapture;
import java.nio.ByteBuffer;
import java.util.Arrays;
@@ -118,8 +119,8 @@ public class RotationAnimationUtils {
Point size = new Point();
display.getSize(size);
Rect crop = new Rect(0, 0, size.x, size.y);
- SurfaceControl.ScreenshotHardwareBuffer buffer =
- SurfaceControl.captureLayers(surfaceControl, crop, 1);
+ ScreenCapture.ScreenshotHardwareBuffer buffer =
+ ScreenCapture.captureLayers(surfaceControl, crop, 1);
if (buffer == null) {
return 0;
}
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index daca1531d41f..b7a4fd1be261 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -37,6 +37,7 @@ enum class DeviceType {
KEYBOARD,
MOUSE,
TOUCHSCREEN,
+ DPAD,
};
enum class UinputAction {
@@ -76,6 +77,13 @@ static std::map<int, int> TOOL_TYPE_MAPPING = {
{AMOTION_EVENT_TOOL_TYPE_PALM, MT_TOOL_PALM},
};
+// Dpad keycode mapping from https://source.android.com/devices/input/keyboard-devices
+static std::map<int, int> DPAD_KEY_CODE_MAPPING = {
+ {AKEYCODE_DPAD_DOWN, KEY_DOWN}, {AKEYCODE_DPAD_UP, KEY_UP},
+ {AKEYCODE_DPAD_LEFT, KEY_LEFT}, {AKEYCODE_DPAD_RIGHT, KEY_RIGHT},
+ {AKEYCODE_DPAD_CENTER, KEY_SELECT},
+};
+
// Keycode mapping from https://source.android.com/devices/input/keyboard-devices
static std::map<int, int> KEY_CODE_MAPPING = {
{AKEYCODE_0, KEY_0},
@@ -200,8 +208,13 @@ static int openUinput(const char* readableName, jint vendorId, jint productId, c
ioctl(fd, UI_SET_EVBIT, EV_KEY);
ioctl(fd, UI_SET_EVBIT, EV_SYN);
switch (deviceType) {
+ case DeviceType::DPAD:
+ for (const auto& [_, keyCode] : DPAD_KEY_CODE_MAPPING) {
+ ioctl(fd, UI_SET_KEYBIT, keyCode);
+ }
+ break;
case DeviceType::KEYBOARD:
- for (const auto& [ignored, keyCode] : KEY_CODE_MAPPING) {
+ for (const auto& [_, keyCode] : KEY_CODE_MAPPING) {
ioctl(fd, UI_SET_KEYBIT, keyCode);
}
break;
@@ -327,6 +340,12 @@ static int openUinputJni(JNIEnv* env, jstring name, jint vendorId, jint productI
screenHeight, screenWidth);
}
+static int nativeOpenUinputDpad(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
+ jint productId, jstring phys) {
+ return openUinputJni(env, name, vendorId, productId, phys, DeviceType::DPAD,
+ /* screenHeight */ 0, /* screenWidth */ 0);
+}
+
static int nativeOpenUinputKeyboard(JNIEnv* env, jobject thiz, jstring name, jint vendorId,
jint productId, jstring phys) {
return openUinputJni(env, name, vendorId, productId, phys, DeviceType::KEYBOARD,
@@ -355,10 +374,10 @@ static bool writeInputEvent(int fd, uint16_t type, uint16_t code, int32_t value)
return TEMP_FAILURE_RETRY(write(fd, &ev, sizeof(struct input_event))) == sizeof(ev);
}
-static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode,
- jint action) {
- auto keyCodeIterator = KEY_CODE_MAPPING.find(androidKeyCode);
- if (keyCodeIterator == KEY_CODE_MAPPING.end()) {
+static bool writeKeyEvent(jint fd, jint androidKeyCode, jint action,
+ const std::map<int, int>& keyCodeMapping) {
+ auto keyCodeIterator = keyCodeMapping.find(androidKeyCode);
+ if (keyCodeIterator == keyCodeMapping.end()) {
ALOGE("No supportive native keycode for androidKeyCode %d", androidKeyCode);
return false;
}
@@ -376,6 +395,16 @@ static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint android
return true;
}
+static bool nativeWriteDpadKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode,
+ jint action) {
+ return writeKeyEvent(fd, androidKeyCode, action, DPAD_KEY_CODE_MAPPING);
+}
+
+static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jint fd, jint androidKeyCode,
+ jint action) {
+ return writeKeyEvent(fd, androidKeyCode, action, KEY_CODE_MAPPING);
+}
+
static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jint fd, jint buttonCode,
jint action) {
auto buttonCodeIterator = BUTTON_CODE_MAPPING.find(buttonCode);
@@ -461,6 +490,8 @@ static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jint fd, jfloat xA
}
static JNINativeMethod methods[] = {
+ {"nativeOpenUinputDpad", "(Ljava/lang/String;IILjava/lang/String;)I",
+ (void*)nativeOpenUinputDpad},
{"nativeOpenUinputKeyboard", "(Ljava/lang/String;IILjava/lang/String;)I",
(void*)nativeOpenUinputKeyboard},
{"nativeOpenUinputMouse", "(Ljava/lang/String;IILjava/lang/String;)I",
@@ -468,6 +499,7 @@ static JNINativeMethod methods[] = {
{"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)I",
(void*)nativeOpenUinputTouchscreen},
{"nativeCloseUinput", "(I)Z", (void*)nativeCloseUinput},
+ {"nativeWriteDpadKeyEvent", "(III)Z", (void*)nativeWriteDpadKeyEvent},
{"nativeWriteKeyEvent", "(III)Z", (void*)nativeWriteKeyEvent},
{"nativeWriteButtonEvent", "(III)Z", (void*)nativeWriteButtonEvent},
{"nativeWriteTouchEvent", "(IIIIFFFF)Z", (void*)nativeWriteTouchEvent},
diff --git a/services/core/jni/com_android_server_display_DisplayControl.cpp b/services/core/jni/com_android_server_display_DisplayControl.cpp
index 61f2b1411a0b..02e5061a3ac6 100644
--- a/services/core/jni/com_android_server_display_DisplayControl.cpp
+++ b/services/core/jni/com_android_server_display_DisplayControl.cpp
@@ -17,6 +17,7 @@
#include <android_util_Binder.h>
#include <gui/SurfaceComposerClient.h>
#include <jni.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
#include <nativehelper/ScopedUtfChars.h>
namespace android {
@@ -33,6 +34,27 @@ static void nativeDestroyDisplay(JNIEnv* env, jclass clazz, jobject tokenObj) {
SurfaceComposerClient::destroyDisplay(token);
}
+static void nativeOverrideHdrTypes(JNIEnv* env, jclass clazz, jobject tokenObject,
+ jintArray jHdrTypes) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == nullptr || jHdrTypes == nullptr) return;
+
+ ScopedIntArrayRO hdrTypes(env, jHdrTypes);
+ size_t numHdrTypes = hdrTypes.size();
+
+ std::vector<ui::Hdr> hdrTypesVector;
+ hdrTypesVector.reserve(numHdrTypes);
+ for (int i = 0; i < numHdrTypes; i++) {
+ hdrTypesVector.push_back(static_cast<ui::Hdr>(hdrTypes[i]));
+ }
+
+ status_t error = SurfaceComposerClient::overrideHdrTypes(token, hdrTypesVector);
+ if (error != NO_ERROR) {
+ jniThrowExceptionFmt(env, "java/lang/SecurityException",
+ "ACCESS_SURFACE_FLINGER is missing");
+ }
+}
+
// ----------------------------------------------------------------------------
static const JNINativeMethod sDisplayMethods[] = {
@@ -41,6 +63,8 @@ static const JNINativeMethod sDisplayMethods[] = {
(void*)nativeCreateDisplay },
{"nativeDestroyDisplay", "(Landroid/os/IBinder;)V",
(void*)nativeDestroyDisplay },
+ {"nativeOverrideHdrTypes", "(Landroid/os/IBinder;[I)V",
+ (void*)nativeOverrideHdrTypes },
// clang-format on
};
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 78b4ce222dd3..3f380e7914d0 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -168,8 +168,9 @@ static struct {
jmethodID constructor;
jfieldID lightTypeInput;
jfieldID lightTypePlayerId;
+ jfieldID lightTypeKeyboardBacklight;
jfieldID lightCapabilityBrightness;
- jfieldID lightCapabilityRgb;
+ jfieldID lightCapabilityColorRgb;
} gLightClassInfo;
static struct {
@@ -2011,25 +2012,28 @@ static jobject nativeGetLights(JNIEnv* env, jobject nativeImplObj, jint deviceId
jint jTypeId =
env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeInput);
- jint jCapability = 0;
-
- if (lightInfo.type == InputDeviceLightType::MONO) {
- jCapability = env->GetStaticIntField(gLightClassInfo.clazz,
- gLightClassInfo.lightCapabilityBrightness);
- } else if (lightInfo.type == InputDeviceLightType::RGB ||
- lightInfo.type == InputDeviceLightType::MULTI_COLOR) {
- jCapability =
- env->GetStaticIntField(gLightClassInfo.clazz,
- gLightClassInfo.lightCapabilityBrightness) |
- env->GetStaticIntField(gLightClassInfo.clazz,
- gLightClassInfo.lightCapabilityRgb);
+ if (lightInfo.type == InputDeviceLightType::INPUT) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz, gLightClassInfo.lightTypeInput);
} else if (lightInfo.type == InputDeviceLightType::PLAYER_ID) {
jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
gLightClassInfo.lightTypePlayerId);
+ } else if (lightInfo.type == InputDeviceLightType::KEYBOARD_BACKLIGHT) {
+ jTypeId = env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightTypeKeyboardBacklight);
} else {
ALOGW("Unknown light type %d", lightInfo.type);
continue;
}
+
+ jint jCapability = 0;
+ if (lightInfo.capabilityFlags.test(InputDeviceLightCapability::BRIGHTNESS)) {
+ jCapability |= env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightCapabilityBrightness);
+ }
+ if (lightInfo.capabilityFlags.test(InputDeviceLightCapability::RGB)) {
+ jCapability |= env->GetStaticIntField(gLightClassInfo.clazz,
+ gLightClassInfo.lightCapabilityColorRgb);
+ }
ScopedLocalRef<jobject> lightObj(env,
env->NewObject(gLightClassInfo.clazz,
gLightClassInfo.constructor,
@@ -2596,10 +2600,12 @@ int register_android_server_InputManager(JNIEnv* env) {
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_INPUT", "I");
gLightClassInfo.lightTypePlayerId =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_PLAYER_ID", "I");
+ gLightClassInfo.lightTypeKeyboardBacklight =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_TYPE_KEYBOARD_BACKLIGHT", "I");
gLightClassInfo.lightCapabilityBrightness =
env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_BRIGHTNESS", "I");
- gLightClassInfo.lightCapabilityRgb =
- env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_RGB", "I");
+ gLightClassInfo.lightCapabilityColorRgb =
+ env->GetStaticFieldID(gLightClassInfo.clazz, "LIGHT_CAPABILITY_COLOR_RGB", "I");
// ArrayList
FIND_CLASS(gArrayListClassInfo.clazz, "java/util/ArrayList");
diff --git a/services/core/jni/gnss/Utils.cpp b/services/core/jni/gnss/Utils.cpp
index 8f32c47fcb5a..571534f5d16e 100644
--- a/services/core/jni/gnss/Utils.cpp
+++ b/services/core/jni/gnss/Utils.cpp
@@ -195,6 +195,8 @@ jobject translateGnssLocation(JNIEnv* env, const android::hardware::gnss::GnssLo
flags = static_cast<uint32_t>(location.elapsedRealtime.flags);
if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIMESTAMP_NS) {
SET(ElapsedRealtimeNanos, location.elapsedRealtime.timestampNs);
+ } else {
+ SET(ElapsedRealtimeNanos, android::elapsedRealtimeNano());
}
if (flags & android::hardware::gnss::ElapsedRealtime::HAS_TIME_UNCERTAINTY_NS) {
SET(ElapsedRealtimeUncertaintyNanos,
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 6b05d8f74a5e..267cff6652bb 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -97,6 +97,16 @@
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
+ <!-- Set of thresholds that dictate the change needed for screen brightness
+ adaptations while in idle mode -->
+ <xs:element type="thresholds" name="displayBrightnessChangeThresholdsIdle" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- Set of thresholds that dictate the change needed for ambient brightness
+ adaptations while in idle mode -->
+ <xs:element type="thresholds" name="ambientBrightnessChangeThresholdsIdle" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
@@ -319,13 +329,13 @@
<!-- Thresholds for brightness changes. -->
<xs:complexType name="thresholds">
<xs:sequence>
- <!-- Brightening thresholds. -->
+ <!-- Brightening thresholds for active screen brightness mode. -->
<xs:element name="brighteningThresholds" type="brightnessThresholds" minOccurs="0"
maxOccurs="1" >
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
- <!-- Darkening thresholds. -->
+ <!-- Darkening thresholds for active screen brightness mode. -->
<xs:element name="darkeningThresholds" type="brightnessThresholds" minOccurs="0"
maxOccurs="1" >
<xs:annotation name="nonnull"/>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index fb7a920f7d82..f8bff757f1ac 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -61,11 +61,13 @@ package com.android.server.display.config {
public class DisplayConfiguration {
ctor public DisplayConfiguration();
method @NonNull public final com.android.server.display.config.Thresholds getAmbientBrightnessChangeThresholds();
+ method public final com.android.server.display.config.Thresholds getAmbientBrightnessChangeThresholdsIdle();
method public final java.math.BigInteger getAmbientLightHorizonLong();
method public final java.math.BigInteger getAmbientLightHorizonShort();
method public com.android.server.display.config.AutoBrightness getAutoBrightness();
method @Nullable public final com.android.server.display.config.DensityMapping getDensityMapping();
method @NonNull public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholds();
+ method public final com.android.server.display.config.Thresholds getDisplayBrightnessChangeThresholdsIdle();
method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
method public final com.android.server.display.config.SensorDetails getLightSensor();
method public final com.android.server.display.config.SensorDetails getProxSensor();
@@ -80,11 +82,13 @@ package com.android.server.display.config {
method public final java.math.BigDecimal getScreenBrightnessRampSlowIncrease();
method @NonNull public final com.android.server.display.config.ThermalThrottling getThermalThrottling();
method public final void setAmbientBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
+ method public final void setAmbientBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
method public final void setAmbientLightHorizonLong(java.math.BigInteger);
method public final void setAmbientLightHorizonShort(java.math.BigInteger);
method public void setAutoBrightness(com.android.server.display.config.AutoBrightness);
method public final void setDensityMapping(@Nullable com.android.server.display.config.DensityMapping);
method public final void setDisplayBrightnessChangeThresholds(@NonNull com.android.server.display.config.Thresholds);
+ method public final void setDisplayBrightnessChangeThresholdsIdle(com.android.server.display.config.Thresholds);
method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
method public final void setLightSensor(com.android.server.display.config.SensorDetails);
method public final void setProxSensor(com.android.server.display.config.SensorDetails);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index bb6bd4aa1bbc..42dfee375ae4 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -5725,7 +5725,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
try {
return setKeyChainGrantInternal(alias, hasGrant, granteeUid, caller.getUserHandle());
} catch (IllegalArgumentException e) {
- if (mInjector.isChangeEnabled(THROW_EXCEPTION_WHEN_KEY_MISSING, packageName,
+ if (mInjector.isChangeEnabled(THROW_EXCEPTION_WHEN_KEY_MISSING, callerPackage,
caller.getUserId())) {
throw e;
}
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 6196c49c88ce..9c9b363b948e 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -1287,8 +1287,8 @@ int IncrementalService::addBindMount(IncFsMount& ifs, StorageId storage,
bp.set_allocated_dest_path(&target);
bp.set_allocated_source_subdir(&source);
const auto metadata = bp.SerializeAsString();
- bp.release_dest_path();
- bp.release_source_subdir();
+ static_cast<void>(bp.release_dest_path());
+ static_cast<void>(bp.release_source_subdir());
mdFileName = makeBindMdName();
metadataFullPath = path::join(ifs.root, constants().mount, mdFileName);
auto node = mIncFs->makeFile(ifs.control, metadataFullPath, 0444, idFromMetadata(metadata),
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index f921ccf62a3b..7652d6df133a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -373,8 +373,6 @@ public final class SystemServer implements Dumpable {
"com.android.server.searchui.SearchUiManagerService";
private static final String SMARTSPACE_MANAGER_SERVICE_CLASS =
"com.android.server.smartspace.SmartspaceManagerService";
- private static final String CLOUDSEARCH_MANAGER_SERVICE_CLASS =
- "com.android.server.cloudsearch.CloudSearchManagerService";
private static final String DEVICE_IDLE_CONTROLLER_CLASS =
"com.android.server.DeviceIdleController";
private static final String BLOB_STORE_MANAGER_SERVICE_CLASS =
@@ -1857,12 +1855,6 @@ public final class SystemServer implements Dumpable {
mSystemServiceManager.startService(SMARTSPACE_MANAGER_SERVICE_CLASS);
t.traceEnd();
- // CloudSearch manager service
- // TODO: add deviceHasConfigString(context, R.string.config_defaultCloudSearchServices)
- t.traceBegin("StartCloudSearchService");
- mSystemServiceManager.startService(CLOUDSEARCH_MANAGER_SERVICE_CLASS);
- t.traceEnd();
-
t.traceBegin("InitConnectivityModuleConnector");
try {
ConnectivityModuleConnector.getInstance().init(context);
diff --git a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
index 3c6866205fda..f05b1d47ac0b 100644
--- a/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
+++ b/services/profcollect/src/com/android/server/profcollect/ProfcollectForwardingService.java
@@ -218,6 +218,9 @@ public final class ProfcollectForwardingService extends SystemService {
BackgroundThread.get().getThreadHandler().post(
() -> {
try {
+ if (sSelfService.mIProfcollect == null) {
+ return;
+ }
sSelfService.mIProfcollect.process();
} catch (RemoteException e) {
Log.e(LOG_TAG, "Failed to process profiles in background: "
diff --git a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
index 525a9311a723..c8d153ad37a5 100644
--- a/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
+++ b/services/selectiontoolbar/java/com/android/server/selectiontoolbar/SelectionToolbarManagerServiceImpl.java
@@ -23,7 +23,6 @@ import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
-import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -35,6 +34,7 @@ import android.view.selectiontoolbar.ShowInfo;
import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService;
+import com.android.server.input.InputManagerInternal;
final class SelectionToolbarManagerServiceImpl extends
AbstractPerUserSystemService<SelectionToolbarManagerServiceImpl,
diff --git a/services/tests/PackageManagerServiceTests/unit/Android.bp b/services/tests/PackageManagerServiceTests/unit/Android.bp
index 1bcc3d1f70ad..1c6ba33bd279 100644
--- a/services/tests/PackageManagerServiceTests/unit/Android.bp
+++ b/services/tests/PackageManagerServiceTests/unit/Android.bp
@@ -35,6 +35,7 @@ android_test {
"kotlin-reflect",
"services.core",
"servicestests-utils",
+ "servicestests-core-utils",
"truth-prebuilt",
],
platform_apis: true,
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
index 5361041c4aff..1f66a1180aeb 100644
--- 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
@@ -17,13 +17,7 @@
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 com.android.server.pm.pkg.parsing.ParsingPackage
+import android.content.pm.*
import android.net.Uri
import android.os.Bundle
import android.os.Parcelable
@@ -33,18 +27,7 @@ import android.util.SparseIntArray
import com.android.internal.R
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.pkg.AndroidPackage
-import com.android.server.pm.pkg.component.ParsedActivityImpl
-import com.android.server.pm.pkg.component.ParsedApexSystemServiceImpl
-import com.android.server.pm.pkg.component.ParsedAttributionImpl
-import com.android.server.pm.pkg.component.ParsedComponentImpl
-import com.android.server.pm.pkg.component.ParsedInstrumentationImpl
-import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
-import com.android.server.pm.pkg.component.ParsedPermissionGroupImpl
-import com.android.server.pm.pkg.component.ParsedPermissionImpl
-import com.android.server.pm.pkg.component.ParsedProcessImpl
-import com.android.server.pm.pkg.component.ParsedProviderImpl
-import com.android.server.pm.pkg.component.ParsedServiceImpl
-import com.android.server.pm.pkg.component.ParsedUsesPermissionImpl
+import com.android.server.pm.pkg.component.*
import com.android.server.testutils.mockThrowOnUnmocked
import com.android.server.testutils.whenever
import java.security.KeyPairGenerator
@@ -65,6 +48,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
"addMimeGroupsFromComponent",
"assignDerivedFields",
"assignDerivedFields2",
+ "makeImmutable",
"buildFakeForDeletion",
"buildAppClassNamesByProcess",
"capPermissionPriorities",
@@ -103,6 +87,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
"getRequestedPermissions",
// Tested through asSplit
"asSplit",
+ "getSplits",
"getSplitNames",
"getSplitCodePaths",
"getSplitRevisionCodes",
@@ -175,9 +160,9 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
AndroidPackage::getSecondaryNativeLibraryDir,
AndroidPackage::getSharedUserId,
AndroidPackage::getSharedUserLabel,
- AndroidPackage::getSdkLibName,
+ AndroidPackage::getSdkLibraryName,
AndroidPackage::getSdkLibVersionMajor,
- AndroidPackage::getStaticSharedLibName,
+ AndroidPackage::getStaticSharedLibraryName,
AndroidPackage::getStaticSharedLibVersion,
AndroidPackage::getTargetSandboxVersion,
AndroidPackage::getTargetSdkVersion,
@@ -219,6 +204,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
AndroidPackage::isNativeLibraryRootRequiresIsa,
AndroidPackage::isOdm,
AndroidPackage::isOem,
+ AndroidPackage::isOnBackInvokedCallbackEnabled,
AndroidPackage::isOverlay,
AndroidPackage::isOverlayIsStatic,
AndroidPackage::isPartiallyDirectBootAware,
@@ -279,7 +265,7 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
adder(AndroidPackage::getUsesOptionalNativeLibraries, "testUsesOptionalNativeLibrary"),
getSetByValue(
AndroidPackage::areAttributionsUserVisible,
- ParsingPackage::setAttributionsAreUserVisible,
+ PackageImpl::setAttributionsAreUserVisible,
true
),
getSetByValue2(
@@ -513,11 +499,6 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
}
),
getter(AndroidPackage::getKnownActivityEmbeddingCerts, setOf("TESTEMBEDDINGCERT")),
- getSetByValue(
- AndroidPackage::isOnBackInvokedCallbackEnabled,
- ParsingPackage::setOnBackInvokedCallbackEnabled,
- true
- )
)
override fun initialObject() = PackageImpl.forParsing(
@@ -554,16 +535,20 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
SparseArray<IntArray>().apply {
put(0, intArrayOf(-1))
put(1, intArrayOf(0))
+ put(2, intArrayOf(1))
}
)
.setSplitHasCode(0, true)
.setSplitHasCode(1, false)
.setSplitClassLoaderName(0, "testSplitClassLoaderNameZero")
.setSplitClassLoaderName(1, "testSplitClassLoaderNameOne")
-
.addUsesSdkLibrary("testSdk", 2L, arrayOf("testCertDigest1"))
.addUsesStaticLibrary("testStatic", 3L, arrayOf("testCertDigest2"))
+ override fun finalizeObject(parcelable: Parcelable) {
+ (parcelable as PackageImpl).hideAsParsed().hideAsFinal()
+ }
+
override fun extraAssertions(before: Parcelable, after: Parcelable) {
super.extraAssertions(before, after)
after as PackageImpl
@@ -600,9 +585,10 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag
expect.that(after.splitDependencies).isNotNull()
after.splitDependencies?.let {
- expect.that(it.size()).isEqualTo(2)
+ expect.that(it.size()).isEqualTo(3)
expect.that(it.get(0)).asList().containsExactly(-1)
expect.that(it.get(1)).asList().containsExactly(0)
+ expect.that(it.get(2)).asList().containsExactly(1)
}
expect.that(after.usesSdkLibraries).containsExactly("testSdk")
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
index e16a1871f8b8..37bb9354df1c 100644
--- 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
@@ -293,12 +293,21 @@ abstract class ParcelableComponentTest(
first?.let { property(it) } == second?.let { property(it) }
}
- @Test
- fun valueComparison() {
+ fun buildBefore(): Pair<List<Param>, Parcelable> {
val params = baseParams.mapNotNull(::buildParams) + extraParams().filterNotNull()
val before = initialObject()
params.forEach { it.setFunction(arrayOf(before, it.value())) }
+ finalizeObject(before)
+ return params to before
+ }
+
+ protected open fun finalizeObject(parcelable: Parcelable) {
+ }
+
+ @Test
+ fun valueComparison() {
+ val (params, before) = buildBefore()
val parcel = Parcel.obtain()
writeToParcel(parcel, before)
@@ -307,6 +316,15 @@ abstract class ParcelableComponentTest(
parcel.setDataPosition(0)
+ val baseline = initialObject()
+ finalizeObject(baseline)
+
+ val baselineParcel = Parcel.obtain()
+ writeToParcel(baselineParcel, baseline)
+
+ // Check that something substantial actually changed in the test object
+ expect.that(parcel.dataSize()).isGreaterThan(baselineParcel.dataSize())
+
val after = creator.createFromParcel(parcel)
expect.withMessage("Mismatched write and read data sizes")
@@ -314,6 +332,7 @@ abstract class ParcelableComponentTest(
.isEqualTo(dataSize)
parcel.recycle()
+ baselineParcel.recycle()
runAssertions(params, before, after)
}
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
new file mode 100644
index 000000000000..8a855e51fc3d
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/pkg/PackageStateTest.kt
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.pkg
+
+import android.content.Intent
+import android.content.pm.overlay.OverlayPaths
+import android.content.pm.PackageManager
+import android.content.pm.PathPermission
+import android.content.pm.SharedLibraryInfo
+import android.content.pm.VersionedPackage
+import android.os.PatternMatcher
+import android.util.ArraySet
+import com.android.server.pm.PackageSetting
+import com.android.server.pm.PackageSettingBuilder
+import com.android.server.pm.parsing.pkg.PackageImpl
+import com.android.server.pm.pkg.AndroidPackage
+import com.android.server.pm.pkg.PackageState
+import com.android.server.pm.pkg.PackageStateImpl
+import com.android.server.pm.pkg.PackageUserState
+import com.android.server.pm.pkg.PackageUserStateImpl
+import com.android.server.pm.pkg.component.ParsedActivity
+import com.android.server.pm.pkg.component.ParsedActivityImpl
+import com.android.server.pm.pkg.component.ParsedComponentImpl
+import com.android.server.pm.pkg.component.ParsedInstrumentation
+import com.android.server.pm.pkg.component.ParsedIntentInfoImpl
+import com.android.server.pm.pkg.component.ParsedPermission
+import com.android.server.pm.pkg.component.ParsedPermissionGroup
+import com.android.server.pm.pkg.component.ParsedPermissionImpl
+import com.android.server.pm.pkg.component.ParsedProcess
+import com.android.server.pm.pkg.component.ParsedProcessImpl
+import com.android.server.pm.pkg.component.ParsedProvider
+import com.android.server.pm.pkg.component.ParsedProviderImpl
+import com.android.server.pm.pkg.component.ParsedService
+import com.android.server.pm.test.parsing.parcelling.AndroidPackageTest
+import com.google.common.truth.Expect
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TemporaryFolder
+import kotlin.contracts.ExperimentalContracts
+import kotlin.reflect.KClass
+import kotlin.reflect.KFunction
+import kotlin.reflect.KType
+import kotlin.reflect.full.isSubtypeOf
+import kotlin.reflect.full.memberFunctions
+import kotlin.reflect.full.starProjectedType
+
+class PackageStateTest {
+
+ companion object {
+ private val IGNORED_TYPES = listOf(
+ "java.io.File",
+ "java.lang.Boolean",
+ "java.lang.Byte",
+ "java.lang.CharSequence",
+ "java.lang.Character",
+ "java.lang.Double",
+ "java.lang.Float",
+ "java.lang.Integer",
+ "java.lang.Long",
+ "java.lang.Short",
+ "java.lang.String",
+ "java.lang.Void",
+ )
+ // STOPSHIP: Remove these and fix the implementations
+ private val IGNORED_FUNCTIONS = listOf(
+ ParsedActivity::getIntents,
+ ParsedActivity::getKnownActivityEmbeddingCerts,
+ ParsedActivity::getProperties,
+ ParsedInstrumentation::getIntents,
+ ParsedInstrumentation::getIntents,
+ ParsedInstrumentation::getProperties,
+ ParsedInstrumentation::getProperties,
+ ParsedPermission::getIntents,
+ ParsedPermission::getProperties,
+ ParsedPermissionGroup::getIntents,
+ ParsedPermissionGroup::getProperties,
+ ParsedProcess::getAppClassNamesByPackage,
+ ParsedProvider::getIntents,
+ ParsedProvider::getPathPermissions,
+ ParsedProvider::getProperties,
+ ParsedProvider::getUriPermissionPatterns,
+ ParsedService::getIntents,
+ ParsedService::getProperties,
+ Intent::getCategories,
+ PackageUserState::getDisabledComponents,
+ PackageUserState::getEnabledComponents,
+ PackageUserState::getSharedLibraryOverlayPaths,
+ OverlayPaths::getOverlayPaths,
+ OverlayPaths::getResourceDirs,
+ )
+ }
+
+ @get:Rule
+ val tempFolder = TemporaryFolder()
+
+ @get:Rule
+ val expect = Expect.create()
+
+ private val collectionType = MutableCollection::class.starProjectedType
+ private val mapType = Map::class.starProjectedType
+
+ @OptIn(ExperimentalContracts::class)
+ @Test
+ fun collectionImmutability() {
+ val seenTypes = mutableSetOf<KType>()
+ val (_, pkg) = AndroidPackageTest().buildBefore()
+ val packageState = PackageSettingBuilder()
+ .setPackage(pkg as AndroidPackage)
+ .setCodePath(tempFolder.newFile().path)
+ .build()
+
+ fillMissingData(packageState, pkg as PackageImpl)
+
+ visitType(seenTypes, emptyList(), PackageStateImpl.copy(packageState),
+ PackageState::class.starProjectedType)
+ visitType(seenTypes, emptyList(), pkg, AndroidPackage::class.starProjectedType)
+ visitType(seenTypes, emptyList(), packageState.getUserStateOrDefault(0),
+ PackageUserState::class.starProjectedType)
+
+ // Don't check empties for defaults since their collections will always be empty
+ visitType(seenTypes, emptyList(), PackageUserState.DEFAULT,
+ PackageUserState::class.starProjectedType, enforceNonEmpty = false)
+
+ // Check that some minimum number of functions were validated,
+ // in case the type checking breaks somehow
+ expect.that(seenTypes.size).isGreaterThan(10)
+ }
+
+ /**
+ * Fill fields in [PackageState] and its children that are not filled by [AndroidPackageTest].
+ * Real objects and real invocations of the live APIs are necessary to ensure that the test
+ * mirrors real device behavior.
+ */
+ private fun fillMissingData(pkgSetting: PackageSetting, pkg: PackageImpl) {
+ pkgSetting.addUsesLibraryFile("usesLibraryFile")
+
+ val sharedLibraryDependency = listOf(SharedLibraryInfo(
+ "pathDependency",
+ "packageNameDependency",
+ listOf(tempFolder.newFile().path),
+ "nameDependency",
+ 1,
+ 0,
+ VersionedPackage("versionedPackage0Dependency", 1),
+ listOf(VersionedPackage("versionedPackage1Dependency", 2)),
+ emptyList(),
+ false
+ ))
+
+ pkgSetting.addUsesLibraryInfo(SharedLibraryInfo(
+ "path",
+ "packageName",
+ listOf(tempFolder.newFile().path),
+ "name",
+ 1,
+ 0,
+ VersionedPackage("versionedPackage0", 1),
+ listOf(VersionedPackage("versionedPackage1", 2)),
+ sharedLibraryDependency,
+ false
+ ))
+ pkgSetting.addMimeTypes("mimeGroup", setOf("mimeType"))
+ pkgSetting.getOrCreateUserState(0).apply {
+ setEnabledComponents(ArraySet<String>().apply { add("com.test.EnabledComponent") })
+ setDisabledComponents(ArraySet<String>().apply { add("com.test.DisabledComponent") })
+ setSharedLibraryOverlayPaths("sharedLibrary",
+ OverlayPaths.Builder().addApkPath("/test/overlay.apk").build())
+ }
+
+ val property = PackageManager.Property("propertyName", 1, "com.test", null)
+ listOf(
+ pkg.activities,
+ pkg.receivers,
+ pkg.providers,
+ pkg.services,
+ pkg.instrumentations,
+ pkg.permissions,
+ pkg.permissionGroups
+ ).map { it.first() as ParsedComponentImpl }
+ .forEach {
+ it.addIntent(ParsedIntentInfoImpl())
+ it.addProperty(property)
+ }
+
+ (pkg.activities.first() as ParsedActivityImpl).knownActivityEmbeddingCerts =
+ setOf("TESTEMBEDDINGCERT")
+
+ (pkg.permissions.first() as ParsedPermissionImpl).knownCerts = setOf("TESTEMBEDDINGCERT")
+
+ (pkg.providers.first() as ParsedProviderImpl).apply {
+ addPathPermission(PathPermission("pattern", PatternMatcher.PATTERN_LITERAL,
+ "readPermission", "writerPermission"))
+ addUriPermissionPattern(PatternMatcher("*", PatternMatcher.PATTERN_LITERAL))
+ }
+
+ (pkg.processes.values.first() as ParsedProcessImpl).apply {
+ deniedPermissions = setOf("deniedPermission")
+ putAppClassNameForPackage("package", "className")
+ }
+ }
+
+ private fun visitType(
+ seenTypes: MutableSet<KType>,
+ parentChain: List<String>,
+ impl: Any,
+ type: KType,
+ enforceNonEmpty: Boolean = true
+ ) {
+ if (!seenTypes.add(type)) return
+ val kClass = type.classifier as KClass<*>
+ val qualifiedName = kClass.qualifiedName!!
+ if (IGNORED_TYPES.contains(qualifiedName)) return
+
+ val newChain = parentChain + kClass.simpleName!!
+ val newChainText = newChain.joinToString()
+
+ val filteredFunctions = kClass.memberFunctions
+ .filter {
+ // Size 1 because the impl receiver counts as a parameter
+ it.parameters.size == 1
+ }
+ .filterNot(IGNORED_FUNCTIONS::contains)
+
+ filteredFunctions.filter { it.returnType.isSubtypeOf(collectionType) }
+ .forEach {
+ val collection = it.call(impl)
+ if (collection as? MutableCollection<*> == null) {
+ expect.withMessage("Method $newChainText ${it.name} cannot return null")
+ .fail()
+ return@forEach
+ }
+
+ val value = try {
+ if (AndroidPackage::getSplits == it) {
+ // The base split is defined to never have any dependencies,
+ // so force the visitor to use the split at index 1 instead of 0.
+ collection.last()
+ } else {
+ collection.first()
+ }
+ } catch (e: Exception) {
+ if (enforceNonEmpty) {
+ expect.withMessage("Method $newChainText ${it.name} returns empty")
+ .that(e)
+ .isNull()
+ return@forEach
+ } else null
+ }
+
+ if (value != null) {
+ it.returnType.arguments.forEach {
+ visitType(seenTypes, newChain, value, it.type!!)
+ }
+ }
+
+ // Must test clear last in case it works and actually clears the collection
+ expectUnsupported(newChain, it) { collection.clear() }
+ }
+ filteredFunctions.filter { it.returnType.isSubtypeOf(mapType) }
+ .forEach {
+ val map = it.call(impl)
+ if (map as? MutableMap<*, *> == null) {
+ expect.withMessage("Method $newChainText ${it.name} cannot return null")
+ .fail()
+ return@forEach
+ }
+
+ val entry = try {
+ map.entries.stream().findFirst().get()!!
+ } catch (e: Exception) {
+ expect.withMessage("Method $newChainText ${it.name} returns empty")
+ .that(e)
+ .isNull()
+ return@forEach
+ }
+
+ visitType(seenTypes, newChain, entry.key!!, it.returnType.arguments[0].type!!)
+ visitType(seenTypes, newChain, entry.value!!, it.returnType.arguments[1].type!!)
+
+ // Must test clear last in case it works and actually clears the map
+ expectUnsupported(newChain, it) { map.clear() }
+ }
+ }
+
+ private fun expectUnsupported(
+ parentChain: List<String>,
+ function: KFunction<*>,
+ block: () -> Unit
+ ) {
+ val exception = try {
+ block()
+ null
+ } catch (e: UnsupportedOperationException) {
+ e
+ }
+
+ expect.withMessage("Method ${parentChain.joinToString()} $function doesn't throw")
+ .that(exception)
+ .isNotNull()
+ }
+} \ No newline at end of file
diff --git a/services/tests/mockingservicestests/AndroidManifest.xml b/services/tests/mockingservicestests/AndroidManifest.xml
index 07b763dcd85b..33ac73560968 100644
--- a/services/tests/mockingservicestests/AndroidManifest.xml
+++ b/services/tests/mockingservicestests/AndroidManifest.xml
@@ -34,10 +34,15 @@
<uses-permission android:name="android.permission.STATUS_BAR_SERVICE" />
<uses-permission android:name="android.permission.MANAGE_GAME_ACTIVITY" />
<uses-permission android:name="android.permission.SET_ALWAYS_FINISH" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" />
<!-- needed by MasterClearReceiverTest to display a system dialog -->
<uses-permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
+ <!-- needed by TrustManagerServiceTest to access LockSettings' secure storage -->
+ <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+
<application android:testOnly="true"
android:debuggable="true">
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java
new file mode 100644
index 000000000000..09df96f4b917
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceInjectorTest.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.view.Display.DEFAULT_DISPLAY;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.hardware.display.DisplayManager;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.Display;
+
+import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
+import com.android.server.ExtendedMockitoTestCase;
+import com.android.server.am.ActivityManagerService.Injector;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+
+import java.util.Arrays;
+
+/**
+ * Run as {@code atest
+ * FrameworksMockingServicesTests:com.android.server.am.ActivityManagerServiceInjectorTest}
+ */
+public final class ActivityManagerServiceInjectorTest extends ExtendedMockitoTestCase {
+
+ private static final String TAG = ActivityManagerServiceInjectorTest.class.getSimpleName();
+
+ private final Display mDefaultDisplay = validDisplay(DEFAULT_DISPLAY);
+
+ @Mock private Context mContext;
+ @Mock private DisplayManager mDisplayManager;
+
+ private Injector mInjector;
+
+ @Before
+ public void setFixture() {
+ mInjector = new Injector(mContext);
+
+ when(mContext.getSystemService(DisplayManager.class)).thenReturn(mDisplayManager);
+ }
+
+ @Override
+ protected void initializeSession(StaticMockitoSessionBuilder builder) {
+ builder.spyStatic(UserManager.class);
+ }
+
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_notSupported() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(false);
+
+ int [] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).isNull();
+ }
+
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_noDisplaysAtAll() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(true);
+ mockGetDisplays();
+
+ int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).isNull();
+ }
+
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_defaultDisplayOnly() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(true);
+ mockGetDisplays(mDefaultDisplay);
+
+ int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).isNull();
+ }
+
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_noDefaultDisplay() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(true);
+ mockGetDisplays(validDisplay(42));
+
+ int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).isNull();
+ }
+
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_mixed() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(true);
+ mockGetDisplays(mDefaultDisplay, validDisplay(42), invalidDisplay(108));
+
+ int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).isNotNull();
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).asList().containsExactly(42);
+ }
+
+ // Extra test to make sure the array is properly copied...
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers_mixed_invalidFirst() {
+ mockUmIsUsersOnSecondaryDisplaysEnabled(true);
+ mockGetDisplays(invalidDisplay(108), mDefaultDisplay, validDisplay(42));
+
+ int[] displayIds = mInjector.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).asList().containsExactly(42);
+ }
+
+ private Display validDisplay(int displayId) {
+ return mockDisplay(displayId, /* valid= */ true);
+ }
+
+ private Display invalidDisplay(int displayId) {
+ return mockDisplay(displayId, /* valid= */ false);
+ }
+
+ private Display mockDisplay(int displayId, boolean valid) {
+ Display display = mock(Display.class);
+
+ when(display.getDisplayId()).thenReturn(displayId);
+ when(display.isValid()).thenReturn(valid);
+
+ return display;
+ }
+
+ private void mockGetDisplays(Display... displays) {
+ Log.d(TAG, "mockGetDisplays(): " + Arrays.toString(displays));
+ when(mDisplayManager.getDisplays()).thenReturn(displays);
+ }
+
+ private void mockUmIsUsersOnSecondaryDisplaysEnabled(boolean enabled) {
+ Log.d(TAG, "Mocking UserManager.isUsersOnSecondaryDisplaysEnabled() to return " + enabled);
+ doReturn(enabled).when(() -> UserManager.isUsersOnSecondaryDisplaysEnabled());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
new file mode 100644
index 000000000000..e09b80e196b1
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.server.am.BroadcastProcessQueue.insertIntoRunnableList;
+import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
+import static com.android.server.am.BroadcastQueueTest.CLASS_GREEN;
+import static com.android.server.am.BroadcastQueueTest.PACKAGE_BLUE;
+import static com.android.server.am.BroadcastQueueTest.PACKAGE_GREEN;
+import static com.android.server.am.BroadcastQueueTest.PACKAGE_RED;
+import static com.android.server.am.BroadcastQueueTest.PACKAGE_YELLOW;
+import static com.android.server.am.BroadcastQueueTest.getUidForPackage;
+import static com.android.server.am.BroadcastQueueTest.makeManifestReceiver;
+
+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 static org.mockito.Mockito.doReturn;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.app.BroadcastOptions;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.HandlerThread;
+import android.os.UserHandle;
+import android.provider.Settings;
+
+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.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnitRunner;
+
+import java.util.List;
+
+@SmallTest
+@RunWith(MockitoJUnitRunner.class)
+public class BroadcastQueueModernImplTest {
+ private static final int TEST_UID = android.os.Process.FIRST_APPLICATION_UID;
+
+ @Mock ActivityManagerService mAms;
+
+ @Mock BroadcastProcessQueue mQueue1;
+ @Mock BroadcastProcessQueue mQueue2;
+ @Mock BroadcastProcessQueue mQueue3;
+ @Mock BroadcastProcessQueue mQueue4;
+
+ HandlerThread mHandlerThread;
+
+ BroadcastConstants mConstants;
+ BroadcastQueueModernImpl mImpl;
+
+ BroadcastProcessQueue mHead;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mHandlerThread = new HandlerThread(getClass().getSimpleName());
+ mHandlerThread.start();
+
+ mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
+ mImpl = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
+ mConstants, mConstants);
+
+ doReturn(1L).when(mQueue1).getRunnableAt();
+ doReturn(2L).when(mQueue2).getRunnableAt();
+ doReturn(3L).when(mQueue3).getRunnableAt();
+ doReturn(4L).when(mQueue4).getRunnableAt();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+ }
+
+ private static void assertOrphan(BroadcastProcessQueue queue) {
+ assertNull(queue.runnableAtNext);
+ assertNull(queue.runnableAtPrev);
+ }
+
+ private static void assertRunnableList(@NonNull List<BroadcastProcessQueue> expected,
+ @NonNull BroadcastProcessQueue actualHead) {
+ BroadcastProcessQueue test = actualHead;
+ final int N = expected.size();
+ for (int i = 0; i < N; i++) {
+ final BroadcastProcessQueue expectedPrev = (i > 0) ? expected.get(i - 1) : null;
+ final BroadcastProcessQueue expectedTest = expected.get(i);
+ final BroadcastProcessQueue expectedNext = (i < N - 1) ? expected.get(i + 1) : null;
+
+ assertEquals("prev", expectedPrev, test.runnableAtPrev);
+ assertEquals("test", expectedTest, test);
+ assertEquals("next", expectedNext, test.runnableAtNext);
+
+ test = test.runnableAtNext;
+ }
+ if (N == 0) {
+ assertNull(actualHead);
+ }
+ }
+
+ private BroadcastRecord makeBroadcastRecord(Intent intent) {
+ return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(),
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), false);
+ }
+
+ private BroadcastRecord makeOrderedBroadcastRecord(Intent intent) {
+ return makeBroadcastRecord(intent, BroadcastOptions.makeBasic(),
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), true);
+ }
+
+ private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options) {
+ return makeBroadcastRecord(intent, options,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), false);
+ }
+
+ private BroadcastRecord makeBroadcastRecord(Intent intent, BroadcastOptions options,
+ List receivers, boolean ordered) {
+ return new BroadcastRecord(mImpl, intent, null, PACKAGE_RED, null, 21, 42, false, null,
+ null, null, null, AppOpsManager.OP_NONE, options, receivers, null,
+ Activity.RESULT_OK, null, null, ordered, false, false, UserHandle.USER_SYSTEM,
+ false, null, false, null);
+ }
+
+ @Test
+ public void testRunnableList_Simple() {
+ assertRunnableList(List.of(), mHead);
+
+ mHead = insertIntoRunnableList(mHead, mQueue1);
+ assertRunnableList(List.of(mQueue1), mHead);
+
+ mHead = removeFromRunnableList(mHead, mQueue1);
+ assertRunnableList(List.of(), mHead);
+ }
+
+ @Test
+ public void testRunnableList_InsertLast() {
+ mHead = insertIntoRunnableList(mHead, mQueue1);
+ mHead = insertIntoRunnableList(mHead, mQueue2);
+ mHead = insertIntoRunnableList(mHead, mQueue3);
+ mHead = insertIntoRunnableList(mHead, mQueue4);
+ assertRunnableList(List.of(mQueue1, mQueue2, mQueue3, mQueue4), mHead);
+ }
+
+ @Test
+ public void testRunnableList_InsertFirst() {
+ mHead = insertIntoRunnableList(mHead, mQueue4);
+ mHead = insertIntoRunnableList(mHead, mQueue3);
+ mHead = insertIntoRunnableList(mHead, mQueue2);
+ mHead = insertIntoRunnableList(mHead, mQueue1);
+ assertRunnableList(List.of(mQueue1, mQueue2, mQueue3, mQueue4), mHead);
+ }
+
+ @Test
+ public void testRunnableList_InsertMiddle() {
+ mHead = insertIntoRunnableList(mHead, mQueue1);
+ mHead = insertIntoRunnableList(mHead, mQueue3);
+ mHead = insertIntoRunnableList(mHead, mQueue2);
+ assertRunnableList(List.of(mQueue1, mQueue2, mQueue3), mHead);
+ }
+
+ @Test
+ public void testRunnableList_Remove() {
+ mHead = insertIntoRunnableList(mHead, mQueue1);
+ mHead = insertIntoRunnableList(mHead, mQueue2);
+ mHead = insertIntoRunnableList(mHead, mQueue3);
+ mHead = insertIntoRunnableList(mHead, mQueue4);
+
+ mHead = removeFromRunnableList(mHead, mQueue3);
+ assertRunnableList(List.of(mQueue1, mQueue2, mQueue4), mHead);
+
+ mHead = removeFromRunnableList(mHead, mQueue1);
+ assertRunnableList(List.of(mQueue2, mQueue4), mHead);
+
+ mHead = removeFromRunnableList(mHead, mQueue4);
+ assertRunnableList(List.of(mQueue2), mHead);
+
+ mHead = removeFromRunnableList(mHead, mQueue2);
+ assertRunnableList(List.of(), mHead);
+
+ // Verify all links cleaned up during removal
+ assertOrphan(mQueue1);
+ assertOrphan(mQueue2);
+ assertOrphan(mQueue3);
+ assertOrphan(mQueue4);
+ }
+
+ @Test
+ public void testProcessQueue_Complex() {
+ BroadcastProcessQueue red = mImpl.getOrCreateProcessQueue(PACKAGE_RED, TEST_UID);
+ BroadcastProcessQueue green = mImpl.getOrCreateProcessQueue(PACKAGE_GREEN, TEST_UID);
+ BroadcastProcessQueue blue = mImpl.getOrCreateProcessQueue(PACKAGE_BLUE, TEST_UID);
+
+ assertEquals(PACKAGE_RED, red.processName);
+ assertEquals(PACKAGE_GREEN, green.processName);
+ assertEquals(PACKAGE_BLUE, blue.processName);
+
+ // Verify that removing middle queue works
+ mImpl.removeProcessQueue(PACKAGE_GREEN, TEST_UID);
+ assertEquals(red, mImpl.getProcessQueue(PACKAGE_RED, TEST_UID));
+ assertNull(mImpl.getProcessQueue(PACKAGE_GREEN, TEST_UID));
+ assertEquals(blue, mImpl.getProcessQueue(PACKAGE_BLUE, TEST_UID));
+ assertNull(mImpl.getProcessQueue(PACKAGE_YELLOW, TEST_UID));
+
+ // Verify that removing head queue works
+ mImpl.removeProcessQueue(PACKAGE_RED, TEST_UID);
+ assertNull(mImpl.getProcessQueue(PACKAGE_RED, TEST_UID));
+ assertNull(mImpl.getProcessQueue(PACKAGE_GREEN, TEST_UID));
+ assertEquals(blue, mImpl.getProcessQueue(PACKAGE_BLUE, TEST_UID));
+ assertNull(mImpl.getProcessQueue(PACKAGE_YELLOW, TEST_UID));
+
+ // Verify that removing last queue works
+ mImpl.removeProcessQueue(PACKAGE_BLUE, TEST_UID);
+ assertNull(mImpl.getProcessQueue(PACKAGE_RED, TEST_UID));
+ assertNull(mImpl.getProcessQueue(PACKAGE_GREEN, TEST_UID));
+ assertNull(mImpl.getProcessQueue(PACKAGE_BLUE, TEST_UID));
+ assertNull(mImpl.getProcessQueue(PACKAGE_YELLOW, TEST_UID));
+
+ // Verify that removing missing doesn't crash
+ mImpl.removeProcessQueue(PACKAGE_YELLOW, TEST_UID);
+
+ // Verify that we can start all over again safely
+ BroadcastProcessQueue yellow = mImpl.getOrCreateProcessQueue(PACKAGE_YELLOW, TEST_UID);
+ assertEquals(yellow, mImpl.getProcessQueue(PACKAGE_YELLOW, TEST_UID));
+ }
+
+ /**
+ * Empty queue isn't runnable.
+ */
+ @Test
+ public void testRunnableAt_Empty() {
+ BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
+ PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
+ assertFalse(queue.isRunnable());
+ assertEquals(Long.MAX_VALUE, queue.getRunnableAt());
+ }
+
+ /**
+ * Queue with a "normal" broadcast is runnable at different times depending
+ * on process cached state; when cached it's delayed by some amount.
+ */
+ @Test
+ public void testRunnableAt_Normal() {
+ BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
+ PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane);
+ queue.enqueueBroadcast(airplaneRecord, 0);
+
+ queue.setProcessCached(false);
+ final long notCachedRunnableAt = queue.getRunnableAt();
+ queue.setProcessCached(true);
+ final long cachedRunnableAt = queue.getRunnableAt();
+ assertTrue(cachedRunnableAt > notCachedRunnableAt);
+ }
+
+ /**
+ * Queue with foreground broadcast is always runnable immediately,
+ * regardless of process cached state.
+ */
+ @Test
+ public void testRunnableAt_Foreground() {
+ BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
+ PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ final BroadcastRecord airplaneRecord = makeBroadcastRecord(airplane);
+ queue.enqueueBroadcast(airplaneRecord, 0);
+
+ queue.setProcessCached(false);
+ assertTrue(queue.isRunnable());
+ assertEquals(airplaneRecord.enqueueTime, queue.getRunnableAt());
+
+ queue.setProcessCached(true);
+ assertTrue(queue.isRunnable());
+ assertEquals(airplaneRecord.enqueueTime, queue.getRunnableAt());
+ }
+
+ /**
+ * Verify that sending a broadcast that removes any matching pending
+ * broadcasts is applied as expected.
+ */
+ @Test
+ public void testRemoveMatchingFilter() {
+ final Intent screenOn = new Intent(Intent.ACTION_SCREEN_ON);
+ final BroadcastOptions optionsOn = BroadcastOptions.makeBasic();
+ optionsOn.setRemoveMatchingFilter(new IntentFilter(Intent.ACTION_SCREEN_OFF));
+
+ final Intent screenOff = new Intent(Intent.ACTION_SCREEN_OFF);
+ final BroadcastOptions optionsOff = BroadcastOptions.makeBasic();
+ optionsOff.setRemoveMatchingFilter(new IntentFilter(Intent.ACTION_SCREEN_ON));
+
+ // Halt all processing so that we get a consistent view
+ mHandlerThread.getLooper().getQueue().postSyncBarrier();
+
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, optionsOn));
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, optionsOff));
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOn, optionsOn));
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(screenOff, optionsOff));
+
+ // Marching through the queue we should only have one SCREEN_OFF
+ // broadcast, since that's the last state we dispatched
+ final BroadcastProcessQueue queue = mImpl.getProcessQueue(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ queue.makeActiveNextPending();
+ assertEquals(Intent.ACTION_SCREEN_OFF, queue.getActive().intent.getAction());
+ assertTrue(queue.isEmpty());
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 9bdc93e11b6a..89accf877f99 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -16,23 +16,36 @@
package com.android.server.am;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.Activity;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
+import android.app.RemoteServiceException.CannotDeliverBroadcastException;
+import android.app.usage.UsageEvents.Event;
+import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -40,13 +53,16 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.os.Binder;
+import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
-import android.os.SystemClock;
+import android.os.PowerExemptionManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -60,6 +76,7 @@ import com.android.server.am.ActivityManagerService.Injector;
import com.android.server.appop.AppOpsService;
import com.android.server.wm.ActivityTaskManagerService;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -67,14 +84,23 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.mockito.ArgumentMatcher;
+import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import org.mockito.verification.VerificationMode;
+import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.UnaryOperator;
/**
* Common tests for {@link BroadcastQueue} implementations.
@@ -92,7 +118,8 @@ public class BroadcastQueueTest {
private final Impl mImpl;
private enum Impl {
- DEFAULT
+ DEFAULT,
+ MODERN,
}
private Context mContext;
@@ -105,18 +132,31 @@ public class BroadcastQueueTest {
private ProcessList mProcessList;
@Mock
private PackageManagerInternal mPackageManagerInt;
+ @Mock
+ private UsageStatsManagerInternal mUsageStatsManagerInt;
private ActivityManagerService mAms;
private BroadcastQueue mQueue;
/**
+ * When enabled {@link ActivityManagerService#startProcessLocked} will fail
+ * by returning {@code null}; otherwise it will spawn a new mock process.
+ */
+ private boolean mFailStartProcess;
+
+ /**
* Map from PID to registered registered runtime receivers.
*/
private SparseArray<ReceiverList> mRegisteredReceivers = new SparseArray<>();
+ /**
+ * Collection of all active processes during current test run.
+ */
+ private List<ProcessRecord> mActiveProcesses = new ArrayList<>();
+
@Parameters(name = "impl={0}")
public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][] { {Impl.DEFAULT} });
+ return Arrays.asList(new Object[][] { {Impl.DEFAULT}, {Impl.MODERN} });
}
public BroadcastQueueTest(Impl impl) {
@@ -137,29 +177,54 @@ public class BroadcastQueueTest {
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
doNothing().when(mPackageManagerInt).setPackageStoppedState(any(), anyBoolean(), anyInt());
+ doAnswer((invocation) -> {
+ return getUidForPackage(invocation.getArgument(0));
+ }).when(mPackageManagerInt).getPackageUid(any(), anyLong(), eq(UserHandle.USER_SYSTEM));
final ActivityManagerService realAms = new ActivityManagerService(
new TestInjector(mContext), mServiceThreadRule.getThread());
realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
+ realAms.mOomAdjuster.mCachedAppOptimizer = spy(realAms.mOomAdjuster.mCachedAppOptimizer);
realAms.mPackageManagerInt = mPackageManagerInt;
+ realAms.mUsageStatsService = mUsageStatsManagerInt;
+ realAms.mProcessesReady = true;
mAms = spy(realAms);
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting startProcessLocked() for "
+ Arrays.toString(invocation.getArguments()));
+ if (mFailStartProcess) {
+ return null;
+ }
final String processName = invocation.getArgument(0);
final ApplicationInfo ai = invocation.getArgument(1);
- final ProcessRecord res = makeActiveProcessRecord(ai, processName);
+ final ProcessRecord res = makeActiveProcessRecord(ai, processName,
+ ProcessBehavior.NORMAL, UnaryOperator.identity());
mHandlerThread.getThreadHandler().post(() -> {
- mQueue.onApplicationAttachedLocked(res);
+ synchronized (mAms) {
+ mQueue.onApplicationAttachedLocked(res);
+ }
});
return res;
}).when(mAms).startProcessLocked(any(), any(), anyBoolean(), anyInt(),
any(), anyInt(), anyBoolean(), anyBoolean());
+ doAnswer((invocation) -> {
+ final String processName = invocation.getArgument(0);
+ final int uid = invocation.getArgument(1);
+ for (ProcessRecord r : mActiveProcesses) {
+ if (Objects.equals(r.processName, processName) && r.uid == uid) {
+ return r;
+ }
+ }
+ return null;
+ }).when(mAms).getProcessRecordLocked(any(), anyInt());
+ doNothing().when(mAms).appNotResponding(any(), any());
final BroadcastConstants constants = new BroadcastConstants(
Settings.Global.BROADCAST_FG_CONSTANTS);
+ constants.TIMEOUT = 100;
+ constants.ALLOW_BG_ACTIVITY_START_TIMEOUT = 0;
final BroadcastSkipPolicy emptySkipPolicy = new BroadcastSkipPolicy(mAms) {
public boolean shouldSkip(BroadcastRecord r, ResolveInfo info) {
return false;
@@ -177,11 +242,26 @@ public class BroadcastQueueTest {
mQueue = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(), TAG,
constants, emptySkipPolicy, emptyHistory, false,
ProcessList.SCHED_GROUP_DEFAULT);
+ } else if (mImpl == Impl.MODERN) {
+ mQueue = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
+ constants, constants, emptySkipPolicy, emptyHistory);
} else {
throw new UnsupportedOperationException();
}
}
+ @After
+ public void tearDown() throws Exception {
+ mHandlerThread.quit();
+
+ // Verify that all processes have finished handling broadcasts
+ for (ProcessRecord app : mActiveProcesses) {
+ assertTrue(app.toShortString(), app.mReceivers.numberOfCurReceivers() == 0);
+ assertTrue(app.toShortString(), mQueue.getPreferredSchedulingGroupLocked(app)
+ == ProcessList.SCHED_GROUP_UNDEFINED);
+ }
+ }
+
private class TestInjector extends Injector {
TestInjector(Context context) {
super(context);
@@ -203,21 +283,68 @@ public class BroadcastQueueTest {
}
}
+ /**
+ * Helper that leverages try-with-resources to pause dispatch of
+ * {@link #mHandlerThread} until released.
+ */
+ private class SyncBarrier implements AutoCloseable {
+ private final int mToken;
+
+ public SyncBarrier() {
+ mToken = mHandlerThread.getLooper().getQueue().postSyncBarrier();
+ }
+
+ @Override
+ public void close() throws Exception {
+ mHandlerThread.getLooper().getQueue().removeSyncBarrier(mToken);
+ }
+ }
+
+ private enum ProcessBehavior {
+ /** Process broadcasts normally */
+ NORMAL,
+ /** Wedge and never confirm broadcast receipt */
+ WEDGE,
+ /** Process broadcast by requesting abort */
+ ABORT,
+ /** Appear to behave completely dead */
+ DEAD,
+ }
+
private ProcessRecord makeActiveProcessRecord(String packageName) throws Exception {
final ApplicationInfo ai = makeApplicationInfo(packageName);
- return makeActiveProcessRecord(ai, ai.processName);
+ return makeActiveProcessRecord(ai, ai.processName, ProcessBehavior.NORMAL,
+ UnaryOperator.identity());
}
- private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai, String processName)
- throws Exception {
- final ProcessRecord r = new ProcessRecord(mAms, ai, processName, ai.uid);
+ private ProcessRecord makeActiveProcessRecord(String packageName,
+ ProcessBehavior behavior) throws Exception {
+ final ApplicationInfo ai = makeApplicationInfo(packageName);
+ return makeActiveProcessRecord(ai, ai.processName, behavior,
+ UnaryOperator.identity());
+ }
+
+ private ProcessRecord makeActiveProcessRecord(ApplicationInfo ai, String processName,
+ ProcessBehavior behavior, UnaryOperator<Bundle> extrasOperator) throws Exception {
+ final boolean wedge = (behavior == ProcessBehavior.WEDGE);
+ final boolean abort = (behavior == ProcessBehavior.ABORT);
+ final boolean dead = (behavior == ProcessBehavior.DEAD);
+
+ final ProcessRecord r = spy(new ProcessRecord(mAms, ai, processName, ai.uid));
r.setPid(mNextPid.getAndIncrement());
+ mActiveProcesses.add(r);
- final IApplicationThread thread = mock(IApplicationThread.class);
+ final IApplicationThread thread;
+ if (dead) {
+ thread = mock(IApplicationThread.class, (invocation) -> {
+ throw new DeadObjectException();
+ });
+ } else {
+ thread = mock(IApplicationThread.class);
+ }
final IBinder threadBinder = new Binder();
doReturn(threadBinder).when(thread).asBinder();
r.makeActive(thread, mAms.mProcessStats);
- doReturn(r).when(mAms).getProcessRecordLocked(eq(r.info.processName), eq(r.info.uid));
final IIntentReceiver receiver = mock(IIntentReceiver.class);
final IBinder receiverBinder = new Binder();
@@ -227,12 +354,31 @@ public class BroadcastQueueTest {
mRegisteredReceivers.put(r.getPid(), receiverList);
doAnswer((invocation) -> {
+ Log.v(TAG, "Intercepting killLocked() for "
+ + Arrays.toString(invocation.getArguments()));
+ mActiveProcesses.remove(r);
+ mRegisteredReceivers.remove(r.getPid());
+ return invocation.callRealMethod();
+ }).when(r).killLocked(any(), any(), anyInt(), anyInt(), anyBoolean());
+
+ // If we're entirely dead, rely on default behaviors above
+ if (dead) return r;
+
+ doAnswer((invocation) -> {
Log.v(TAG, "Intercepting scheduleReceiver() for "
+ Arrays.toString(invocation.getArguments()));
- mHandlerThread.getThreadHandler().post(() -> {
- mQueue.finishReceiverLocked(r, Activity.RESULT_OK,
- null, null, false, false);
- });
+ final Bundle extras = invocation.getArgument(5);
+ if (!wedge) {
+ assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
+ assertTrue(mQueue.getPreferredSchedulingGroupLocked(r)
+ != ProcessList.SCHED_GROUP_UNDEFINED);
+ mHandlerThread.getThreadHandler().post(() -> {
+ synchronized (mAms) {
+ mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null,
+ extrasOperator.apply(extras), abort, false);
+ }
+ });
+ }
return null;
}).when(thread).scheduleReceiver(any(), any(), any(), anyInt(), any(), any(), anyBoolean(),
anyInt(), anyInt());
@@ -240,10 +386,19 @@ public class BroadcastQueueTest {
doAnswer((invocation) -> {
Log.v(TAG, "Intercepting scheduleRegisteredReceiver() for "
+ Arrays.toString(invocation.getArguments()));
- mHandlerThread.getThreadHandler().post(() -> {
- mQueue.finishReceiverLocked(r, Activity.RESULT_OK, null, null,
- false, false);
- });
+ final Bundle extras = invocation.getArgument(4);
+ final boolean ordered = invocation.getArgument(5);
+ if (!wedge && ordered) {
+ assertTrue(r.mReceivers.numberOfCurReceivers() > 0);
+ assertTrue(mQueue.getPreferredSchedulingGroupLocked(r)
+ != ProcessList.SCHED_GROUP_UNDEFINED);
+ mHandlerThread.getThreadHandler().post(() -> {
+ synchronized (mAms) {
+ mQueue.finishReceiverLocked(r, Activity.RESULT_OK,
+ null, extrasOperator.apply(extras), abort, false);
+ }
+ });
+ }
return null;
}).when(thread).scheduleRegisteredReceiver(any(), any(), anyInt(), any(), any(),
anyBoolean(), anyBoolean(), anyInt(), anyInt());
@@ -251,7 +406,7 @@ public class BroadcastQueueTest {
return r;
}
- private ApplicationInfo makeApplicationInfo(String packageName) {
+ static ApplicationInfo makeApplicationInfo(String packageName) {
final ApplicationInfo ai = new ApplicationInfo();
ai.packageName = packageName;
ai.processName = packageName;
@@ -259,7 +414,7 @@ public class BroadcastQueueTest {
return ai;
}
- private ResolveInfo makeManifestReceiver(String packageName, String name) {
+ static ResolveInfo makeManifestReceiver(String packageName, String name) {
final ResolveInfo ri = new ResolveInfo();
ri.activityInfo = new ActivityInfo();
ri.activityInfo.packageName = packageName;
@@ -281,15 +436,35 @@ public class BroadcastQueueTest {
private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
List receivers) {
- return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(), receivers);
+ return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(),
+ receivers, false, null, null);
+ }
+
+ private BroadcastRecord makeOrderedBroadcastRecord(Intent intent, ProcessRecord callerApp,
+ List receivers, IIntentReceiver orderedResultTo, Bundle orderedExtras) {
+ return makeBroadcastRecord(intent, callerApp, BroadcastOptions.makeBasic(),
+ receivers, true, orderedResultTo, orderedExtras);
}
private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
BroadcastOptions options, List receivers) {
+ return makeBroadcastRecord(intent, callerApp, options, receivers, false, null, null);
+ }
+
+ private BroadcastRecord makeBroadcastRecord(Intent intent, ProcessRecord callerApp,
+ BroadcastOptions options, List receivers, boolean ordered,
+ IIntentReceiver orderedResultTo, Bundle orderedExtras) {
return new BroadcastRecord(mQueue, intent, callerApp, callerApp.info.packageName, null,
callerApp.getPid(), callerApp.info.uid, false, null, null, null, null,
- AppOpsManager.OP_NONE, options, receivers, null, Activity.RESULT_OK, null, null,
- false, false, false, UserHandle.USER_SYSTEM, false, null, false, null);
+ AppOpsManager.OP_NONE, options, receivers, orderedResultTo, Activity.RESULT_OK,
+ null, orderedExtras, ordered, false, false, UserHandle.USER_SYSTEM, false, null,
+ false, null);
+ }
+
+ private ArgumentMatcher<Intent> filterEquals(Intent intent) {
+ return (test) -> {
+ return intent.filterEquals(test);
+ };
}
private ArgumentMatcher<Intent> filterEqualsIgnoringComponent(Intent intent) {
@@ -302,6 +477,23 @@ public class BroadcastQueueTest {
};
}
+ private ArgumentMatcher<Bundle> bundleEquals(Bundle bundle) {
+ return (test) -> {
+ // TODO: check values in addition to keys
+ return Objects.equals(test.keySet(), bundle.keySet());
+ };
+ }
+
+ private @NonNull Bundle clone(@Nullable Bundle b) {
+ return (b != null) ? new Bundle(b) : new Bundle();
+ }
+
+ private void enqueueBroadcast(BroadcastRecord r) {
+ synchronized (mAms) {
+ mQueue.enqueueBroadcastLocked(r);
+ }
+ }
+
private void waitForIdle() throws Exception {
mQueue.waitForIdle(null);
}
@@ -312,6 +504,22 @@ public class BroadcastQueueTest {
any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
}
+ private void verifyScheduleReceiver(VerificationMode mode, ProcessRecord app, Intent intent)
+ throws Exception {
+ verify(app.getThread(), mode).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(intent)), any(), any(), anyInt(), any(),
+ any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+ }
+
+ private void verifyScheduleReceiver(VerificationMode mode, ProcessRecord app, Intent intent,
+ ComponentName component) throws Exception {
+ final Intent targetedIntent = new Intent(intent);
+ targetedIntent.setComponent(component);
+ verify(app.getThread(), mode).scheduleReceiver(
+ argThat(filterEquals(targetedIntent)), any(), any(), anyInt(), any(),
+ any(), eq(false), eq(UserHandle.USER_SYSTEM), anyInt());
+ }
+
private void verifyScheduleRegisteredReceiver(ProcessRecord app, Intent intent)
throws Exception {
verify(app.getThread()).scheduleRegisteredReceiver(any(),
@@ -319,17 +527,19 @@ public class BroadcastQueueTest {
anyBoolean(), anyBoolean(), eq(UserHandle.USER_SYSTEM), anyInt());
}
- private static final String PACKAGE_RED = "com.example.red";
- private static final String PACKAGE_GREEN = "com.example.green";
- private static final String PACKAGE_BLUE = "com.example.blue";
- private static final String PACKAGE_YELLOW = "com.example.yellow";
+ static final int USER_GUEST = 11;
+
+ static final String PACKAGE_RED = "com.example.red";
+ static final String PACKAGE_GREEN = "com.example.green";
+ static final String PACKAGE_BLUE = "com.example.blue";
+ static final String PACKAGE_YELLOW = "com.example.yellow";
- private static final String CLASS_RED = "com.example.red.Red";
- private static final String CLASS_GREEN = "com.example.green.Green";
- private static final String CLASS_BLUE = "com.example.blue.Blue";
- private static final String CLASS_YELLOW = "com.example.yellow.Yellow";
+ static final String CLASS_RED = "com.example.red.Red";
+ static final String CLASS_GREEN = "com.example.green.Green";
+ static final String CLASS_BLUE = "com.example.blue.Blue";
+ static final String CLASS_YELLOW = "com.example.yellow.Yellow";
- private static int getUidForPackage(String packageName) {
+ static int getUidForPackage(@NonNull String packageName) {
switch (packageName) {
case PACKAGE_RED: return android.os.Process.FIRST_APPLICATION_UID + 1;
case PACKAGE_GREEN: return android.os.Process.FIRST_APPLICATION_UID + 2;
@@ -339,6 +549,14 @@ public class BroadcastQueueTest {
}
}
+ @Test
+ public void testDump() throws Exception {
+ // To maximize test coverage, dump current state; we're not worried
+ // about the actual output, just that we don't crash
+ mQueue.dumpLocked(FileDescriptor.err, new PrintWriter(new ByteArrayOutputStream()),
+ null, 0, true, null, false);
+ }
+
/**
* Verify dispatch of simple broadcast to single manifest receiver in
* already-running warm app.
@@ -349,7 +567,7 @@ public class BroadcastQueueTest {
final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
final Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
- mQueue.enqueueBroadcastLocked(makeBroadcastRecord(intent, callerApp,
+ enqueueBroadcast(makeBroadcastRecord(intent, callerApp,
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
waitForIdle();
@@ -368,12 +586,12 @@ public class BroadcastQueueTest {
final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
- mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp,
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp,
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
waitForIdle();
@@ -394,12 +612,12 @@ public class BroadcastQueueTest {
// the second time it should already be running
final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
- mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp,
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp,
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
waitForIdle();
@@ -407,7 +625,6 @@ public class BroadcastQueueTest {
getUidForPackage(PACKAGE_GREEN));
final ProcessRecord receiverBlueApp = mAms.getProcessRecordLocked(PACKAGE_BLUE,
getUidForPackage(PACKAGE_BLUE));
- assertTrue(receiverBlueApp.getPid() > receiverGreenApp.getPid());
verifyScheduleReceiver(receiverGreenApp, timezone);
verifyScheduleReceiver(receiverGreenApp, airplane);
verifyScheduleReceiver(receiverBlueApp, timezone);
@@ -423,7 +640,7 @@ public class BroadcastQueueTest {
final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
final Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
- mQueue.enqueueBroadcastLocked(makeBroadcastRecord(intent, callerApp,
+ enqueueBroadcast(makeBroadcastRecord(intent, callerApp,
List.of(makeRegisteredReceiver(receiverApp))));
waitForIdle();
@@ -442,12 +659,12 @@ public class BroadcastQueueTest {
final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
- mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp,
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
List.of(makeRegisteredReceiver(receiverGreenApp),
makeRegisteredReceiver(receiverBlueApp))));
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp,
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
List.of(makeRegisteredReceiver(receiverBlueApp))));
waitForIdle();
@@ -468,14 +685,17 @@ public class BroadcastQueueTest {
final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
- mQueue.enqueueBroadcastLocked(makeBroadcastRecord(timezone, callerApp,
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
makeRegisteredReceiver(receiverGreenApp),
makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
makeRegisteredReceiver(receiverYellowApp))));
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- mQueue.enqueueBroadcastLocked(makeBroadcastRecord(airplane, callerApp,
+ airplane.setComponent(new ComponentName(PACKAGE_YELLOW, CLASS_YELLOW));
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.recordResponseEventWhileInBackground(42L);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, options,
List.of(makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
waitForIdle();
@@ -486,5 +706,444 @@ public class BroadcastQueueTest {
verifyScheduleReceiver(receiverBlueApp, timezone);
verifyScheduleRegisteredReceiver(receiverYellowApp, timezone);
verifyScheduleReceiver(receiverYellowApp, airplane);
+
+ for (ProcessRecord receiverApp : new ProcessRecord[] {
+ receiverGreenApp, receiverBlueApp, receiverYellowApp
+ }) {
+ // Confirm expected OOM adjustments; we were invoked once to upgrade
+ // and once to downgrade
+ assertEquals(ActivityManager.PROCESS_STATE_RECEIVER,
+ receiverApp.mState.getReportedProcState());
+ verify(mAms, times(2)).enqueueOomAdjTargetLocked(eq(receiverApp));
+
+ if ((mImpl == Impl.DEFAULT) && (receiverApp == receiverBlueApp)) {
+ // Nuance: the default implementation doesn't ask for manifest
+ // cold-started apps to be thawed, but the modern stack does
+ } else {
+ // Confirm that app was thawed
+ verify(mAms.mOomAdjuster.mCachedAppOptimizer).unfreezeTemporarily(eq(receiverApp),
+ eq(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER));
+
+ // Confirm that we added package to process
+ verify(receiverApp, atLeastOnce()).addPackage(eq(receiverApp.info.packageName),
+ anyLong(), any());
+ }
+
+ // Confirm that we've reported package as being used
+ verify(mAms, atLeastOnce()).notifyPackageUse(eq(receiverApp.info.packageName),
+ eq(PackageManager.NOTIFY_PACKAGE_USE_BROADCAST_RECEIVER));
+
+ // Confirm that we unstopped manifest receivers
+ verify(mAms.mPackageManagerInt, atLeastOnce()).setPackageStoppedState(
+ eq(receiverApp.info.packageName), eq(false), eq(UserHandle.USER_SYSTEM));
+ }
+
+ // Confirm that we've reported expected usage events
+ verify(mAms.mUsageStatsService).reportBroadcastDispatched(eq(callerApp.uid),
+ eq(PACKAGE_YELLOW), eq(UserHandle.SYSTEM), eq(42L), anyLong(), anyInt());
+ verify(mAms.mUsageStatsService).reportEvent(eq(PACKAGE_YELLOW), eq(UserHandle.USER_SYSTEM),
+ eq(Event.APP_COMPONENT_USED));
+ }
+
+ /**
+ * Verify that we detect and ANR a wedged process.
+ */
+ @Test
+ public void testWedged() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
+ ProcessBehavior.WEDGE);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+
+ waitForIdle();
+ verify(mAms).appNotResponding(eq(receiverApp), any());
+ }
+
+ /**
+ * Verify that we handle registered receivers in a process that always
+ * responds with {@link DeadObjectException}, recovering to restart the
+ * process and deliver their next broadcast.
+ */
+ @Test
+ public void testDead_Registered() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
+ ProcessBehavior.DEAD);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeRegisteredReceiver(receiverApp))));
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+ waitForIdle();
+
+ // First broadcast should have already been dead
+ verifyScheduleRegisteredReceiver(receiverApp, airplane);
+ verify(receiverApp).scheduleCrashLocked(any(),
+ eq(CannotDeliverBroadcastException.TYPE_ID), any());
+
+ // Second broadcast in new process should work fine
+ final ProcessRecord restartedReceiverApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ assertNotEquals(receiverApp, restartedReceiverApp);
+ verifyScheduleReceiver(restartedReceiverApp, timezone);
+ }
+
+ /**
+ * Verify that we handle manifest receivers in a process that always
+ * responds with {@link DeadObjectException}, recovering to restart the
+ * process and deliver their next broadcast.
+ */
+ @Test
+ public void testDead_Manifest() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN,
+ ProcessBehavior.DEAD);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+ waitForIdle();
+
+ // First broadcast should have already been dead
+ verifyScheduleReceiver(receiverApp, airplane);
+ verify(receiverApp).scheduleCrashLocked(any(),
+ eq(CannotDeliverBroadcastException.TYPE_ID), any());
+
+ // Second broadcast in new process should work fine
+ final ProcessRecord restartedReceiverApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ assertNotEquals(receiverApp, restartedReceiverApp);
+ verifyScheduleReceiver(restartedReceiverApp, timezone);
+ }
+
+ /**
+ * Verify that we handle the system failing to start a process.
+ */
+ @Test
+ public void testFailStartProcess() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+ // Send broadcast while process starts are failing
+ mFailStartProcess = true;
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+
+ // Confirm that queue goes idle, with no processes
+ waitForIdle();
+ assertEquals(1, mActiveProcesses.size());
+
+ // Send more broadcasts with working process starts
+ mFailStartProcess = false;
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW))));
+
+ // Confirm that we only saw second broadcast
+ waitForIdle();
+ assertEquals(3, mActiveProcesses.size());
+ final ProcessRecord receiverGreenApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ final ProcessRecord receiverYellowApp = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+ getUidForPackage(PACKAGE_YELLOW));
+ verifyScheduleReceiver(never(), receiverGreenApp, airplane);
+ verifyScheduleReceiver(never(), receiverYellowApp, airplane);
+ verifyScheduleReceiver(times(1), receiverGreenApp, timezone);
+ verifyScheduleReceiver(times(1), receiverYellowApp, timezone);
+ }
+
+ /**
+ * Verify that we cleanup a disabled component, skipping a pending dispatch
+ * of broadcast to that component.
+ */
+ @Test
+ public void testCleanup() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ try (SyncBarrier b = new SyncBarrier()) {
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_RED),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_BLUE)))));
+
+ synchronized (mAms) {
+ mQueue.cleanupDisabledPackageReceiversLocked(PACKAGE_GREEN, Set.of(CLASS_GREEN),
+ UserHandle.USER_SYSTEM);
+
+ // Also try clearing out other unrelated things that should leave
+ // the final receiver intact
+ mQueue.cleanupDisabledPackageReceiversLocked(PACKAGE_RED, null,
+ UserHandle.USER_SYSTEM);
+ mQueue.cleanupDisabledPackageReceiversLocked(null, null, USER_GUEST);
+ }
+
+ // To maximize test coverage, dump current state; we're not worried
+ // about the actual output, just that we don't crash
+ mQueue.dumpLocked(FileDescriptor.err, new PrintWriter(new ByteArrayOutputStream()),
+ null, 0, true, null, false);
+ }
+
+ waitForIdle();
+ verifyScheduleReceiver(times(1), receiverApp, airplane,
+ new ComponentName(PACKAGE_GREEN, CLASS_RED));
+ verifyScheduleReceiver(never(), receiverApp, airplane,
+ new ComponentName(PACKAGE_GREEN, CLASS_GREEN));
+ verifyScheduleReceiver(times(1), receiverApp, airplane,
+ new ComponentName(PACKAGE_GREEN, CLASS_BLUE));
+ }
+
+ /**
+ * Verify that killing a running process skips registered receivers.
+ */
+ @Test
+ public void testKill() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord oldApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ try (SyncBarrier b = new SyncBarrier()) {
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
+ List.of(makeRegisteredReceiver(oldApp),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)))));
+
+ synchronized (mAms) {
+ oldApp.killLocked(TAG, 42, false);
+ mQueue.onApplicationCleanupLocked(oldApp);
+ }
+ }
+ waitForIdle();
+
+ // Confirm that we cold-started after the kill
+ final ProcessRecord newApp = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN));
+ assertNotEquals(oldApp, newApp);
+
+ // Confirm that we saw no registered receiver traffic
+ final IApplicationThread oldThread = oldApp.getThread();
+ verify(oldThread, never()).scheduleRegisteredReceiver(any(),
+ any(), anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyInt(), anyInt());
+ final IApplicationThread newThread = newApp.getThread();
+ verify(newThread, never()).scheduleRegisteredReceiver(any(),
+ any(), anyInt(), any(), any(), anyBoolean(), anyBoolean(), anyInt(), anyInt());
+
+ // Confirm that we saw final manifest broadcast
+ verifyScheduleReceiver(times(1), newApp, airplane,
+ new ComponentName(PACKAGE_GREEN, CLASS_GREEN));
+ }
+
+ /**
+ * Verify that we skip broadcasts to an app being backed up.
+ */
+ @Test
+ public void testBackup() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
+ receiverApp.setInFullBackup(true);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+
+ waitForIdle();
+ verifyScheduleReceiver(never(), receiverApp, airplane,
+ new ComponentName(PACKAGE_GREEN, CLASS_GREEN));
+ }
+
+ /**
+ * Verify that an ordered broadcast collects results from everyone along the
+ * chain, and is delivered to final destination.
+ */
+ @Test
+ public void testOrdered() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+ // Purposefully warm-start the middle apps to make sure we dispatch to
+ // both cold and warm apps in expected order
+ makeActiveProcessRecord(makeApplicationInfo(PACKAGE_BLUE), PACKAGE_BLUE,
+ ProcessBehavior.NORMAL, (extras) -> {
+ extras = clone(extras);
+ extras.putBoolean(PACKAGE_BLUE, true);
+ return extras;
+ });
+ makeActiveProcessRecord(makeApplicationInfo(PACKAGE_YELLOW), PACKAGE_YELLOW,
+ ProcessBehavior.NORMAL, (extras) -> {
+ extras = clone(extras);
+ extras.putBoolean(PACKAGE_YELLOW, true);
+ return extras;
+ });
+
+ final IIntentReceiver orderedResultTo = mock(IIntentReceiver.class);
+ final Bundle orderedExtras = new Bundle();
+ orderedExtras.putBoolean(PACKAGE_RED, true);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeOrderedBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW)),
+ orderedResultTo, orderedExtras));
+
+ waitForIdle();
+ final IApplicationThread greenThread = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN)).getThread();
+ final IApplicationThread blueThread = mAms.getProcessRecordLocked(PACKAGE_BLUE,
+ getUidForPackage(PACKAGE_BLUE)).getThread();
+ final IApplicationThread yellowThread = mAms.getProcessRecordLocked(PACKAGE_YELLOW,
+ getUidForPackage(PACKAGE_YELLOW)).getThread();
+ final IApplicationThread redThread = mAms.getProcessRecordLocked(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED)).getThread();
+
+ // Verify that we called everyone in specific order, and that each of
+ // them observed the expected extras at that stage
+ final InOrder inOrder = inOrder(greenThread, blueThread, yellowThread, redThread);
+ final Bundle expectedExtras = new Bundle();
+ expectedExtras.putBoolean(PACKAGE_RED, true);
+ inOrder.verify(greenThread).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(airplane)), any(), any(),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)), eq(true),
+ eq(UserHandle.USER_SYSTEM), anyInt());
+ inOrder.verify(blueThread).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(airplane)), any(), any(),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)), eq(true),
+ eq(UserHandle.USER_SYSTEM), anyInt());
+ expectedExtras.putBoolean(PACKAGE_BLUE, true);
+ inOrder.verify(yellowThread).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(airplane)), any(), any(),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)), eq(true),
+ eq(UserHandle.USER_SYSTEM), anyInt());
+ expectedExtras.putBoolean(PACKAGE_YELLOW, true);
+ inOrder.verify(redThread).scheduleRegisteredReceiver(any(), argThat(filterEquals(airplane)),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)), eq(false),
+ anyBoolean(), eq(UserHandle.USER_SYSTEM), anyInt());
+
+ // Finally, verify that we thawed the final receiver
+ verify(mAms.mOomAdjuster.mCachedAppOptimizer).unfreezeTemporarily(eq(callerApp),
+ eq(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER));
+ }
+
+ /**
+ * Verify that an ordered broadcast can be aborted partially through
+ * dispatch, and is then delivered to final destination.
+ */
+ @Test
+ public void testOrdered_Aborting() throws Exception {
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ doOrdered_Aborting(airplane);
+ }
+
+ /**
+ * Verify that an ordered broadcast marked with
+ * {@link Intent#FLAG_RECEIVER_NO_ABORT} cannot be aborted partially through
+ * dispatch, and is delivered to everyone in order.
+ */
+ @Test
+ public void testOrdered_Aborting_NoAbort() throws Exception {
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ airplane.addFlags(Intent.FLAG_RECEIVER_NO_ABORT);
+ doOrdered_Aborting(airplane);
+ }
+
+ public void doOrdered_Aborting(@NonNull Intent intent) throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+
+ // Create a process that aborts any ordered broadcasts
+ makeActiveProcessRecord(makeApplicationInfo(PACKAGE_GREEN), PACKAGE_GREEN,
+ ProcessBehavior.ABORT, (extras) -> {
+ extras = clone(extras);
+ extras.putBoolean(PACKAGE_GREEN, true);
+ return extras;
+ });
+ makeActiveProcessRecord(PACKAGE_BLUE);
+
+ final IIntentReceiver orderedResultTo = mock(IIntentReceiver.class);
+
+ enqueueBroadcast(makeOrderedBroadcastRecord(intent, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE)),
+ orderedResultTo, null));
+
+ waitForIdle();
+ final IApplicationThread greenThread = mAms.getProcessRecordLocked(PACKAGE_GREEN,
+ getUidForPackage(PACKAGE_GREEN)).getThread();
+ final IApplicationThread blueThread = mAms.getProcessRecordLocked(PACKAGE_BLUE,
+ getUidForPackage(PACKAGE_BLUE)).getThread();
+ final IApplicationThread redThread = mAms.getProcessRecordLocked(PACKAGE_RED,
+ getUidForPackage(PACKAGE_RED)).getThread();
+
+ final Bundle expectedExtras = new Bundle();
+ expectedExtras.putBoolean(PACKAGE_GREEN, true);
+
+ // Verify that we always invoke the first receiver, but then we might
+ // have invoked or skipped the second receiver depending on the intent
+ // flag policy; we always deliver to final receiver regardless of abort
+ final InOrder inOrder = inOrder(greenThread, blueThread, redThread);
+ inOrder.verify(greenThread).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(intent)), any(), any(),
+ eq(Activity.RESULT_OK), any(), any(), eq(true), eq(UserHandle.USER_SYSTEM),
+ anyInt());
+ if ((intent.getFlags() & Intent.FLAG_RECEIVER_NO_ABORT) != 0) {
+ inOrder.verify(blueThread).scheduleReceiver(
+ argThat(filterEqualsIgnoringComponent(intent)), any(), any(),
+ eq(Activity.RESULT_OK), any(), any(), eq(true), eq(UserHandle.USER_SYSTEM),
+ anyInt());
+ } else {
+ inOrder.verify(blueThread, never()).scheduleReceiver(any(), any(), any(), anyInt(),
+ any(), any(), anyBoolean(), anyInt(), anyInt());
+ }
+ inOrder.verify(redThread).scheduleRegisteredReceiver(any(), argThat(filterEquals(intent)),
+ eq(Activity.RESULT_OK), any(), argThat(bundleEquals(expectedExtras)),
+ eq(false), anyBoolean(), eq(UserHandle.USER_SYSTEM), anyInt());
+ }
+
+ @Test
+ public void testBackgroundActivityStarts() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+ final Binder backgroundActivityStartsToken = new Binder();
+ final Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ final BroadcastRecord r = new BroadcastRecord(mQueue, intent, callerApp,
+ callerApp.info.packageName, null, callerApp.getPid(), callerApp.info.uid, false,
+ null, null, null, null, AppOpsManager.OP_NONE, BroadcastOptions.makeBasic(),
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)), null, Activity.RESULT_OK,
+ null, null, false, false, false, UserHandle.USER_SYSTEM, true,
+ backgroundActivityStartsToken, false, null);
+ enqueueBroadcast(r);
+
+ waitForIdle();
+ verify(receiverApp).addOrUpdateAllowBackgroundActivityStartsToken(eq(r),
+ eq(backgroundActivityStartsToken));
+ verify(receiverApp).removeAllowBackgroundActivityStartsToken(eq(r));
+ }
+
+ @Test
+ public void testOptions_TemporaryAppAllowlist() throws Exception {
+ final ProcessRecord callerApp = makeActiveProcessRecord(PACKAGE_RED);
+ final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
+
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ final BroadcastOptions options = BroadcastOptions.makeBasic();
+ options.setTemporaryAppAllowlist(1_000,
+ PowerExemptionManager.TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED,
+ PowerExemptionManager.REASON_VPN, TAG);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, options,
+ List.of(makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN))));
+
+ waitForIdle();
+ verify(mAms).tempAllowlistUidLocked(eq(receiverApp.uid), eq(1_000L),
+ eq(options.getTemporaryAppAllowlistReasonCode()), any(),
+ eq(options.getTemporaryAppAllowlistType()), eq(callerApp.uid));
}
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
new file mode 100644
index 000000000000..5dc12510368c
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsLegacyRestrictionsTest.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import static android.app.AppOpsManager.OP_COARSE_LOCATION;
+import static android.app.AppOpsManager.OP_FINE_LOCATION;
+
+import static org.junit.Assert.assertEquals;
+
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.quality.Strictness;
+
+public class AppOpsLegacyRestrictionsTest {
+ private static final int UID_ANY = -2;
+
+ final Object mClientToken = new Object();
+ final int mUserId1 = 65001;
+ final int mUserId2 = 65002;
+ final int mOpCode1 = OP_COARSE_LOCATION;
+ final int mOpCode2 = OP_FINE_LOCATION;
+ final String mPackageName = "com.example.test";
+ final String mAttributionTag = "test-attribution-tag";
+
+ StaticMockitoSession mSession;
+
+ @Mock
+ AppOpsService.Constants mConstants;
+
+ @Mock
+ Context mContext;
+
+ @Mock
+ Handler mHandler;
+
+ @Mock
+ AppOpsServiceInterface mLegacyAppOpsService;
+
+ AppOpsRestrictions mAppOpsRestrictions;
+
+ @Before
+ public void setUp() {
+ mSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+ mConstants.TOP_STATE_SETTLE_TIME = 10 * 1000L;
+ mConstants.FG_SERVICE_STATE_SETTLE_TIME = 5 * 1000L;
+ mConstants.BG_STATE_SETTLE_TIME = 1 * 1000L;
+ Mockito.when(mHandler.post(Mockito.any(Runnable.class))).then(inv -> {
+ Runnable r = inv.getArgument(0);
+ r.run();
+ return true;
+ });
+ mAppOpsRestrictions = new AppOpsRestrictionsImpl(mContext, mHandler, mLegacyAppOpsService);
+ }
+
+ @After
+ public void tearDown() {
+ mSession.finishMocking();
+ }
+
+ @Test
+ public void testSetAndGetSingleGlobalRestriction() {
+ // Verify: empty
+ assertEquals(false, mAppOpsRestrictions.hasGlobalRestrictions(mClientToken));
+ assertEquals(false, mAppOpsRestrictions.getGlobalRestriction(mClientToken, mOpCode1));
+ // Act: add a restriction
+ assertEquals(true, mAppOpsRestrictions.setGlobalRestriction(mClientToken, mOpCode1, true));
+ // Act: add same restriction again (expect false; should be no-op)
+ assertEquals(false, mAppOpsRestrictions.setGlobalRestriction(mClientToken, mOpCode1, true));
+ // Verify: not empty
+ assertEquals(true, mAppOpsRestrictions.hasGlobalRestrictions(mClientToken));
+ assertEquals(true, mAppOpsRestrictions.getGlobalRestriction(mClientToken, mOpCode1));
+ // Act: remove the restriction
+ assertEquals(true, mAppOpsRestrictions.setGlobalRestriction(mClientToken, mOpCode1, false));
+ // Act: remove same restriction again (expect false; should be no-op)
+ assertEquals(false,
+ mAppOpsRestrictions.setGlobalRestriction(mClientToken, mOpCode1, false));
+ // Verify: empty
+ assertEquals(false, mAppOpsRestrictions.hasGlobalRestrictions(mClientToken));
+ assertEquals(false, mAppOpsRestrictions.getGlobalRestriction(mClientToken, mOpCode1));
+ }
+
+ @Test
+ public void testSetAndGetDoubleGlobalRestriction() {
+ // Act: add opCode1 restriction
+ assertEquals(true, mAppOpsRestrictions.setGlobalRestriction(mClientToken, mOpCode1, true));
+ // Act: add opCode2 restriction
+ assertEquals(true, mAppOpsRestrictions.setGlobalRestriction(mClientToken, mOpCode2, true));
+ // Verify: not empty
+ assertEquals(true, mAppOpsRestrictions.hasGlobalRestrictions(mClientToken));
+ // Act: remove opCode1 restriction
+ assertEquals(true, mAppOpsRestrictions.setGlobalRestriction(mClientToken, mOpCode1, false));
+ // Verify: not empty
+ assertEquals(true, mAppOpsRestrictions.hasGlobalRestrictions(mClientToken));
+ // Act: remove opCode2 restriction
+ assertEquals(true, mAppOpsRestrictions.setGlobalRestriction(mClientToken, mOpCode2, false));
+ // Verify: empty
+ assertEquals(false, mAppOpsRestrictions.hasGlobalRestrictions(mClientToken));
+ }
+
+ @Test
+ public void testClearGlobalRestrictions() {
+ // Act: clear (should be no-op)
+ assertEquals(false, mAppOpsRestrictions.clearGlobalRestrictions(mClientToken));
+ // Act: add opCodes
+ assertEquals(true, mAppOpsRestrictions.setGlobalRestriction(mClientToken, mOpCode1, true));
+ assertEquals(true, mAppOpsRestrictions.setGlobalRestriction(mClientToken, mOpCode2, true));
+ // Verify: not empty
+ assertEquals(true, mAppOpsRestrictions.hasGlobalRestrictions(mClientToken));
+ // Act: clear
+ assertEquals(true, mAppOpsRestrictions.clearGlobalRestrictions(mClientToken));
+ // Verify: empty
+ assertEquals(false, mAppOpsRestrictions.hasGlobalRestrictions(mClientToken));
+ // Act: clear (should be no-op)
+ assertEquals(false, mAppOpsRestrictions.clearGlobalRestrictions(mClientToken));
+ }
+
+ @Test
+ public void testSetAndGetSingleUserRestriction() {
+ // Verify: empty
+ assertEquals(false, mAppOpsRestrictions.hasUserRestrictions(mClientToken));
+ assertEquals(false, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode1, mPackageName, mAttributionTag, false));
+ assertEquals(false, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode1, mPackageName, mAttributionTag, true));
+ // Act: add a restriction
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode1, true, null));
+ // Act: add the restriction again (should be no-op)
+ assertEquals(false, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode1, true, null));
+ // Verify: not empty
+ assertEquals(true, mAppOpsRestrictions.hasUserRestrictions(mClientToken));
+ assertEquals(true, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode1, mPackageName, mAttributionTag, false));
+ assertEquals(true, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode1, mPackageName, mAttributionTag, true));
+ // Act: remove the restriction
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode1, false, null));
+ // Act: remove the restriction again (should be no-op)
+ assertEquals(false, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode1, false, null));
+ // Verify: empty
+ assertEquals(false, mAppOpsRestrictions.hasUserRestrictions(mClientToken));
+ assertEquals(false, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode1, mPackageName, mAttributionTag, false));
+ }
+
+ @Test
+ public void testSetAndGetDoubleUserRestriction() {
+ // Act: add opCode1 restriction
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode1, true, null));
+ // Act: add opCode2 restriction
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode2, true, null));
+ // Verify: not empty
+ assertEquals(true, mAppOpsRestrictions.hasUserRestrictions(mClientToken));
+ assertEquals(true, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode1, mPackageName, mAttributionTag, false));
+ assertEquals(true, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode1, mPackageName, mAttributionTag, true));
+ assertEquals(true, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode2, mPackageName, mAttributionTag, false));
+ assertEquals(true, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode2, mPackageName, mAttributionTag, true));
+ // Act: remove opCode1 restriction
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode1, false, null));
+ // Verify: opCode1 is removed but not opCode22
+ assertEquals(true, mAppOpsRestrictions.hasUserRestrictions(mClientToken));
+ assertEquals(false, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode1, mPackageName, mAttributionTag, false));
+ assertEquals(false, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode1, mPackageName, mAttributionTag, true));
+ assertEquals(true, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode2, mPackageName, mAttributionTag, false));
+ assertEquals(true, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode2, mPackageName, mAttributionTag, true));
+ // Act: remove opCode2 restriction
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode2, false, null));
+ // Verify: empty
+ assertEquals(false, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode2, mPackageName, mAttributionTag, false));
+ assertEquals(false, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode2, mPackageName, mAttributionTag, true));
+ assertEquals(false, mAppOpsRestrictions.hasUserRestrictions(mClientToken));
+ }
+
+ @Test
+ public void testClearUserRestrictionsAllUsers() {
+ // Act: clear (should be no-op)
+ assertEquals(false, mAppOpsRestrictions.clearUserRestrictions(mClientToken));
+ // Act: add restrictions
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode1, true, null));
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode2, true, null));
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId2, mOpCode1, true, null));
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId2, mOpCode2, true, null));
+ // Verify: not empty
+ assertEquals(true, mAppOpsRestrictions.hasUserRestrictions(mClientToken));
+ // Act: clear all user restrictions
+ assertEquals(true, mAppOpsRestrictions.clearUserRestrictions(mClientToken));
+ // Verify: empty
+ assertEquals(false, mAppOpsRestrictions.hasUserRestrictions(mClientToken));
+ }
+
+ @Test
+ public void testClearUserRestrictionsSpecificUsers() {
+ // Act: clear (should be no-op)
+ assertEquals(false, mAppOpsRestrictions.clearUserRestrictions(mClientToken, mUserId1));
+ // Act: add restrictions
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode1, true, null));
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId1, mOpCode2, true, null));
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId2, mOpCode1, true, null));
+ assertEquals(true, mAppOpsRestrictions.setUserRestriction(
+ mClientToken, mUserId2, mOpCode2, true, null));
+ // Verify: not empty
+ assertEquals(true, mAppOpsRestrictions.hasUserRestrictions(mClientToken));
+ // Act: clear userId1
+ assertEquals(true, mAppOpsRestrictions.clearUserRestrictions(mClientToken, mUserId1));
+ // Act: clear userId1 again (should be no-op)
+ assertEquals(false, mAppOpsRestrictions.clearUserRestrictions(mClientToken, mUserId1));
+ // Verify: userId1 is removed but not userId2
+ assertEquals(false, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId1, mOpCode1, mPackageName, mAttributionTag, false));
+ assertEquals(true, mAppOpsRestrictions.getUserRestriction(
+ mClientToken, mUserId2, mOpCode2, mPackageName, mAttributionTag, false));
+ // Act: clear userId2
+ assertEquals(true, mAppOpsRestrictions.clearUserRestrictions(mClientToken, mUserId2));
+ // Act: clear userId2 again (should be no-op)
+ assertEquals(false, mAppOpsRestrictions.clearUserRestrictions(mClientToken, mUserId2));
+ // Verify: empty
+ assertEquals(false, mAppOpsRestrictions.hasUserRestrictions(mClientToken));
+ }
+
+ @Test
+ public void testNotify() {
+ mAppOpsRestrictions.setUserRestriction(mClientToken, mUserId1, mOpCode1, true, null);
+ mAppOpsRestrictions.clearUserRestrictions(mClientToken);
+ Mockito.verify(mLegacyAppOpsService, Mockito.times(1))
+ .notifyWatchersOfChange(mOpCode1, UID_ANY);
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
index 9acc5b9f2214..c0688d131610 100644
--- a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsServiceTest.java
@@ -15,10 +15,8 @@
*/
package com.android.server.appop;
-import static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
import static android.app.AppOpsManager.MODE_ALLOWED;
import static android.app.AppOpsManager.MODE_ERRORED;
-import static android.app.AppOpsManager.MODE_FOREGROUND;
import static android.app.AppOpsManager.OP_COARSE_LOCATION;
import static android.app.AppOpsManager.OP_FLAGS_ALL;
import static android.app.AppOpsManager.OP_READ_SMS;
@@ -41,7 +39,6 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
-import android.app.ActivityManager;
import android.app.AppOpsManager.OpEntry;
import android.app.AppOpsManager.PackageOps;
import android.content.ContentResolver;
@@ -335,124 +332,6 @@ public class AppOpsServiceTest {
assertThat(getLoggedOps()).isNull();
}
- private void setupProcStateTests() {
- // For the location proc state tests
- mAppOpsService.setMode(OP_COARSE_LOCATION, mMyUid, sMyPackageName, MODE_FOREGROUND);
- mAppOpsService.mConstants.FG_SERVICE_STATE_SETTLE_TIME = 0;
- mAppOpsService.mConstants.TOP_STATE_SETTLE_TIME = 0;
- mAppOpsService.mConstants.BG_STATE_SETTLE_TIME = 0;
- }
-
- @Test
- public void testUidProcStateChange_cachedToTopToCached() throws Exception {
- setupProcStateTests();
-
- mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isNotEqualTo(MODE_ALLOWED);
-
- mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP,
- ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isEqualTo(MODE_ALLOWED);
-
- mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- // Second time to make sure that settle time is overcome
- Thread.sleep(50);
- mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isNotEqualTo(MODE_ALLOWED);
- }
-
- @Test
- public void testUidProcStateChange_cachedToFgs() {
- setupProcStateTests();
- mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isNotEqualTo(MODE_ALLOWED);
-
- mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isNotEqualTo(MODE_ALLOWED);
- }
-
- @Test
- public void testUidProcStateChange_cachedToFgsLocation() {
- setupProcStateTests();
-
- mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isNotEqualTo(MODE_ALLOWED);
-
- mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isEqualTo(MODE_ALLOWED);
- }
-
- @Test
- public void testUidProcStateChange_topToFgs() throws Exception {
- setupProcStateTests();
-
- mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isNotEqualTo(MODE_ALLOWED);
-
- mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP,
- ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isEqualTo(MODE_ALLOWED);
-
- mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- // Second time to make sure that settle time is overcome
- Thread.sleep(50);
- mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isNotEqualTo(MODE_ALLOWED);
- }
-
- @Test
- public void testUidProcStateChange_topToFgsLocationToFgs() throws Exception {
- setupProcStateTests();
-
- mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_CACHED_EMPTY,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isNotEqualTo(MODE_ALLOWED);
-
- mAppOpsService.updateUidProcState(mMyUid, ActivityManager.PROCESS_STATE_TOP,
- ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isEqualTo(MODE_ALLOWED);
-
- mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
- // Second time to make sure that settle time is overcome
- Thread.sleep(50);
- mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isEqualTo(MODE_ALLOWED);
-
- mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- // Second time to make sure that settle time is overcome
- Thread.sleep(50);
- mAppOpsService.updateUidProcState(mMyUid, PROCESS_STATE_FOREGROUND_SERVICE,
- ActivityManager.PROCESS_CAPABILITY_NONE);
- assertThat(mAppOpsService.noteOperation(OP_COARSE_LOCATION, mMyUid, sMyPackageName, null,
- false, null, false).getOpMode()).isNotEqualTo(MODE_ALLOWED);
- }
-
private List<PackageOps> getLoggedOps() {
return mAppOpsService.getOpsForPackage(mMyUid, sMyPackageName, null /* all ops */);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
new file mode 100644
index 000000000000..e1713b0beb77
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/appop/AppOpsUidStateTrackerTest.java
@@ -0,0 +1,917 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_COARSE_LOCATION;
+import static android.app.AppOpsManager.OP_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_WIFI_SCAN;
+import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
+import static android.app.AppOpsManager.UID_STATE_CACHED;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
+import static android.app.AppOpsManager.UID_STATE_MAX_LAST_NON_RESTRICTED;
+import static android.app.AppOpsManager.UID_STATE_TOP;
+
+import static com.android.server.appop.AppOpsUidStateTracker.processStateToUidState;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
+import android.app.AppOpsManager;
+import android.util.SparseArray;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.dx.mockito.inline.extended.StaticMockitoSession;
+import com.android.internal.os.Clock;
+import com.android.server.appop.AppOpsUidStateTracker.UidStateChangedCallback;
+import com.android.server.appop.AppOpsUidStateTrackerImpl.DelayableExecutor;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.quality.Strictness;
+
+import java.util.PriorityQueue;
+
+public class AppOpsUidStateTrackerTest {
+
+ private static final int UID = 10001;
+
+ // An op code that's not location/cam/mic so that we can test the code for evaluating mode
+ // without a specific capability associated with it.
+ public static final int OP_NO_CAPABILITIES = OP_WIFI_SCAN;
+
+ @Mock
+ ActivityManagerInternal mAmi;
+
+ @Mock
+ AppOpsService.Constants mConstants;
+
+ AppOpsUidStateTrackerTestExecutor mExecutor = new AppOpsUidStateTrackerTestExecutor();
+
+ AppOpsUidStateTrackerTestClock mClock = new AppOpsUidStateTrackerTestClock(mExecutor);
+
+ AppOpsUidStateTracker mIntf;
+
+ StaticMockitoSession mSession;
+
+ @Before
+ public void setUp() {
+ mSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.LENIENT)
+ .startMocking();
+ mConstants.TOP_STATE_SETTLE_TIME = 10 * 1000L;
+ mConstants.FG_SERVICE_STATE_SETTLE_TIME = 5 * 1000L;
+ mConstants.BG_STATE_SETTLE_TIME = 1 * 1000L;
+ mIntf = new AppOpsUidStateTrackerImpl(mAmi, mExecutor, mClock, mConstants,
+ Thread.currentThread());
+ }
+
+ @After
+ public void tearDown() {
+ mSession.finishMocking();
+ }
+
+ /**
+ * This class makes the assumption that all ops are restricted at the same state, this is likely
+ * to be the case forever or become obsolete with the capability mechanism. If this fails
+ * something in {@link AppOpsUidStateTrackerImpl} might break when reporting if foreground mode
+ * might change.
+ */
+ @Test
+ public void testConstantFirstUnrestrictedUidState() {
+ for (int i = 0; i < AppOpsManager.getNumOps(); i++) {
+ assertEquals(UID_STATE_MAX_LAST_NON_RESTRICTED,
+ AppOpsManager.resolveFirstUnrestrictedUidState(i));
+ }
+ }
+
+ @Test
+ public void testNoCapability() {
+ procStateBuilder(UID)
+ .topState()
+ .update();
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testForegroundWithMicrophoneCapability() {
+ procStateBuilder(UID)
+ .topState()
+ .microphoneCapability()
+ .update();
+
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testBackgroundWithMicrophoneCapability() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .microphoneCapability()
+ .update();
+
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testForegroundWithCameraCapability() {
+ procStateBuilder(UID)
+ .topState()
+ .cameraCapability()
+ .update();
+
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testBackgroundWithCameraCapability() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .cameraCapability()
+ .update();
+
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testForegroundWithLocationCapability() {
+ procStateBuilder(UID)
+ .topState()
+ .locationCapability()
+ .update();
+
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testBackgroundWithLocationCapability() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .locationCapability()
+ .update();
+
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testForegroundNotCapabilitiesTracked() {
+ procStateBuilder(UID)
+ .topState()
+ .update();
+
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_NO_CAPABILITIES, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testBackgroundNotCapabilitiesTracked() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_NO_CAPABILITIES, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testBackgroundToForegroundTransition() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+ assertBackground(UID);
+
+ procStateBuilder(UID)
+ .topState()
+ .update();
+ assertForeground(UID);
+ }
+
+ @Test
+ public void testForegroundToBackgroundTransition() {
+ procStateBuilder(UID)
+ .topState()
+ .update();
+ assertForeground(UID);
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+ // Still in foreground due to settle time
+ assertForeground(UID);
+
+ mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME - 1);
+ assertForeground(UID);
+
+ mClock.advanceTime(1);
+ assertBackground(UID);
+ }
+
+ @Test
+ public void testForegroundServiceToBackgroundTransition() {
+ procStateBuilder(UID)
+ .foregroundServiceState()
+ .update();
+ assertForeground(UID);
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+ // Still in foreground due to settle time
+ assertForeground(UID);
+
+ mClock.advanceTime(mConstants.FG_SERVICE_STATE_SETTLE_TIME - 1);
+ assertForeground(UID);
+
+ mClock.advanceTime(1);
+ assertBackground(UID);
+ }
+
+ @Test
+ public void testEarlyUpdateDoesntCommit() {
+ procStateBuilder(UID)
+ .foregroundServiceState()
+ .update();
+ assertForeground(UID);
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+ // Still in foreground due to settle time
+ assertForeground(UID);
+
+ // 1 ms short of settle time
+ mClock.advanceTime(mConstants.FG_SERVICE_STATE_SETTLE_TIME - 1);
+ assertForeground(UID);
+ }
+
+ @Test
+ public void testMicrophoneCapabilityAdded() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .microphoneCapability()
+ .update();
+
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testMicrophoneCapabilityRemoved() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .microphoneCapability()
+ .update();
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testCameraCapabilityAdded() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .cameraCapability()
+ .update();
+
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testCameraCapabilityRemoved() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .cameraCapability()
+ .update();
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testLocationCapabilityAdded() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .locationCapability()
+ .update();
+
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testLocationCapabilityRemoved() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .locationCapability()
+ .update();
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_IGNORED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testVisibleAppWidget() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ SparseArray<String> appPackageNames = new SparseArray<>();
+ appPackageNames.put(UID, "");
+ mIntf.updateAppWidgetVisibility(appPackageNames, true);
+
+ assertForeground(UID);
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testPendingTop() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ doReturn(true).when(mAmi).isPendingTopUid(eq(UID));
+
+ assertForeground(UID);
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testTempAllowlist() {
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ doReturn(true).when(mAmi).isTempAllowlistedForFgsWhileInUse(eq(UID));
+
+ assertForeground(UID);
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_RECORD_AUDIO, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_CAMERA, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_COARSE_LOCATION, MODE_FOREGROUND));
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(UID, OP_FINE_LOCATION, MODE_FOREGROUND));
+ }
+
+ @Test
+ public void testUidStateChangedCallbackNewProcessTop() {
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .topState()
+ .update();
+
+ verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_TOP), eq(true));
+ }
+
+ @Test
+ public void testUidStateChangedCallbackNewProcessForegroundService() {
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .foregroundServiceState()
+ .update();
+
+ verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_FOREGROUND_SERVICE), eq(true));
+ }
+
+ @Test
+ public void testUidStateChangedCallbackNewProcessForeground() {
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .foregroundState()
+ .update();
+
+ verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_FOREGROUND), eq(true));
+ }
+
+ @Test
+ public void testUidStateChangedCallbackNewProcessBackground() {
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ verify(cb).onUidStateChanged(eq(UID), eq(UID_STATE_BACKGROUND), eq(false));
+ }
+
+ @Test
+ public void testUidStateChangedCallbackNewProcessCached() {
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .cachedState()
+ .update();
+
+ // Cached is the default, no change in uid state.
+ verify(cb, times(0)).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testUidStateChangedCallbackCachedToBackground() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
+ ActivityManager.PROCESS_STATE_RECEIVER);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackCachedToForeground() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
+ ActivityManager.PROCESS_STATE_BOUND_TOP);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackCachedToForegroundService() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackCachedToTop() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_CACHED_ACTIVITY,
+ ActivityManager.PROCESS_STATE_TOP);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackBackgroundToCached() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_RECEIVER,
+ ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackBackgroundToForeground() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_RECEIVER,
+ ActivityManager.PROCESS_STATE_BOUND_TOP);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackBackgroundToForegroundService() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_RECEIVER,
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackBackgroundToTop() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_RECEIVER,
+ ActivityManager.PROCESS_STATE_TOP);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackForegroundToCached() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_BOUND_TOP,
+ ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackForegroundToBackground() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_BOUND_TOP,
+ ActivityManager.PROCESS_STATE_RECEIVER);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackForegroundToForegroundService() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_BOUND_TOP,
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackForegroundToTop() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_BOUND_TOP,
+ ActivityManager.PROCESS_STATE_TOP);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackForegroundServiceToCached() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
+ ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackForegroundServiceToBackground() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
+ ActivityManager.PROCESS_STATE_RECEIVER);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackForegroundServiceToForeground() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
+ ActivityManager.PROCESS_STATE_BOUND_TOP);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackForegroundServiceToTop() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE,
+ ActivityManager.PROCESS_STATE_TOP);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackTopToCached() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_TOP,
+ ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackTopToBackground() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_TOP,
+ ActivityManager.PROCESS_STATE_RECEIVER);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackTopToForeground() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_TOP,
+ ActivityManager.PROCESS_STATE_BOUND_TOP);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackTopToForegroundService() {
+ testUidStateChangedCallback(
+ ActivityManager.PROCESS_STATE_TOP,
+ ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE);
+ }
+
+ @Test
+ public void testUidStateChangedCallbackCachedToNonexistent() {
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .cachedState()
+ .update();
+
+ procStateBuilder(UID)
+ .nonExistentState()
+ .update();
+
+ verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void testUidStateChangedCallbackBackgroundToNonexistent() {
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ procStateBuilder(UID)
+ .nonExistentState()
+ .update();
+
+ verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(false));
+ }
+
+ @Test
+ public void testUidStateChangedCallbackForegroundToNonexistent() {
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .foregroundState()
+ .update();
+
+ procStateBuilder(UID)
+ .nonExistentState()
+ .update();
+
+ verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
+ }
+
+ @Test
+ public void testUidStateChangedCallbackForegroundServiceToNonexistent() {
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .foregroundServiceState()
+ .update();
+
+ procStateBuilder(UID)
+ .nonExistentState()
+ .update();
+
+ verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
+ }
+
+ @Test
+ public void testUidStateChangedCallbackTopToNonexistent() {
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .topState()
+ .update();
+
+ procStateBuilder(UID)
+ .nonExistentState()
+ .update();
+
+ verify(cb, atLeastOnce()).onUidStateChanged(eq(UID), eq(UID_STATE_CACHED), eq(true));
+ }
+
+ @Test
+ public void testUidStateChangedBackgroundThenForegroundImmediately() {
+ procStateBuilder(UID)
+ .topState()
+ .update();
+
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .backgroundState()
+ .update();
+
+ mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME - 1);
+
+ procStateBuilder(UID)
+ .topState()
+ .update();
+
+ mClock.advanceTime(1);
+
+ verify(cb, never()).onUidStateChanged(anyInt(), anyInt(), anyBoolean());
+ }
+
+ public void testUidStateChangedCallback(int initialState, int finalState) {
+ int initialUidState = processStateToUidState(initialState);
+ int finalUidState = processStateToUidState(finalState);
+ boolean foregroundChange = initialUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED
+ != finalUidState <= UID_STATE_MAX_LAST_NON_RESTRICTED;
+ boolean finalUidStateIsBackgroundAndLessImportant =
+ finalUidState > UID_STATE_MAX_LAST_NON_RESTRICTED
+ && finalUidState > initialUidState;
+
+ UidStateChangedCallback cb = addUidStateChangeCallback();
+
+ procStateBuilder(UID)
+ .setState(initialState)
+ .update();
+
+ procStateBuilder(UID)
+ .setState(finalState)
+ .update();
+
+ if (finalUidStateIsBackgroundAndLessImportant) {
+ mClock.advanceTime(mConstants.TOP_STATE_SETTLE_TIME + 1);
+ }
+
+ verify(cb, atLeastOnce())
+ .onUidStateChanged(eq(UID), eq(finalUidState), eq(foregroundChange));
+ }
+
+ private UidStateChangedCallback addUidStateChangeCallback() {
+ UidStateChangedCallback cb =
+ Mockito.mock(UidStateChangedCallback.class);
+ mIntf.addUidStateChangedCallback(r -> r.run(), cb);
+ return cb;
+ }
+
+ /* If testForegroundNotCapabilitiesTracked fails, this assertion is probably incorrect */
+ private void assertForeground(int uid) {
+ assertEquals(MODE_ALLOWED, mIntf.evalMode(uid, OP_NO_CAPABILITIES, MODE_FOREGROUND));
+ }
+
+ /* If testBackgroundNotCapabilitiesTracked fails, this assertion is probably incorrect */
+ private void assertBackground(int uid) {
+ assertEquals(MODE_IGNORED, mIntf.evalMode(uid, OP_NO_CAPABILITIES, MODE_FOREGROUND));
+ }
+
+ private UidProcStateUpdateBuilder procStateBuilder(int uid) {
+ return new UidProcStateUpdateBuilder(mIntf, uid);
+ }
+
+ private static class UidProcStateUpdateBuilder {
+ private AppOpsUidStateTracker mIntf;
+ private int mUid;
+ private int mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ private int mCapability = 0;
+
+ private UidProcStateUpdateBuilder(AppOpsUidStateTracker intf, int uid) {
+ mUid = uid;
+ mIntf = intf;
+ }
+
+ public void update() {
+ mIntf.updateUidProcState(mUid, mProcState, mCapability);
+ }
+
+ public UidProcStateUpdateBuilder persistentState() {
+ mProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
+ return this;
+ }
+
+ public UidProcStateUpdateBuilder setState(int procState) {
+ mProcState = procState;
+ return this;
+ }
+
+ public UidProcStateUpdateBuilder topState() {
+ mProcState = ActivityManager.PROCESS_STATE_TOP;
+ return this;
+ }
+
+ public UidProcStateUpdateBuilder foregroundServiceState() {
+ mProcState = ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
+ return this;
+ }
+
+ public UidProcStateUpdateBuilder foregroundState() {
+ mProcState = ActivityManager.PROCESS_STATE_BOUND_TOP;
+ return this;
+ }
+
+ public UidProcStateUpdateBuilder backgroundState() {
+ mProcState = ActivityManager.PROCESS_STATE_SERVICE;
+ return this;
+ }
+
+ public UidProcStateUpdateBuilder cachedState() {
+ mProcState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+ return this;
+ }
+
+ public UidProcStateUpdateBuilder nonExistentState() {
+ mProcState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ return this;
+ }
+
+ public UidProcStateUpdateBuilder locationCapability() {
+ mCapability |= ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
+ return this;
+ }
+
+ public UidProcStateUpdateBuilder cameraCapability() {
+ mCapability |= ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
+ return this;
+ }
+
+ public UidProcStateUpdateBuilder microphoneCapability() {
+ mCapability |= ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+ return this;
+ }
+ }
+
+ private static class AppOpsUidStateTrackerTestClock extends Clock {
+
+ private AppOpsUidStateTrackerTestExecutor mExecutor;
+ long mElapsedRealTime = 0x5f3759df;
+
+ AppOpsUidStateTrackerTestClock(AppOpsUidStateTrackerTestExecutor executor) {
+ mExecutor = executor;
+ executor.setUptime(mElapsedRealTime);
+ }
+
+ @Override
+ public long elapsedRealtime() {
+ return mElapsedRealTime;
+ }
+
+ void advanceTime(long time) {
+ mElapsedRealTime += time;
+ mExecutor.setUptime(mElapsedRealTime); // assume uptime == elapsedtime
+ }
+ }
+
+ private static class AppOpsUidStateTrackerTestExecutor implements DelayableExecutor {
+
+ private static class QueueElement implements Comparable<QueueElement> {
+
+ private long mExecutionTime;
+ private Runnable mRunnable;
+
+ private QueueElement(long executionTime, Runnable runnable) {
+ mExecutionTime = executionTime;
+ mRunnable = runnable;
+ }
+
+ @Override
+ public int compareTo(QueueElement queueElement) {
+ return Long.compare(mExecutionTime, queueElement.mExecutionTime);
+ }
+ }
+
+ private long mUptime = 0;
+
+ private PriorityQueue<QueueElement> mDelayedMessages = new PriorityQueue();
+
+ @Override
+ public void execute(Runnable runnable) {
+ runnable.run();
+ }
+
+ @Override
+ public void executeDelayed(Runnable runnable, long delay) {
+ if (delay <= 0) {
+ execute(runnable);
+ }
+
+ mDelayedMessages.add(new QueueElement(mUptime + delay, runnable));
+ }
+
+ private void setUptime(long uptime) {
+ while (!mDelayedMessages.isEmpty()
+ && mDelayedMessages.peek().mExecutionTime <= uptime) {
+ mDelayedMessages.poll().mRunnable.run();
+ }
+
+ mUptime = uptime;
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java
index c4c3abc1388e..145e66c92f14 100644
--- a/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/companion/virtual/CameraAccessControllerTest.java
@@ -30,10 +30,13 @@ import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.UserInfo;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraInjectionSession;
import android.hardware.camera2.CameraManager;
import android.os.Process;
+import android.os.UserManager;
import android.testing.TestableContext;
import android.util.ArraySet;
@@ -51,12 +54,17 @@ import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+
@RunWith(AndroidJUnit4.class)
public class CameraAccessControllerTest {
private static final String FRONT_CAMERA = "0";
private static final String REAR_CAMERA = "1";
private static final String TEST_APP_PACKAGE = "some.package";
private static final String OTHER_APP_PACKAGE = "other.package";
+ private static final int PERSONAL_PROFILE_USER_ID = 0;
+ private static final int WORK_PROFILE_USER_ID = 10;
private CameraAccessController mController;
@@ -69,6 +77,8 @@ public class CameraAccessControllerTest {
@Mock
private PackageManager mPackageManager;
@Mock
+ private UserManager mUserManager;
+ @Mock
private VirtualDeviceManagerInternal mDeviceManagerInternal;
@Mock
private CameraAccessController.CameraAccessBlockedCallback mBlockedCallback;
@@ -76,6 +86,7 @@ public class CameraAccessControllerTest {
private ApplicationInfo mTestAppInfo = new ApplicationInfo();
private ApplicationInfo mOtherAppInfo = new ApplicationInfo();
private ArraySet<Integer> mRunningUids = new ArraySet<>();
+ private List<UserInfo> mAliveUsers = new ArrayList<>();
@Captor
ArgumentCaptor<CameraInjectionSession.InjectionStatusCallback> mInjectionCallbackCaptor;
@@ -84,6 +95,7 @@ public class CameraAccessControllerTest {
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
mContext.addMockSystemService(CameraManager.class, mCameraManager);
+ mContext.addMockSystemService(UserManager.class, mUserManager);
mContext.setMockPackageManager(mPackageManager);
LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
LocalServices.addService(VirtualDeviceManagerInternal.class, mDeviceManagerInternal);
@@ -92,10 +104,14 @@ public class CameraAccessControllerTest {
mTestAppInfo.uid = Process.FIRST_APPLICATION_UID;
mOtherAppInfo.uid = Process.FIRST_APPLICATION_UID + 1;
mRunningUids.add(Process.FIRST_APPLICATION_UID);
- when(mPackageManager.getApplicationInfo(eq(TEST_APP_PACKAGE), anyInt())).thenReturn(
- mTestAppInfo);
- when(mPackageManager.getApplicationInfo(eq(OTHER_APP_PACKAGE), anyInt())).thenReturn(
- mOtherAppInfo);
+ mAliveUsers.add(new UserInfo(PERSONAL_PROFILE_USER_ID, "", 0));
+ when(mPackageManager.getApplicationInfoAsUser(
+ eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES),
+ anyInt())).thenReturn(mTestAppInfo);
+ when(mPackageManager.getApplicationInfoAsUser(
+ eq(OTHER_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES),
+ anyInt())).thenReturn(mOtherAppInfo);
+ when(mUserManager.getAliveUsers()).thenReturn(mAliveUsers);
mController.startObservingIfNeeded();
}
@@ -227,4 +243,74 @@ public class CameraAccessControllerTest {
verify(mCameraManager, times(1)).injectCamera(any(), any(), any(), any(), any());
}
+
+ @Test
+ public void multipleUsers_getPersonalProfileAppUid_cameraBlocked()
+ throws CameraAccessException, NameNotFoundException {
+ mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0));
+ when(mPackageManager.getApplicationInfoAsUser(
+ eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES),
+ eq(PERSONAL_PROFILE_USER_ID))).thenReturn(mTestAppInfo);
+ when(mPackageManager.getApplicationInfoAsUser(
+ eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES),
+ eq(WORK_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class);
+ when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(
+ eq(mTestAppInfo.uid))).thenReturn(true);
+ mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE);
+
+ verify(mCameraManager).injectCamera(eq(TEST_APP_PACKAGE), eq(FRONT_CAMERA), anyString(),
+ any(), any());
+ }
+
+ @Test
+ public void multipleUsers_getPersonalProfileAppUid_noCameraBlocking()
+ throws CameraAccessException, NameNotFoundException {
+ mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0));
+ when(mPackageManager.getApplicationInfoAsUser(
+ eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES),
+ eq(PERSONAL_PROFILE_USER_ID))).thenReturn(mTestAppInfo);
+ when(mPackageManager.getApplicationInfoAsUser(
+ eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES),
+ eq(WORK_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class);
+ when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(
+ eq(mTestAppInfo.uid))).thenReturn(false);
+ mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE);
+
+ verify(mCameraManager, never()).injectCamera(any(), any(), any(), any(), any());
+ }
+
+ @Test
+ public void multipleUsers_getWorkProfileAppUid_cameraBlocked()
+ throws CameraAccessException, NameNotFoundException {
+ mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0));
+ when(mPackageManager.getApplicationInfoAsUser(
+ eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES),
+ eq(PERSONAL_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class);
+ when(mPackageManager.getApplicationInfoAsUser(
+ eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES),
+ eq(WORK_PROFILE_USER_ID))).thenReturn(mTestAppInfo);
+ when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(
+ eq(mTestAppInfo.uid))).thenReturn(true);
+ mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE);
+
+ verify(mCameraManager).injectCamera(eq(TEST_APP_PACKAGE), eq(FRONT_CAMERA), anyString(),
+ any(), any());
+ }
+
+ @Test
+ public void multipleUsers_getWorkProfileAppUid_noCameraBlocking()
+ throws CameraAccessException, NameNotFoundException {
+ mAliveUsers.add(new UserInfo(WORK_PROFILE_USER_ID, "", 0));
+ when(mPackageManager.getApplicationInfoAsUser(
+ eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES),
+ eq(PERSONAL_PROFILE_USER_ID))).thenThrow(NameNotFoundException.class);
+ when(mPackageManager.getApplicationInfoAsUser(
+ eq(TEST_APP_PACKAGE), eq(PackageManager.GET_ACTIVITIES),
+ eq(WORK_PROFILE_USER_ID))).thenReturn(mTestAppInfo);
+ when(mDeviceManagerInternal.isAppRunningOnAnyVirtualDevice(
+ eq(mTestAppInfo.uid))).thenReturn(false);
+ mController.onCameraOpened(FRONT_CAMERA, TEST_APP_PACKAGE);
+
+ verify(mCameraManager, never()).injectCamera(any(), any(), any(), any(), any());
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
new file mode 100644
index 000000000000..1a5d496f2002
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Resources;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerCallbacks;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.test.TestLooper;
+import android.util.FloatProperty;
+import android.view.Display;
+import android.view.DisplayInfo;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+import com.android.server.display.RampAnimator.DualRampAnimator;
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.testutils.OffsettableClock;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class DisplayPowerController2Test {
+ private static final String UNIQUE_DISPLAY_ID = "unique_id_test123";
+ private static final int DISPLAY_ID = 42;
+
+ private OffsettableClock mClock;
+ private TestLooper mTestLooper;
+ private Handler mHandler;
+ private DisplayPowerController2.Injector mInjector;
+ private Context mContextSpy;
+
+ @Mock
+ private DisplayPowerCallbacks mDisplayPowerCallbacksMock;
+ @Mock
+ private SensorManager mSensorManagerMock;
+ @Mock
+ private DisplayBlanker mDisplayBlankerMock;
+ @Mock
+ private LogicalDisplay mLogicalDisplayMock;
+ @Mock
+ private DisplayDevice mDisplayDeviceMock;
+ @Mock
+ private BrightnessTracker mBrightnessTrackerMock;
+ @Mock
+ private BrightnessSetting mBrightnessSettingMock;
+ @Mock
+ private WindowManagerPolicy mWindowManagerPolicyMock;
+ @Mock
+ private PowerManager mPowerManagerMock;
+ @Mock
+ private Resources mResourcesMock;
+ @Mock
+ private DisplayDeviceConfig mDisplayDeviceConfigMock;
+ @Mock
+ private DisplayPowerState mDisplayPowerStateMock;
+ @Mock
+ private DualRampAnimator<DisplayPowerState> mDualRampAnimatorMock;
+
+ @Captor
+ private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
+ mClock = new OffsettableClock.Stopped();
+ mTestLooper = new TestLooper(mClock::now);
+ mHandler = new Handler(mTestLooper.getLooper());
+ mInjector = new DisplayPowerController2.Injector() {
+ @Override
+ DisplayPowerController2.Clock getClock() {
+ return mClock::now;
+ }
+
+ @Override
+ DisplayPowerState getDisplayPowerState(DisplayBlanker blanker, ColorFade colorFade,
+ int displayId, int displayState) {
+ return mDisplayPowerStateMock;
+ }
+
+ @Override
+ DualRampAnimator<DisplayPowerState> getDualRampAnimator(DisplayPowerState dps,
+ FloatProperty<DisplayPowerState> firstProperty,
+ FloatProperty<DisplayPowerState> secondProperty) {
+ return mDualRampAnimatorMock;
+ }
+ };
+
+ addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+
+ when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
+ when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+ }
+
+ @After
+ public void tearDown() {
+ LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+ }
+
+ @Test
+ public void testReleaseProxSuspendBlockersOnExit() throws Exception {
+ setUpDisplay(DISPLAY_ID, UNIQUE_DISPLAY_ID);
+
+ Sensor proxSensor = setUpProxSensor();
+
+ DisplayPowerController2 dpc = new DisplayPowerController2(
+ mContextSpy, mInjector, mDisplayPowerCallbacksMock, mHandler,
+ mSensorManagerMock, mDisplayBlankerMock, mLogicalDisplayMock,
+ mBrightnessTrackerMock, mBrightnessSettingMock, () -> {
+ });
+
+ when(mDisplayPowerStateMock.getScreenState()).thenReturn(Display.STATE_ON);
+ // send a display power request
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ dpr.policy = DisplayPowerRequest.POLICY_BRIGHT;
+ dpr.useProximitySensor = true;
+ dpc.requestPowerState(dpr, false /* waitForNegativeProximity */);
+
+ // Run updatePowerState to start listener for the prox sensor
+ advanceTime(1);
+
+ SensorEventListener listener = getSensorEventListener(proxSensor);
+ assertNotNull(listener);
+
+ listener.onSensorChanged(TestUtils.createSensorEvent(proxSensor, 5 /* lux */));
+ advanceTime(1);
+
+ // two times, one for unfinished business and one for proximity
+ verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+ dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+ verify(mDisplayPowerCallbacksMock).acquireSuspendBlocker(
+ dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+
+ dpc.stop();
+ advanceTime(1);
+
+ // two times, one for unfinished business and one for proximity
+ verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+ dpc.getSuspendBlockerUnfinishedBusinessId(DISPLAY_ID));
+ verify(mDisplayPowerCallbacksMock).releaseSuspendBlocker(
+ dpc.getSuspendBlockerProxDebounceId(DISPLAY_ID));
+ }
+
+ /**
+ * Creates a mock and registers it to {@link LocalServices}.
+ */
+ private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
+ LocalServices.removeServiceForTest(clazz);
+ LocalServices.addService(clazz, mock);
+ }
+
+ private void advanceTime(long timeMs) {
+ mClock.fastForward(timeMs);
+ mTestLooper.dispatchAll();
+ }
+
+ private Sensor setUpProxSensor() throws Exception {
+ Sensor proxSensor = TestUtils.createSensor(
+ Sensor.TYPE_PROXIMITY, Sensor.STRING_TYPE_PROXIMITY);
+ when(mSensorManagerMock.getSensorList(eq(Sensor.TYPE_ALL)))
+ .thenReturn(List.of(proxSensor));
+ return proxSensor;
+ }
+
+ private SensorEventListener getSensorEventListener(Sensor sensor) {
+ verify(mSensorManagerMock).registerListener(mSensorEventListenerCaptor.capture(),
+ eq(sensor), eq(SensorManager.SENSOR_DELAY_NORMAL), isA(Handler.class));
+ return mSensorEventListenerCaptor.getValue();
+ }
+
+ private void setUpDisplay(int displayId, String uniqueId) {
+ DisplayInfo info = new DisplayInfo();
+ DisplayDeviceInfo deviceInfo = new DisplayDeviceInfo();
+
+ when(mLogicalDisplayMock.getDisplayIdLocked()).thenReturn(displayId);
+ when(mLogicalDisplayMock.getPrimaryDisplayDeviceLocked()).thenReturn(mDisplayDeviceMock);
+ when(mLogicalDisplayMock.getDisplayInfoLocked()).thenReturn(info);
+ when(mLogicalDisplayMock.isEnabled()).thenReturn(true);
+ when(mLogicalDisplayMock.getPhase()).thenReturn(LogicalDisplay.DISPLAY_PHASE_ENABLED);
+ when(mDisplayDeviceMock.getDisplayDeviceInfoLocked()).thenReturn(deviceInfo);
+ when(mDisplayDeviceMock.getUniqueId()).thenReturn(uniqueId);
+ when(mDisplayDeviceMock.getDisplayDeviceConfig()).thenReturn(mDisplayDeviceConfigMock);
+ when(mDisplayDeviceConfigMock.getProximitySensor()).thenReturn(
+ new DisplayDeviceConfig.SensorData() {
+ {
+ type = Sensor.STRING_TYPE_PROXIMITY;
+ name = null;
+ }
+ });
+ when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 6a18dc181393..9a4bb22d5195 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -233,4 +233,4 @@ public final class DisplayPowerControllerTest {
});
when(mDisplayDeviceConfigMock.getNits()).thenReturn(new float[]{2, 500});
}
-}
+} \ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
index bc9e9bb9bce8..941a3a419d59 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
@@ -16,14 +16,18 @@
package com.android.server.display.color;
+import static android.view.Display.DEFAULT_DISPLAY;
+
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertArrayEquals;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
+import android.hardware.display.DisplayManagerInternal;
import android.os.Binder;
import android.os.IBinder;
import android.view.SurfaceControl;
@@ -50,6 +54,8 @@ public class DisplayWhiteBalanceTintControllerTest {
private Context mMockedContext;
@Mock
private Resources mMockedResources;
+ @Mock
+ private DisplayManagerInternal mDisplayManagerInternal;
private MockitoSession mSession;
private Resources mResources;
@@ -81,7 +87,6 @@ public class DisplayWhiteBalanceTintControllerTest {
doReturn(mMockedResources).when(mMockedContext).getResources();
mDisplayToken = new Binder();
- doReturn(mDisplayToken).when(() -> SurfaceControl.getInternalDisplayToken());
}
@After
@@ -114,8 +119,8 @@ public class DisplayWhiteBalanceTintControllerTest {
displayPrimaries.white.X = 0.950456f;
displayPrimaries.white.Y = 1.000000f;
displayPrimaries.white.Z = 1.089058f;
- doReturn(displayPrimaries)
- .when(() -> SurfaceControl.getDisplayNativePrimaries(mDisplayToken));
+ when(mDisplayManagerInternal.getDisplayNativePrimaries(DEFAULT_DISPLAY))
+ .thenReturn(displayPrimaries);
setUpTintController();
assertWithMessage("Setup with valid SurfaceControl failed")
@@ -134,8 +139,8 @@ public class DisplayWhiteBalanceTintControllerTest {
displayPrimaries.green = new CieXyz();
displayPrimaries.blue = new CieXyz();
displayPrimaries.white = new CieXyz();
- doReturn(displayPrimaries)
- .when(() -> SurfaceControl.getDisplayNativePrimaries(mDisplayToken));
+ when(mDisplayManagerInternal.getDisplayNativePrimaries(DEFAULT_DISPLAY))
+ .thenReturn(displayPrimaries);
setUpTintController();
assertWithMessage("Setup with invalid SurfaceControl succeeded")
@@ -154,7 +159,7 @@ public class DisplayWhiteBalanceTintControllerTest {
.when(mMockedResources)
.getStringArray(R.array.config_displayWhiteBalanceDisplayPrimaries);
// Make SurfaceControl setup fail
- doReturn(null).when(() -> SurfaceControl.getDisplayNativePrimaries(mDisplayToken));
+ when(mDisplayManagerInternal.getDisplayNativePrimaries(DEFAULT_DISPLAY)).thenReturn(null);
setUpTintController();
assertWithMessage("Setup with valid Resources failed")
@@ -178,7 +183,7 @@ public class DisplayWhiteBalanceTintControllerTest {
.when(mMockedResources)
.getStringArray(R.array.config_displayWhiteBalanceDisplayPrimaries);
// Make SurfaceControl setup fail
- doReturn(null).when(() -> SurfaceControl.getDisplayNativePrimaries(mDisplayToken));
+ when(mDisplayManagerInternal.getDisplayNativePrimaries(DEFAULT_DISPLAY)).thenReturn(null);
setUpTintController();
assertWithMessage("Setup with invalid Resources succeeded")
@@ -208,8 +213,8 @@ public class DisplayWhiteBalanceTintControllerTest {
displayPrimaries.white.X = 0.950456f;
displayPrimaries.white.Y = 1.000000f;
displayPrimaries.white.Z = 1.089058f;
- doReturn(displayPrimaries)
- .when(() -> SurfaceControl.getDisplayNativePrimaries(mDisplayToken));
+ when(mDisplayManagerInternal.getDisplayNativePrimaries(DEFAULT_DISPLAY))
+ .thenReturn(displayPrimaries);
setUpTintController();
assertWithMessage("Setup with valid SurfaceControl failed")
@@ -234,7 +239,8 @@ public class DisplayWhiteBalanceTintControllerTest {
}
private void setUpTintController() {
- mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController();
+ mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController(
+ mDisplayManagerInternal);
mDisplayWhiteBalanceTintController.setUp(mMockedContext, true);
mDisplayWhiteBalanceTintController.setActivated(true);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakePackageResetHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakePackageResetHelper.java
new file mode 100644
index 000000000000..c2768d51970a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakePackageResetHelper.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.injector;
+
+/** Version of PackageResetHelper for testing. */
+public class FakePackageResetHelper extends PackageResetHelper {
+
+ public FakePackageResetHelper() {}
+
+ @Override
+ protected void onRegister() {}
+
+ @Override
+ protected void onUnregister() {}
+
+ public boolean isResetableForPackage(String packageName) {
+ return queryResetableForPackage(packageName);
+ }
+
+ public void reset(String packageName) {
+ notifyPackageReset(packageName);
+ }
+}
+
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index 02cacb7bc57c..ca730910943b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -35,6 +35,7 @@ public class TestInjector implements Injector {
private final FakeDeviceIdleHelper mDeviceIdleHelper;
private final FakeEmergencyHelper mEmergencyHelper;
private final LocationUsageLogger mLocationUsageLogger;
+ private final FakePackageResetHelper mPackageResetHelper;
public TestInjector(Context context) {
mUserInfoHelper = new FakeUserInfoHelper();
@@ -50,6 +51,7 @@ public class TestInjector implements Injector {
mDeviceIdleHelper = new FakeDeviceIdleHelper();
mEmergencyHelper = new FakeEmergencyHelper();
mLocationUsageLogger = new LocationUsageLogger();
+ mPackageResetHelper = new FakePackageResetHelper();
}
@Override
@@ -116,4 +118,9 @@ public class TestInjector implements Injector {
public LocationUsageLogger getLocationUsageLogger() {
return mLocationUsageLogger;
}
+
+ @Override
+ public FakePackageResetHelper getPackageResetHelper() {
+ return mPackageResetHelper;
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 0ac14432d113..20e4e8011327 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -1218,6 +1218,44 @@ public class LocationProviderManagerTest {
assertThat(mProvider.getRequest().isActive()).isFalse();
}
+ @Test
+ public void testQueryPackageReset() {
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isFalse();
+
+ ILocationListener listener1 = createMockLocationListener();
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build(), IDENTITY, PERMISSION_FINE, listener1);
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isTrue();
+
+ ILocationListener listener2 = createMockLocationListener();
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build(), IDENTITY, PERMISSION_FINE, listener2);
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isTrue();
+
+ mManager.unregisterLocationRequest(listener1);
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isTrue();
+
+ mManager.unregisterLocationRequest(listener2);
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isFalse();
+ }
+
+ @Test
+ public void testPackageReset() {
+ ILocationListener listener1 = createMockLocationListener();
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build(), IDENTITY, PERMISSION_FINE, listener1);
+ ILocationListener listener2 = createMockLocationListener();
+ mManager.registerLocationRequest(new LocationRequest.Builder(0).setWorkSource(
+ WORK_SOURCE).build(), IDENTITY, PERMISSION_FINE, listener2);
+
+ assertThat(mProvider.getRequest().isActive()).isTrue();
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isTrue();
+
+ mInjector.getPackageResetHelper().reset("mypackage");
+ assertThat(mProvider.getRequest().isActive()).isFalse();
+ assertThat(mInjector.getPackageResetHelper().isResetableForPackage("mypackage")).isFalse();
+ }
+
private ILocationListener createMockLocationListener() {
return spy(new ILocationListener.Stub() {
@Override
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 bb5b1d8a67a9..cc57b9f913e3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -62,10 +62,10 @@ import com.android.server.compat.PlatformCompat
import com.android.server.extendedtestutils.wheneverStatic
import com.android.server.pm.dex.DexManager
import com.android.server.pm.parsing.PackageParser2
-import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.parsing.pkg.PackageImpl
import com.android.server.pm.parsing.pkg.ParsedPackage
import com.android.server.pm.permission.PermissionManagerServiceInternal
+import com.android.server.pm.pkg.AndroidPackage
import com.android.server.pm.pkg.parsing.ParsingPackage
import com.android.server.pm.pkg.parsing.ParsingPackageUtils
import com.android.server.pm.resolution.ComponentResolver
@@ -77,14 +77,6 @@ import com.android.server.testutils.mock
import com.android.server.testutils.nullable
import com.android.server.testutils.whenever
import com.android.server.utils.WatchedArrayMap
-import libcore.util.HexEncoding
-import org.junit.Assert
-import org.junit.rules.TestRule
-import org.junit.runner.Description
-import org.junit.runners.model.Statement
-import org.mockito.AdditionalMatchers.or
-import org.mockito.Mockito
-import org.mockito.quality.Strictness
import java.io.File
import java.io.IOException
import java.nio.file.Files
@@ -93,6 +85,14 @@ import java.security.cert.CertificateException
import java.util.Arrays
import java.util.Random
import java.util.concurrent.FutureTask
+import libcore.util.HexEncoding
+import org.junit.Assert
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+import org.mockito.AdditionalMatchers.or
+import org.mockito.Mockito
+import org.mockito.quality.Strictness
/**
* A utility for mocking behavior of the system and dependencies when testing PackageManagerService
@@ -522,7 +522,7 @@ class MockSystem(withSession: (StaticMockitoSessionBuilder) -> Unit = {}) {
whenever(mocks.packageParser.parsePackage(
or(eq(path), eq(basePath)), anyInt(), anyBoolean())) { parsedPackage }
whenever(mocks.packageParser.parsePackage(
- or(eq(path), eq(basePath)), anyInt(), anyBoolean(), any())) { parsedPackage }
+ or(eq(path), eq(basePath)), anyInt(), anyBoolean())) { parsedPackage }
return parsedPackage
}
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 987192d41203..da929af3267b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/PackageManagerServiceBootTest.kt
@@ -23,6 +23,7 @@ import android.os.Process
import android.util.Log
import com.android.server.pm.pkg.AndroidPackage
import com.android.server.testutils.whenever
+import java.io.File
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.notNullValue
@@ -33,13 +34,11 @@ import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
-import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.argThat
import org.mockito.Mockito
import org.mockito.Mockito.verify
-import java.io.File
@RunWith(JUnit4::class)
class PackageManagerServiceBootTest {
@@ -120,8 +119,7 @@ class PackageManagerServiceBootTest {
whenever(rule.mocks().packageParser.parsePackage(
argThat { path: File -> path.path.contains("a.data.package") },
anyInt(),
- anyBoolean(),
- any()))
+ anyBoolean()))
.thenThrow(PackageManagerException(
PackageManager.INSTALL_FAILED_INVALID_APK, "Oh no!"))
val pm = createPackageManagerService()
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
index 8744f327565b..e28d331b770b 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/SharedLibrariesImplTest.kt
@@ -386,7 +386,7 @@ class SharedLibrariesImplTest {
pkg.setTargetSdkVersion(Build.VERSION_CODES.S)
libraries?.forEach { pkg.addLibraryName(it) }
staticLibrary?.let {
- pkg.setStaticSharedLibName(it)
+ pkg.setStaticSharedLibraryName(it)
pkg.setStaticSharedLibVersion(staticLibraryVersion)
pkg.setStaticSharedLibrary(true)
}
@@ -430,7 +430,7 @@ class SharedLibrariesImplTest {
setTargetSdkVersion(Build.VERSION_CODES.S)
libraries?.forEach { addLibraryName(it) }
staticLibrary?.let {
- setStaticSharedLibName(it)
+ setStaticSharedLibraryName(it)
setStaticSharedLibVersion(staticLibraryVersion)
setStaticSharedLibrary(true)
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java
index 574bab2d9962..278e04ab1a41 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerInternalTest.java
@@ -19,8 +19,6 @@ import static android.os.UserHandle.USER_SYSTEM;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
-import static com.android.server.pm.UserManagerInternal.PARENT_DISPLAY;
-
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
@@ -163,15 +161,33 @@ public final class UserManagerInternalTest extends UserManagerServiceOrInternalT
}
@Test
+ public void testAssignUserToDisplay_userAlreadyAssigned() {
+ enableUsersOnSecondaryDisplays();
+
+ mUmi.assignUserToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+
+ IllegalStateException e = assertThrows(IllegalStateException.class,
+ () -> mUmi.assignUserToDisplay(USER_ID, OTHER_SECONDARY_DISPLAY_ID));
+
+ Log.v(TAG, "Exception: " + e);
+ assertWithMessage("exception (%s) message", e).that(e).hasMessageThat()
+ .matches("Cannot.*" + USER_ID + ".*" + OTHER_SECONDARY_DISPLAY_ID + ".*already.*"
+ + SECONDARY_DISPLAY_ID + ".*");
+
+ assertUserAssignedToDisplay(USER_ID, SECONDARY_DISPLAY_ID);
+ }
+
+ @Test
public void testAssignUserToDisplay_profileOnSameDisplayAsParent() {
enableUsersOnSecondaryDisplays();
addDefaultProfileAndParent();
mUmi.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
- mUmi.assignUserToDisplay(PROFILE_USER_ID, PARENT_DISPLAY);
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
- assertUsersAssignedToDisplays(PARENT_USER_ID, SECONDARY_DISPLAY_ID,
- pair(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+ Log.v(TAG, "Exception: " + e);
+ assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
}
@Test
@@ -181,7 +197,20 @@ public final class UserManagerInternalTest extends UserManagerServiceOrInternalT
mUmi.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
- () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, SECONDARY_DISPLAY_ID));
+ () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, OTHER_SECONDARY_DISPLAY_ID));
+
+ Log.v(TAG, "Exception: " + e);
+ assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+ }
+
+ @Test
+ public void testAssignUserToDisplay_profileDefaultDisplayParentOnSecondaryDisplay() {
+ enableUsersOnSecondaryDisplays();
+ addDefaultProfileAndParent();
+
+ mUmi.assignUserToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
+ IllegalArgumentException e = assertThrows(IllegalArgumentException.class,
+ () -> mUmi.assignUserToDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY));
Log.v(TAG, "Exception: " + e);
assertUserAssignedToDisplay(PARENT_USER_ID, SECONDARY_DISPLAY_ID);
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
index 538adb211fff..90a5fa0d9f38 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceOrInternalTestCase.java
@@ -33,7 +33,6 @@ import android.content.Context;
import android.content.pm.UserInfo;
import android.os.UserManager;
import android.util.Log;
-import android.util.Pair;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -342,7 +341,6 @@ abstract class UserManagerServiceOrInternalTestCase extends ExtendedMockitoTestC
addDefaultProfileAndParent();
mockCurrentUser(PARENT_USER_ID);
startDefaultProfile();
- setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
assertWithMessage("isUserVisibleOnDisplay(%s, %s)", PROFILE_USER_ID, INVALID_DISPLAY)
.that(isUserVisibleOnDisplay(PROFILE_USER_ID, DEFAULT_DISPLAY)).isTrue();
@@ -584,11 +582,19 @@ abstract class UserManagerServiceOrInternalTestCase extends ExtendedMockitoTestC
}
protected final void startDefaultProfile() {
- setUserState(PROFILE_USER_ID, UserState.STATE_RUNNING_UNLOCKED);
+ startUser(PROFILE_USER_ID);
}
protected final void stopDefaultProfile() {
- setUserState(PROFILE_USER_ID, UserState.STATE_STOPPING);
+ stopUser(PROFILE_USER_ID);
+ }
+
+ protected final void startUser(@UserIdInt int userId) {
+ setUserState(userId, UserState.STATE_RUNNING_UNLOCKED);
+ }
+
+ protected final void stopUser(@UserIdInt int userId) {
+ setUserState(userId, UserState.STATE_STOPPING);
}
// NOTE: should only called by tests that indirectly needs to check user assignments (like
@@ -607,24 +613,6 @@ abstract class UserManagerServiceOrInternalTestCase extends ExtendedMockitoTestC
.containsExactly(userId, displayId);
}
- @SafeVarargs
- protected final void assertUsersAssignedToDisplays(@UserIdInt int userId, int displayId,
- @SuppressWarnings("unchecked") Pair<Integer, Integer>... others) {
- Object[] otherObjects = new Object[others.length * 2];
- for (int i = 0; i < others.length; i++) {
- Pair<Integer, Integer> other = others[i];
- otherObjects[i * 2] = other.first;
- otherObjects[i * 2 + 1] = other.second;
-
- }
- assertWithMessage("mUsersOnSecondaryDisplays()").that(usersOnSecondaryDisplaysAsMap())
- .containsExactly(userId, displayId, otherObjects);
- }
-
- protected static Pair<Integer, Integer> pair(@UserIdInt int userId, int secondaryDisplayId) {
- return new Pair<>(userId, secondaryDisplayId);
- }
-
///////////////////
// Private infra //
///////////////////
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 8388a70a87e5..8b5921cd45bd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -88,6 +88,42 @@ public final class UserManagerServiceTest extends UserManagerServiceOrInternalTe
.that(mUms.isCurrentUserOrRunningProfileOfCurrentUser(PROFILE_USER_ID)).isFalse();
}
+ @Test
+ public void testIsUserRunning_StartedUserShouldReturnTrue() {
+ addUser(USER_ID);
+ startUser(USER_ID);
+
+ assertWithMessage("isUserRunning(%s)", USER_ID)
+ .that(mUms.isUserRunning(USER_ID)).isTrue();
+ }
+
+ @Test
+ public void testIsUserRunning_StoppedUserShouldReturnFalse() {
+ addUser(USER_ID);
+ stopUser(USER_ID);
+
+ assertWithMessage("isUserRunning(%s)", USER_ID)
+ .that(mUms.isUserRunning(USER_ID)).isFalse();
+ }
+
+ @Test
+ public void testIsUserRunning_CurrentUserStartedWorkProfileShouldReturnTrue() {
+ addDefaultProfileAndParent();
+ startDefaultProfile();
+
+ assertWithMessage("isUserRunning(%s)", PROFILE_USER_ID)
+ .that(mUms.isUserRunning(PROFILE_USER_ID)).isTrue();
+ }
+
+ @Test
+ public void testIsUserRunning_CurrentUserStoppedWorkProfileShouldReturnFalse() {
+ addDefaultProfileAndParent();
+ stopDefaultProfile();
+
+ assertWithMessage("isUserRunning(%s)", PROFILE_USER_ID)
+ .that(mUms.isUserRunning(PROFILE_USER_ID)).isFalse();
+ }
+
@Override
protected boolean isUserVisible(int userId) {
return mUms.isUserVisibleUnchecked(userId);
diff --git a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
index 19b798da5aab..b7bbcd7562cd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/tare/JobSchedulerEconomicPolicyTest.java
@@ -134,15 +134,36 @@ public class JobSchedulerEconomicPolicyTest {
mEconomicPolicy.getInitialSatiatedConsumptionLimit());
assertEquals(EconomyManager.DEFAULT_JS_HARD_CONSUMPTION_LIMIT_CAKES,
mEconomicPolicy.getHardSatiatedConsumptionLimit());
+
final String pkgRestricted = "com.pkg.restricted";
when(mIrs.isPackageRestricted(anyInt(), eq(pkgRestricted))).thenReturn(true);
+ assertEquals(0, mEconomicPolicy.getMinSatiatedBalance(0, pkgRestricted));
assertEquals(0, mEconomicPolicy.getMaxSatiatedBalance(0, pkgRestricted));
- assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
- mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
+
final String pkgExempted = "com.pkg.exempted";
when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_EXEMPTED_CAKES,
mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+ assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
+ mEconomicPolicy.getMaxSatiatedBalance(0, pkgExempted));
+
+ final String pkgUpdater = "com.pkg.updater";
+ when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(5);
+ assertEquals(5 * EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES
+ + EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
+ mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
+ assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
+ mEconomicPolicy.getMaxSatiatedBalance(0, pkgUpdater));
+ // Make sure it doesn't suggest a min balance greater than max.
+ final int updateCount = (int) (EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES
+ / EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER_CAKES);
+ when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater)))
+ .thenReturn(updateCount);
+ assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
+ mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
+
+ assertEquals(EconomyManager.DEFAULT_JS_MAX_SATIATED_BALANCE_CAKES,
+ mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
assertEquals(EconomyManager.DEFAULT_JS_MIN_SATIATED_BALANCE_OTHER_APP_CAKES,
mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
}
@@ -152,8 +173,10 @@ public class JobSchedulerEconomicPolicyTest {
setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
setDeviceConfigCakes(EconomyManager.KEY_JS_HARD_CONSUMPTION_LIMIT, arcToCake(25));
setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(10));
- setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(9));
- setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(7));
+ setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(6));
+ setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(4));
+ setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
+ arcToCake(1));
assertEquals(arcToCake(5), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
assertEquals(arcToCake(25), mEconomicPolicy.getHardSatiatedConsumptionLimit());
@@ -163,8 +186,12 @@ public class JobSchedulerEconomicPolicyTest {
assertEquals(arcToCake(10), mEconomicPolicy.getMaxSatiatedBalance(0, "com.any.other.app"));
final String pkgExempted = "com.pkg.exempted";
when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
- assertEquals(arcToCake(9), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
- assertEquals(arcToCake(7), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+ assertEquals(arcToCake(6), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
+ assertEquals(arcToCake(4), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+ final String pkgUpdater = "com.pkg.updater";
+ when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(3);
+ assertEquals(arcToCake(4) + 3 * arcToCake(1),
+ mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
}
@Test
@@ -175,6 +202,8 @@ public class JobSchedulerEconomicPolicyTest {
setDeviceConfigCakes(EconomyManager.KEY_JS_MAX_SATIATED_BALANCE, arcToCake(-1));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_EXEMPTED, arcToCake(-2));
setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_OTHER_APP, arcToCake(-3));
+ setDeviceConfigCakes(EconomyManager.KEY_JS_MIN_SATIATED_BALANCE_INCREMENT_APP_UPDATER,
+ arcToCake(-4));
assertEquals(arcToCake(1), mEconomicPolicy.getInitialSatiatedConsumptionLimit());
assertEquals(arcToCake(1), mEconomicPolicy.getHardSatiatedConsumptionLimit());
@@ -186,6 +215,10 @@ public class JobSchedulerEconomicPolicyTest {
when(mIrs.isPackageExempted(anyInt(), eq(pkgExempted))).thenReturn(true);
assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, pkgExempted));
assertEquals(arcToCake(0), mEconomicPolicy.getMinSatiatedBalance(0, "com.any.other.app"));
+ final String pkgUpdater = "com.pkg.updater";
+ when(mIrs.getAppUpdateResponsibilityCount(anyInt(), eq(pkgUpdater))).thenReturn(5);
+ assertEquals(arcToCake(0) + 5 * arcToCake(0),
+ mEconomicPolicy.getMinSatiatedBalance(0, pkgUpdater));
// Test min+max reversed.
setDeviceConfigCakes(EconomyManager.KEY_JS_INITIAL_CONSUMPTION_LIMIT, arcToCake(5));
diff --git a/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
new file mode 100644
index 000000000000..33870f1d3b86
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/trust/TrustManagerServiceTest.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.trust;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.argThat;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.annotation.Nullable;
+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.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.os.test.TestLooper;
+import android.provider.Settings;
+import android.service.trust.TrustAgentService;
+import android.testing.TestableContext;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.server.LocalServices;
+import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
+
+import com.google.android.collect.Lists;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class TrustManagerServiceTest {
+
+ @Rule
+ public MockitoRule mMockitoRule = MockitoJUnit.rule();
+ @Rule
+ public final MockContext mMockContext = new MockContext(
+ ApplicationProvider.getApplicationContext());
+
+ private static final String URI_SCHEME_PACKAGE = "package";
+ private static final int TEST_USER_ID = UserHandle.USER_SYSTEM;
+
+ private final TestLooper mLooper = new TestLooper();
+ private final ArrayList<ResolveInfo> mTrustAgentResolveInfoList = new ArrayList<>();
+ private final LockPatternUtils mLockPatternUtils = new LockPatternUtils(mMockContext);
+ private final TrustManagerService mService = new TrustManagerService(mMockContext);
+
+ @Mock
+ private PackageManager mPackageManagerMock;
+
+ @Before
+ public void setUp() {
+ resetTrustAgentLockSettings();
+ LocalServices.addService(SystemServiceManager.class, mock(SystemServiceManager.class));
+
+ ArgumentMatcher<Intent> trustAgentIntentMatcher = new ArgumentMatcher<Intent>() {
+ @Override
+ public boolean matches(Intent argument) {
+ return TrustAgentService.SERVICE_INTERFACE.equals(argument.getAction());
+ }
+ };
+ when(mPackageManagerMock.queryIntentServicesAsUser(argThat(trustAgentIntentMatcher),
+ anyInt(), anyInt())).thenReturn(mTrustAgentResolveInfoList);
+ when(mPackageManagerMock.checkPermission(any(), any())).thenReturn(
+ PackageManager.PERMISSION_GRANTED);
+ mMockContext.setMockPackageManager(mPackageManagerMock);
+ }
+
+ @After
+ public void tearDown() {
+ resetTrustAgentLockSettings();
+ LocalServices.removeServiceForTest(SystemServiceManager.class);
+ }
+
+ @Test
+ public void firstBootCompleted_systemTrustAgentsEnabled() {
+ ComponentName systemTrustAgent1 = ComponentName.unflattenFromString(
+ "com.android/.SystemTrustAgent");
+ ComponentName systemTrustAgent2 = ComponentName.unflattenFromString(
+ "com.android/.AnotherSystemTrustAgent");
+ ComponentName userTrustAgent1 = ComponentName.unflattenFromString(
+ "com.user/.UserTrustAgent");
+ ComponentName userTrustAgent2 = ComponentName.unflattenFromString(
+ "com.user/.AnotherUserTrustAgent");
+ addTrustAgent(systemTrustAgent1, /* isSystemApp= */ true);
+ addTrustAgent(systemTrustAgent2, /* isSystemApp= */ true);
+ addTrustAgent(userTrustAgent1, /* isSystemApp= */ false);
+ addTrustAgent(userTrustAgent2, /* isSystemApp= */ false);
+
+ bootService();
+
+ assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
+ systemTrustAgent1, systemTrustAgent2);
+ assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
+ systemTrustAgent1, systemTrustAgent2, userTrustAgent1, userTrustAgent2);
+ }
+
+ @Test
+ public void firstBootCompleted_defaultTrustAgentEnabled() {
+ ComponentName systemTrustAgent = ComponentName.unflattenFromString(
+ "com.android/.SystemTrustAgent");
+ ComponentName defaultTrustAgent = ComponentName.unflattenFromString(
+ "com.user/.DefaultTrustAgent");
+ addTrustAgent(systemTrustAgent, /* isSystemApp= */ true);
+ addTrustAgent(defaultTrustAgent, /* isSystemApp= */ false);
+ mMockContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.string.config_defaultTrustAgent,
+ defaultTrustAgent.flattenToString());
+
+ bootService();
+
+ assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
+ defaultTrustAgent);
+ assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
+ systemTrustAgent, defaultTrustAgent);
+ }
+
+ @Test
+ public void serviceBooted_knownAgentsNotSet_enabledAgentsNotUpdated() {
+ ComponentName trustAgent1 = ComponentName.unflattenFromString(
+ "com.android/.SystemTrustAgent");
+ ComponentName trustAgent2 = ComponentName.unflattenFromString(
+ "com.android/.AnotherSystemTrustAgent");
+ initializeEnabledAgents(trustAgent1);
+ addTrustAgent(trustAgent1, /* isSystemApp= */ true);
+ addTrustAgent(trustAgent2, /* isSystemApp= */ true);
+
+ bootService();
+
+ assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
+ trustAgent1);
+ assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
+ trustAgent1, trustAgent2);
+ }
+
+ @Test
+ public void serviceBooted_knownAgentsSet_enabledAgentsUpdated() {
+ ComponentName trustAgent1 = ComponentName.unflattenFromString(
+ "com.android/.SystemTrustAgent");
+ ComponentName trustAgent2 = ComponentName.unflattenFromString(
+ "com.android/.AnotherSystemTrustAgent");
+ initializeEnabledAgents(trustAgent1);
+ initializeKnownAgents(trustAgent1);
+ addTrustAgent(trustAgent1, /* isSystemApp= */ true);
+ addTrustAgent(trustAgent2, /* isSystemApp= */ true);
+
+ bootService();
+
+ assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
+ trustAgent1, trustAgent2);
+ assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
+ trustAgent1, trustAgent2);
+ }
+
+ @Test
+ public void newSystemTrustAgent_setToEnabledAndKnown() {
+ bootService();
+ ComponentName newAgentComponentName = ComponentName.unflattenFromString(
+ "com.android/.SystemTrustAgent");
+ addTrustAgent(newAgentComponentName, /* isSystemApp= */ true);
+
+ mMockContext.sendPackageChangedBroadcast(newAgentComponentName);
+
+ assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
+ newAgentComponentName);
+ assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
+ newAgentComponentName);
+ }
+
+ @Test
+ public void newSystemTrustAgent_notEnabledWhenDefaultAgentIsSet() {
+ ComponentName defaultTrustAgent = ComponentName.unflattenFromString(
+ "com.user/.DefaultTrustAgent");
+ addTrustAgent(defaultTrustAgent, /* isSystemApp= */ false);
+ mMockContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.string.config_defaultTrustAgent,
+ defaultTrustAgent.flattenToString());
+ bootService();
+ ComponentName newAgentComponentName = ComponentName.unflattenFromString(
+ "com.android/.SystemTrustAgent");
+ addTrustAgent(newAgentComponentName, /* isSystemApp= */ true);
+
+ mMockContext.sendPackageChangedBroadcast(newAgentComponentName);
+
+ assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
+ defaultTrustAgent);
+ assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
+ defaultTrustAgent, newAgentComponentName);
+ }
+
+ @Test
+ public void newNonSystemTrustAgent_notEnabledButMarkedAsKnown() {
+ bootService();
+ ComponentName newAgentComponentName = ComponentName.unflattenFromString(
+ "com.user/.UserTrustAgent");
+ addTrustAgent(newAgentComponentName, /* isSystemApp= */ false);
+
+ mMockContext.sendPackageChangedBroadcast(newAgentComponentName);
+
+ assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).isEmpty();
+ assertThat(mLockPatternUtils.getKnownTrustAgents(TEST_USER_ID)).containsExactly(
+ newAgentComponentName);
+ }
+
+ @Test
+ public void existingTrustAgentChanged_notEnabled() {
+ ComponentName systemTrustAgent1 = ComponentName.unflattenFromString(
+ "com.android/.SystemTrustAgent");
+ ComponentName systemTrustAgent2 = ComponentName.unflattenFromString(
+ "com.android/.AnotherSystemTrustAgent");
+ addTrustAgent(systemTrustAgent1, /* isSystemApp= */ true);
+ addTrustAgent(systemTrustAgent2, /* isSystemApp= */ true);
+ bootService();
+ // Simulate user turning off systemTrustAgent2
+ mLockPatternUtils.setEnabledTrustAgents(Collections.singletonList(systemTrustAgent1),
+ TEST_USER_ID);
+
+ mMockContext.sendPackageChangedBroadcast(systemTrustAgent2);
+
+ assertThat(mLockPatternUtils.getEnabledTrustAgents(TEST_USER_ID)).containsExactly(
+ systemTrustAgent1);
+ }
+
+ private void addTrustAgent(ComponentName agentComponentName, boolean isSystemApp) {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ if (isSystemApp) {
+ applicationInfo.flags = ApplicationInfo.FLAG_SYSTEM;
+ }
+
+ ServiceInfo serviceInfo = new ServiceInfo();
+ serviceInfo.packageName = agentComponentName.getPackageName();
+ serviceInfo.name = agentComponentName.getClassName();
+ serviceInfo.applicationInfo = applicationInfo;
+
+ ResolveInfo resolveInfo = new ResolveInfo();
+ resolveInfo.serviceInfo = serviceInfo;
+ mTrustAgentResolveInfoList.add(resolveInfo);
+ }
+
+ private void initializeEnabledAgents(ComponentName... enabledAgents) {
+ mLockPatternUtils.setEnabledTrustAgents(Lists.newArrayList(enabledAgents), TEST_USER_ID);
+ Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
+ Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
+ }
+
+ private void initializeKnownAgents(ComponentName... knownAgents) {
+ mLockPatternUtils.setKnownTrustAgents(Lists.newArrayList(knownAgents), TEST_USER_ID);
+ Settings.Secure.putIntForUser(mMockContext.getContentResolver(),
+ Settings.Secure.KNOWN_TRUST_AGENTS_INITIALIZED, 1, TEST_USER_ID);
+ }
+
+ private void bootService() {
+ mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+ mService.onBootPhase(SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ }
+
+ private void resetTrustAgentLockSettings() {
+ mLockPatternUtils.setEnabledTrustAgents(Collections.emptyList(), TEST_USER_ID);
+ mLockPatternUtils.setKnownTrustAgents(Collections.emptyList(), TEST_USER_ID);
+ }
+
+ /** A mock Context that allows the test process to send protected broadcasts. */
+ private static final class MockContext extends TestableContext {
+
+ private final ArrayList<BroadcastReceiver> mPackageChangedBroadcastReceivers =
+ new ArrayList<>();
+
+ MockContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ @Nullable
+ public Intent registerReceiverAsUser(BroadcastReceiver receiver,
+ UserHandle user, IntentFilter filter, @Nullable String broadcastPermission,
+ @Nullable Handler scheduler) {
+
+ if (filter.hasAction(Intent.ACTION_PACKAGE_CHANGED)) {
+ mPackageChangedBroadcastReceivers.add(receiver);
+ }
+ return super.registerReceiverAsUser(receiver, user, filter, broadcastPermission,
+ scheduler);
+ }
+
+ void sendPackageChangedBroadcast(ComponentName changedComponent) {
+ Intent intent = new Intent(
+ Intent.ACTION_PACKAGE_CHANGED,
+ Uri.fromParts(URI_SCHEME_PACKAGE,
+ changedComponent.getPackageName(), /* fragment= */ null))
+ .putExtra(Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST,
+ new String[]{changedComponent.getClassName()})
+ .putExtra(Intent.EXTRA_USER_HANDLE, TEST_USER_ID)
+ .putExtra(Intent.EXTRA_UID, UserHandle.of(TEST_USER_ID).getUid(1234));
+ for (BroadcastReceiver receiver : mPackageChangedBroadcastReceivers) {
+ receiver.onReceive(this, intent);
+ }
+ }
+ }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
index 849e6730ac11..00d7541a79dc 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
@@ -155,6 +155,27 @@ public class AlarmQueueTest {
anyInt(), eq(nowElapsed + HOUR_IN_MILLIS), eq(ALARM_TAG), any(), any());
}
+ @Test
+ public void testAddingLargeAlarmTimes() {
+ final AlarmQueue<String> alarmQueue = createAlarmQueue(true, 0);
+ final long nowElapsed = mInjector.getElapsedRealtime();
+
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ alarmQueue.addAlarm("com.android.test.1", Long.MAX_VALUE - 5);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1))
+ .setExact(anyInt(), eq(Long.MAX_VALUE - 5), eq(ALARM_TAG), any(), any());
+ alarmQueue.addAlarm("com.android.test.2", Long.MAX_VALUE - 4);
+ inOrder.verify(mAlarmManager, never())
+ .setExact(anyInt(), anyLong(), eq(ALARM_TAG), any(), any());
+ alarmQueue.addAlarm("com.android.test.3", nowElapsed + 5);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1))
+ .setExact(anyInt(), eq(nowElapsed + 5), eq(ALARM_TAG), any(), any());
+ alarmQueue.addAlarm("com.android.test.4", nowElapsed + 6);
+ inOrder.verify(mAlarmManager, never())
+ .setExact(anyInt(), anyLong(), eq(ALARM_TAG), any(), any());
+ }
+
/**
* Verify that updating the alarm time for a key will result in the AlarmManager alarm changing,
* if needed.
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
index 3c1710250803..0d6f3267f41d 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerServiceTest.java
@@ -37,11 +37,14 @@ import static com.android.server.am.ProcessList.NETWORK_STATE_BLOCK;
import static com.android.server.am.ProcessList.NETWORK_STATE_NO_CHANGE;
import static com.android.server.am.ProcessList.NETWORK_STATE_UNBLOCK;
+import static com.google.common.truth.Truth.assertWithMessage;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
@@ -62,7 +65,6 @@ import android.app.SyncNotedAppOp;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
@@ -74,6 +76,8 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.util.IntArray;
+import android.util.Log;
+import android.util.Pair;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;
@@ -116,6 +120,7 @@ public class ActivityManagerServiceTest {
private static final String TAG = ActivityManagerServiceTest.class.getSimpleName();
private static final int TEST_UID = 11111;
+ private static final int USER_ID = 666;
private static final long TEST_PROC_STATE_SEQ1 = 555;
private static final long TEST_PROC_STATE_SEQ2 = 556;
@@ -147,8 +152,8 @@ public class ActivityManagerServiceTest {
@Rule public ServiceThreadRule mServiceThreadRule = new ServiceThreadRule();
private Context mContext = getInstrumentation().getTargetContext();
+
@Mock private AppOpsService mAppOpsService;
- @Mock private PackageManager mPackageManager;
private TestInjector mInjector;
private ActivityManagerService mAms;
@@ -828,6 +833,57 @@ public class ActivityManagerServiceTest {
true); // expectWait
}
+ @Test
+ public void testGetSecondaryDisplayIdsForStartingBackgroundUsers() {
+ mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{4, 8, 15, 16, 23, 42};
+
+ int [] displayIds = mAms.getSecondaryDisplayIdsForStartingBackgroundUsers();
+
+ assertWithMessage("mAms.getSecondaryDisplayIdsForStartingBackgroundUsers()")
+ .that(displayIds).asList().containsExactly(4, 8, 15, 16, 23, 42);
+ }
+
+ @Test
+ public void testStartUserInBackgroundOnSecondaryDisplay_invalidDisplay() {
+ mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{4, 8, 15, 16, 23, 42};
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mAms.startUserInBackgroundOnSecondaryDisplay(USER_ID, 666));
+
+ assertWithMessage("UserController.startUserOnSecondaryDisplay() calls")
+ .that(mInjector.usersStartedOnSecondaryDisplays).isEmpty();
+ }
+
+ @Test
+ public void testStartUserInBackgroundOnSecondaryDisplay_validDisplay_failed() {
+ mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{ 4, 8, 15, 16, 23, 42 };
+ mInjector.returnValueForstartUserOnSecondaryDisplay = false;
+
+ boolean started = mAms.startUserInBackgroundOnSecondaryDisplay(USER_ID, 42);
+ Log.v(TAG, "Started: " + started);
+
+ assertWithMessage("mAms.startUserInBackgroundOnSecondaryDisplay(%s, 42)", USER_ID)
+ .that(started).isFalse();
+ assertWithMessage("UserController.startUserOnSecondaryDisplay() calls")
+ .that(mInjector.usersStartedOnSecondaryDisplays)
+ .containsExactly(new Pair<>(USER_ID, 42));
+ }
+
+ @Test
+ public void testStartUserInBackgroundOnSecondaryDisplay_validDisplay_success() {
+ mInjector.secondaryDisplayIdsForStartingBackgroundUsers = new int[]{ 4, 8, 15, 16, 23, 42 };
+ mInjector.returnValueForstartUserOnSecondaryDisplay = true;
+
+ boolean started = mAms.startUserInBackgroundOnSecondaryDisplay(USER_ID, 42);
+ Log.v(TAG, "Started: " + started);
+
+ assertWithMessage("mAms.startUserInBackgroundOnSecondaryDisplay(%s, 42)", USER_ID)
+ .that(started).isTrue();
+ assertWithMessage("UserController.startUserOnSecondaryDisplay() calls")
+ .that(mInjector.usersStartedOnSecondaryDisplays)
+ .containsExactly(new Pair<>(USER_ID, 42));
+ }
+
private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq,
long lastNetworkUpdatedProcStateSeq,
final long procStateSeqToWait, boolean expectWait) throws Exception {
@@ -922,7 +978,11 @@ public class ActivityManagerServiceTest {
}
private class TestInjector extends Injector {
- private boolean mRestricted = true;
+ public boolean restricted = true;
+ public int[] secondaryDisplayIdsForStartingBackgroundUsers;
+
+ public boolean returnValueForstartUserOnSecondaryDisplay;
+ public List<Pair<Integer, Integer>> usersStartedOnSecondaryDisplays = new ArrayList<>();
TestInjector(Context context) {
super(context);
@@ -940,11 +1000,18 @@ public class ActivityManagerServiceTest {
@Override
public boolean isNetworkRestrictedForUid(int uid) {
- return mRestricted;
+ return restricted;
}
- public void setNetworkRestrictedForUid(boolean restricted) {
- mRestricted = restricted;
+ @Override
+ public int[] getSecondaryDisplayIdsForStartingBackgroundUsers() {
+ return secondaryDisplayIdsForStartingBackgroundUsers;
+ }
+
+ @Override
+ public boolean startUserOnSecondaryDisplay(int userId, int displayId) {
+ usersStartedOnSecondaryDisplays.add(new Pair<>(userId, displayId));
+ return returnValueForstartUserOnSecondaryDisplay;
}
}
}
diff --git a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
index 693bc7dc4a42..574aaf00e460 100644
--- a/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/CoreSettingsObserverTest.java
@@ -24,10 +24,12 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.os.Bundle;
import android.os.Handler;
import android.provider.Settings;
@@ -99,6 +101,9 @@ public class CoreSettingsObserverTest {
// To prevent NullPointerException at the constructor of ActivityManagerConstants.
when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
when(mResources.getIntArray(anyInt())).thenReturn(new int[0]);
+ final TypedArray mockTypedArray = mock(TypedArray.class);
+ when(mockTypedArray.length()).thenReturn(1);
+ when(mResources.obtainTypedArray(anyInt())).thenReturn(mockTypedArray);
mAms = new ActivityManagerService(new TestInjector(mContext),
mServiceThreadRule.getThread());
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 81f899c7d645..96c3823f8349 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -679,7 +679,7 @@ public class UserControllerTest {
setUpAndStartProfileInBackground(TEST_USER_ID1);
startBackgroundUserAssertions();
- verifyUserAssignedToDisplay(TEST_USER_ID1, UserManagerInternal.PARENT_DISPLAY);
+ verifyUserAssignedToDisplay(TEST_USER_ID1, Display.DEFAULT_DISPLAY);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index 5d9d7656aa5b..6b8c26d7b1d4 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -25,7 +25,6 @@ import static org.mockito.Mockito.verify;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.IInputManager;
-import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -39,6 +38,7 @@ import android.view.WindowManager;
import androidx.test.InstrumentationRegistry;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
index cc5ed92e02d9..51bd5b0f76c9 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputManagerMockHelper.java
@@ -57,6 +57,8 @@ class InputManagerMockHelper {
doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputMouse(
anyString(), anyInt(), anyInt(), anyString());
+ doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputDpad(
+ anyString(), anyInt(), anyInt(), anyString());
doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputKeyboard(
anyString(), anyInt(), anyInt(), anyString());
doAnswer(this::handleNativeOpenInputDevice).when(mNativeWrapperMock).openUinputTouchscreen(
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index cc2cdba69af6..57ded9905273 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -56,7 +56,6 @@ import android.content.pm.ApplicationInfo;
import android.graphics.Point;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.IInputManager;
-import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -84,6 +83,7 @@ import androidx.test.InstrumentationRegistry;
import com.android.internal.app.BlockedAppStreamingActivity;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
import org.junit.Before;
import org.junit.Test;
@@ -387,6 +387,14 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
+ public void createVirtualDpad_noDisplay_failsSecurityException() {
+ assertThrows(
+ SecurityException.class,
+ () -> mDeviceImpl.createVirtualDpad(DISPLAY_ID, DEVICE_NAME, VENDOR_ID,
+ PRODUCT_ID, BINDER));
+ }
+
+ @Test
public void createVirtualKeyboard_noDisplay_failsSecurityException() {
assertThrows(
SecurityException.class,
@@ -418,6 +426,17 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
+ public void createVirtualDpad_noPermission_failsSecurityException() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+ doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
+ eq(Manifest.permission.CREATE_VIRTUAL_DEVICE), anyString());
+ assertThrows(
+ SecurityException.class,
+ () -> mDeviceImpl.createVirtualDpad(DISPLAY_ID, DEVICE_NAME, VENDOR_ID,
+ PRODUCT_ID, BINDER));
+ }
+
+ @Test
public void createVirtualKeyboard_noPermission_failsSecurityException() {
mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
doCallRealMethod().when(mContext).enforceCallingOrSelfPermission(
@@ -468,6 +487,17 @@ public class VirtualDeviceManagerServiceTest {
}
@Test
+ public void createVirtualDpad_hasDisplay_obtainFileDescriptor() {
+ mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
+ mDeviceImpl.createVirtualDpad(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
+ BINDER);
+ assertWithMessage("Virtual dpad should register fd when the display matches")
+ .that(mInputController.mInputDeviceDescriptors).isNotEmpty();
+ verify(mNativeWrapperMock).openUinputDpad(eq(DEVICE_NAME), eq(VENDOR_ID),
+ eq(PRODUCT_ID), anyString());
+ }
+
+ @Test
public void createVirtualKeyboard_hasDisplay_obtainFileDescriptor() {
mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
mDeviceImpl.createVirtualKeyboard(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
@@ -483,7 +513,7 @@ public class VirtualDeviceManagerServiceTest {
mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
mDeviceImpl.createVirtualMouse(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
BINDER);
- assertWithMessage("Virtual keyboard should register fd when the display matches")
+ assertWithMessage("Virtual mouse should register fd when the display matches")
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
verify(mNativeWrapperMock).openUinputMouse(eq(DEVICE_NAME), eq(VENDOR_ID), eq(PRODUCT_ID),
anyString());
@@ -494,7 +524,7 @@ public class VirtualDeviceManagerServiceTest {
mDeviceImpl.mVirtualDisplayIds.add(DISPLAY_ID);
mDeviceImpl.createVirtualTouchscreen(DISPLAY_ID, DEVICE_NAME, VENDOR_ID, PRODUCT_ID,
BINDER, new Point(WIDTH, HEIGHT));
- assertWithMessage("Virtual keyboard should register fd when the display matches")
+ assertWithMessage("Virtual touchscreen should register fd when the display matches")
.that(mInputController.mInputDeviceDescriptors).isNotEmpty();
verify(mNativeWrapperMock).openUinputTouchscreen(eq(DEVICE_NAME), eq(VENDOR_ID),
eq(PRODUCT_ID), anyString(), eq(HEIGHT), eq(WIDTH));
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
index 3f4148bb49e1..a45144e23c17 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/DeviceStateManagerServiceTest.java
@@ -588,6 +588,157 @@ public final class DeviceStateManagerServiceTest {
});
}
+ @Test
+ public void requestBaseStateOverride() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+ flushHandler();
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestBaseStateOverride(token,
+ OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ // Flush the handler twice. The first flush ensures the request is added and the policy is
+ // notified, while the second flush ensures the callback is notified once the change is
+ // committed.
+ flushHandler(2 /* count */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
+ assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mService.getOverrideBaseState().get(), OTHER_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ assertNotNull(callback.getLastNotifiedInfo());
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ OTHER_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mService.getBinderService().cancelBaseStateOverride();
+ flushHandler();
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set back to the requested state once the override is cleared.
+ assertEquals(mService.getCommittedState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ DEFAULT_DEVICE_STATE.getIdentifier() + ":" + DEFAULT_DEVICE_STATE.getName());
+ assertEquals(mService.getBaseState(), Optional.of(DEFAULT_DEVICE_STATE));
+ assertFalse(mService.getOverrideBaseState().isPresent());
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ DEFAULT_DEVICE_STATE.getIdentifier());
+
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ DEFAULT_DEVICE_STATE.getIdentifier());
+ }
+
+ @Test
+ public void requestBaseStateOverride_cancelledByBaseStateUpdate() throws RemoteException {
+ final DeviceState testDeviceState = new DeviceState(2, "TEST", 0);
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+ mProvider.notifySupportedDeviceStates(
+ new DeviceState[]{DEFAULT_DEVICE_STATE, OTHER_DEVICE_STATE, testDeviceState });
+ flushHandler();
+
+ final IBinder token = new Binder();
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_UNKNOWN);
+
+ mService.getBinderService().requestBaseStateOverride(token,
+ OTHER_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ // Flush the handler twice. The first flush ensures the request is added and the policy is
+ // notified, while the second flush ensures the callback is notified once the change is
+ // committed.
+ flushHandler(2 /* count */);
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_ACTIVE);
+ // Committed state changes as there is a requested override.
+ assertEquals(mService.getCommittedState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mSysPropSetter.getValue(),
+ OTHER_DEVICE_STATE.getIdentifier() + ":" + OTHER_DEVICE_STATE.getName());
+ assertEquals(mService.getBaseState(), Optional.of(OTHER_DEVICE_STATE));
+ assertEquals(mService.getOverrideBaseState().get(), OTHER_DEVICE_STATE);
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ assertNotNull(callback.getLastNotifiedInfo());
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ OTHER_DEVICE_STATE.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ OTHER_DEVICE_STATE.getIdentifier());
+
+ mProvider.setState(testDeviceState.getIdentifier());
+ flushHandler();
+
+ assertEquals(callback.getLastNotifiedStatus(token),
+ TestDeviceStateManagerCallback.STATUS_CANCELED);
+ // Committed state is set to the new base state once the override is cleared.
+ assertEquals(mService.getCommittedState(), Optional.of(testDeviceState));
+ assertEquals(mSysPropSetter.getValue(),
+ testDeviceState.getIdentifier() + ":" + testDeviceState.getName());
+ assertEquals(mService.getBaseState(), Optional.of(testDeviceState));
+ assertFalse(mService.getOverrideBaseState().isPresent());
+ assertFalse(mService.getOverrideState().isPresent());
+ assertEquals(mPolicy.getMostRecentRequestedStateToConfigure(),
+ testDeviceState.getIdentifier());
+
+ assertEquals(callback.getLastNotifiedInfo().baseState,
+ testDeviceState.getIdentifier());
+ assertEquals(callback.getLastNotifiedInfo().currentState,
+ testDeviceState.getIdentifier());
+ }
+
+ @Test
+ public void requestBaseState_unsupportedState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestBaseStateOverride(token,
+ UNSUPPORTED_DEVICE_STATE.getIdentifier(), 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestBaseState_invalidState() throws RemoteException {
+ TestDeviceStateManagerCallback callback = new TestDeviceStateManagerCallback();
+ mService.getBinderService().registerCallback(callback);
+
+ assertThrows(IllegalArgumentException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestBaseStateOverride(token, INVALID_DEVICE_STATE,
+ 0 /* flags */);
+ });
+ }
+
+ @Test
+ public void requestBaseState_beforeRegisteringCallback() {
+ assertThrows(IllegalStateException.class, () -> {
+ final IBinder token = new Binder();
+ mService.getBinderService().requestBaseStateOverride(token,
+ DEFAULT_DEVICE_STATE.getIdentifier(),
+ 0 /* flags */);
+ });
+ }
+
private static void assertArrayEquals(int[] expected, int[] actual) {
Assert.assertTrue(Arrays.equals(expected, actual));
}
diff --git a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
index 2297c91818c0..430504ca2428 100644
--- a/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicestate/OverrideRequestControllerTest.java
@@ -16,6 +16,8 @@
package com.android.server.devicestate;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
+import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_ACTIVE;
import static com.android.server.devicestate.OverrideRequestController.STATUS_CANCELED;
@@ -57,7 +59,7 @@ public final class OverrideRequestControllerTest {
@Test
public void addRequest() {
OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
assertNull(mStatusListener.getLastStatus(request));
mController.addRequest(request);
@@ -67,14 +69,14 @@ public final class OverrideRequestControllerTest {
@Test
public void addRequest_cancelExistingRequestThroughNewRequest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
assertNull(mStatusListener.getLastStatus(firstRequest));
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 1 /* requestedState */, 0 /* flags */);
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
assertNull(mStatusListener.getLastStatus(secondRequest));
mController.addRequest(secondRequest);
@@ -85,7 +87,7 @@ public final class OverrideRequestControllerTest {
@Test
public void addRequest_cancelActiveRequest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
mController.addRequest(firstRequest);
@@ -97,30 +99,90 @@ public final class OverrideRequestControllerTest {
}
@Test
+ public void addBaseStateRequest() {
+ OverrideRequest request = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ assertNull(mStatusListener.getLastStatus(request));
+
+ mController.addBaseStateRequest(request);
+ assertEquals(mStatusListener.getLastStatus(request).intValue(), STATUS_ACTIVE);
+ }
+
+ @Test
+ public void addBaseStateRequest_cancelExistingBaseStateRequestThroughNewRequest() {
+ OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ assertNull(mStatusListener.getLastStatus(firstRequest));
+
+ mController.addBaseStateRequest(firstRequest);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+
+ OverrideRequest secondRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+ assertNull(mStatusListener.getLastStatus(secondRequest));
+
+ mController.addBaseStateRequest(secondRequest);
+ assertEquals(mStatusListener.getLastStatus(secondRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ }
+
+ @Test
+ public void addBaseStateRequest_cancelActiveBaseStateRequest() {
+ OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
+
+ mController.addBaseStateRequest(firstRequest);
+
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+
+ mController.cancelBaseStateOverrideRequest();
+
+ assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ }
+
+ @Test
public void handleBaseStateChanged() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
0 /* requestedState */,
- DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */);
+ DeviceStateRequest.FLAG_CANCEL_WHEN_BASE_CHANGES /* flags */,
+ OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 0 /* requestedState */,
+ 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
- mController.handleBaseStateChanged();
+ mController.addBaseStateRequest(baseStateRequest);
+
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
+ mController.handleBaseStateChanged(1);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
}
@Test
public void handleProcessDied() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */,
+ 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ mController.addBaseStateRequest(baseStateRequest);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
mController.handleProcessDied(0);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
}
@Test
@@ -128,13 +190,20 @@ public final class OverrideRequestControllerTest {
mController.setStickyRequestsAllowed(true);
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 0 /* requestedState */, 0 /* flags */);
+ 0 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ mController.addBaseStateRequest(baseStateRequest);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
mController.handleProcessDied(0);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
mController.cancelStickyRequest();
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
@@ -143,22 +212,31 @@ public final class OverrideRequestControllerTest {
@Test
public void handleNewSupportedStates() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 1 /* requestedState */, 0 /* flags */);
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
+
+ OverrideRequest baseStateRequest = new OverrideRequest(new Binder(), 0 /* pid */,
+ 1 /* requestedState */,
+ 0 /* flags */, OVERRIDE_REQUEST_TYPE_BASE_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
- mController.handleNewSupportedStates(new int[]{ 0, 1 });
+ mController.addBaseStateRequest(baseStateRequest);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
+
+ mController.handleNewSupportedStates(new int[]{0, 1});
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_ACTIVE);
- mController.handleNewSupportedStates(new int[]{ 0 });
+ mController.handleNewSupportedStates(new int[]{0});
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_CANCELED);
+ assertEquals(mStatusListener.getLastStatus(baseStateRequest).intValue(), STATUS_CANCELED);
}
@Test
public void cancelOverrideRequestsTest() {
OverrideRequest firstRequest = new OverrideRequest(new Binder(), 0 /* pid */,
- 1 /* requestedState */, 0 /* flags */);
+ 1 /* requestedState */, 0 /* flags */, OVERRIDE_REQUEST_TYPE_EMULATED_STATE);
mController.addRequest(firstRequest);
assertEquals(mStatusListener.getLastStatus(firstRequest).intValue(), STATUS_ACTIVE);
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 9d82f1a90c77..fc2a4cf3a7d8 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -38,7 +38,6 @@ import android.hardware.SensorManager;
import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
import android.os.Handler;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -56,7 +55,6 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class AutomaticBrightnessControllerTest {
private static final float BRIGHTNESS_MIN_FLOAT = 0.0f;
@@ -82,6 +80,8 @@ public class AutomaticBrightnessControllerTest {
@Mock BrightnessMappingStrategy mIdleBrightnessMappingStrategy;
@Mock HysteresisLevels mAmbientBrightnessThresholds;
@Mock HysteresisLevels mScreenBrightnessThresholds;
+ @Mock HysteresisLevels mAmbientBrightnessThresholdsIdle;
+ @Mock HysteresisLevels mScreenBrightnessThresholdsIdle;
@Mock Handler mNoOpHandler;
@Mock HighBrightnessModeController mHbmController;
@Mock BrightnessThrottler mBrightnessThrottler;
@@ -129,6 +129,7 @@ public class AutomaticBrightnessControllerTest {
INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
+ mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle,
mContext, mHbmController, mBrightnessThrottler, mIdleBrightnessMappingStrategy,
AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG
);
@@ -314,8 +315,9 @@ public class AutomaticBrightnessControllerTest {
// Now let's do the same for idle mode
mController.switchToIdleMode();
- // Called once for init, and once when switching
- verify(mBrightnessMappingStrategy, times(2)).isForIdleMode();
+ // Called once for init, and once when switching,
+ // setAmbientLux() is called twice and once in updateAutoBrightness()
+ verify(mBrightnessMappingStrategy, times(5)).isForIdleMode();
// Ensure, after switching, original BMS is not used anymore
verifyNoMoreInteractions(mBrightnessMappingStrategy);
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index f352de4ea54e..89ff2c258c26 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -48,7 +48,7 @@ import java.util.Arrays;
@RunWith(AndroidJUnit4.class)
public class BrightnessMappingStrategyTest {
- private static final int[] LUX_LEVELS = {
+ private static final float[] LUX_LEVELS = {
0,
5,
20,
@@ -126,7 +126,8 @@ public class BrightnessMappingStrategyTest {
private static final int[] EMPTY_INT_ARRAY = new int[0];
private static final float MAXIMUM_GAMMA = 3.0f;
- private static final int[] GAMMA_CORRECTION_LUX = {
+
+ private static final float[] GAMMA_CORRECTION_LUX = {
0,
100,
1000,
@@ -155,7 +156,7 @@ public class BrightnessMappingStrategyTest {
@Test
public void testSimpleStrategyMappingAtControlPoints() {
- Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
@@ -170,7 +171,7 @@ public class BrightnessMappingStrategyTest {
@Test
public void testSimpleStrategyMappingBetweenControlPoints() {
- Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
@@ -179,13 +180,13 @@ public class BrightnessMappingStrategyTest {
final float backlight = simple.getBrightness(lux) * PowerManager.BRIGHTNESS_ON;
assertTrue("Desired brightness should be between adjacent control points.",
backlight > DISPLAY_LEVELS_BACKLIGHT[i - 1]
- && backlight < DISPLAY_LEVELS_BACKLIGHT[i]);
+ && backlight < DISPLAY_LEVELS_BACKLIGHT[i]);
}
}
@Test
public void testSimpleStrategyIgnoresNewConfiguration() {
- Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
@@ -200,7 +201,7 @@ public class BrightnessMappingStrategyTest {
@Test
public void testSimpleStrategyIgnoresNullConfiguration() {
- Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
+ Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
DisplayDeviceConfig ddc = createDdc();
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
@@ -214,8 +215,10 @@ public class BrightnessMappingStrategyTest {
@Test
public void testPhysicalStrategyMappingAtControlPoints() {
- Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
- DisplayDeviceConfig ddc = createDdc();
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT,
+ LUX_LEVELS, DISPLAY_LEVELS_NITS);
BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", physical);
for (int i = 0; i < LUX_LEVELS.length; i++) {
@@ -231,8 +234,9 @@ public class BrightnessMappingStrategyTest {
@Test
public void testPhysicalStrategyMappingBetweenControlPoints() {
- Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE,
+ LUX_LEVELS, DISPLAY_LEVELS_NITS);
BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", physical);
Spline brightnessToNits =
@@ -248,11 +252,12 @@ public class BrightnessMappingStrategyTest {
@Test
public void testPhysicalStrategyUsesNewConfigurations() {
- Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
- DisplayDeviceConfig ddc = createDdc();
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
- final float[] lux = { 0f, 1f };
+ final float[] lux = {0f, 1f};
final float[] nits = {
DISPLAY_RANGE_NITS[0],
DISPLAY_RANGE_NITS[DISPLAY_RANGE_NITS.length - 1]
@@ -273,8 +278,9 @@ public class BrightnessMappingStrategyTest {
@Test
public void testPhysicalStrategyRecalculateSplines() {
- Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS);
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length];
for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) {
@@ -304,28 +310,30 @@ public class BrightnessMappingStrategyTest {
@Test
public void testDefaultStrategyIsPhysical() {
- Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
- DISPLAY_LEVELS_NITS);
- DisplayDeviceConfig ddc = createDdc();
+ Resources res = createResources(DISPLAY_LEVELS_BACKLIGHT);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, DISPLAY_LEVELS_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy);
}
@Test
public void testNonStrictlyIncreasingLuxLevelsFails() {
- final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
+ final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length);
final int idx = lux.length / 2;
- int tmp = lux[idx];
- lux[idx] = lux[idx+1];
- lux[idx+1] = tmp;
- Resources res = createResources(lux, DISPLAY_LEVELS_NITS);
- DisplayDeviceConfig ddc = createDdc();
+ float tmp = lux[idx];
+ lux[idx] = lux[idx + 1];
+ lux[idx + 1] = tmp;
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux, DISPLAY_LEVELS_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
// And make sure we get the same result even if it's monotone but not increasing.
- lux[idx] = lux[idx+1];
- res = createResources(lux, DISPLAY_LEVELS_NITS);
+ lux[idx] = lux[idx + 1];
+ ddc = createDdc(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux,
+ DISPLAY_LEVELS_NITS);
strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
}
@@ -333,62 +341,64 @@ public class BrightnessMappingStrategyTest {
@Test
public void testDifferentNumberOfControlPointValuesFails() {
//Extra lux level
- final int[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length+1);
+ final float[] lux = Arrays.copyOf(LUX_LEVELS, LUX_LEVELS.length + 1);
// Make sure it's strictly increasing so that the only failure is the differing array
// lengths
lux[lux.length - 1] = lux[lux.length - 2] + 1;
- Resources res = createResources(lux, DISPLAY_LEVELS_NITS);
- DisplayDeviceConfig ddc = createDdc();
+ Resources res = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, lux, DISPLAY_LEVELS_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
- res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT);
+ res = createResources(DISPLAY_LEVELS_BACKLIGHT);
strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
// Extra backlight level
final int[] backlight = Arrays.copyOf(
- DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1);
+ DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length + 1);
backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
- res = createResources(LUX_LEVELS, backlight);
+ res = createResources(backlight);
+ ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, EMPTY_FLOAT_ARRAY);
strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
// Extra nits level
- final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1);
+ final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length + 1);
nits[nits.length - 1] = nits[nits.length - 2] + 1;
- res = createResources(LUX_LEVELS, nits);
+ res = createResources(EMPTY_INT_ARRAY);
+ ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, LUX_LEVELS, nits);
strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
}
@Test
public void testPhysicalStrategyRequiresNitsMapping() {
- Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
- DISPLAY_LEVELS_NITS);
+ Resources res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/);
DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY /*nitsRange*/);
BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(physical);
- res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
- DISPLAY_LEVELS_NITS);
+ res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/);
physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(physical);
- res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
- DISPLAY_LEVELS_NITS);
+ res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/);
physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(physical);
}
@Test
public void testStrategiesAdaptToUserDataPoint() {
- Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
- DISPLAY_LEVELS_NITS);
- DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
+ Resources res = createResources(EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE,
+ LUX_LEVELS, DISPLAY_LEVELS_NITS);
assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
- res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
+ res = createResources(DISPLAY_LEVELS_BACKLIGHT);
assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
}
@@ -468,42 +478,19 @@ public class BrightnessMappingStrategyTest {
assertEquals(minBrightness, strategy.getBrightness(LUX_LEVELS[0]), 0.0001f /*tolerance*/);
}
- private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight) {
- return createResources(luxLevels, brightnessLevelsBacklight,
- EMPTY_FLOAT_ARRAY /*brightnessLevelsNits*/);
- }
-
- private Resources createResources(int[] luxLevels, float[] brightnessLevelsNits) {
- return createResources(luxLevels, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
- brightnessLevelsNits);
+ private Resources createResources(int[] brightnessLevelsBacklight) {
+ return createResources(brightnessLevelsBacklight, EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY);
}
- private Resources createResourcesIdle(int[] luxLevels, float[] brightnessLevelsNits) {
- return createResources(EMPTY_INT_ARRAY, EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY,
- luxLevels, brightnessLevelsNits);
+ private Resources createResourcesIdle(int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) {
+ return createResources(EMPTY_INT_ARRAY,
+ luxLevelsIdle, brightnessLevelsNitsIdle);
}
- private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight,
- float[] brightnessLevelsNits) {
- return createResources(luxLevels, brightnessLevelsBacklight, brightnessLevelsNits,
- EMPTY_INT_ARRAY, EMPTY_FLOAT_ARRAY);
-
- }
-
- private Resources createResources(int[] luxLevels, int[] brightnessLevelsBacklight,
- float[] brightnessLevelsNits, int[] luxLevelsIdle, float[] brightnessLevelsNitsIdle) {
+ private Resources createResources(int[] brightnessLevelsBacklight, int[] luxLevelsIdle,
+ float[] brightnessLevelsNitsIdle) {
Resources mockResources = mock(Resources.class);
-
- // For historical reasons, the lux levels resource implicitly defines the first point as 0,
- // so we need to chop it off of the array the mock resource object returns.
- // Don't mock if these values are not set. If we try to use them, we will fail.
- if (luxLevels.length > 0) {
- int[] luxLevelsResource = Arrays.copyOfRange(luxLevels, 1, luxLevels.length);
- when(mockResources.getIntArray(
- com.android.internal.R.array.config_autoBrightnessLevels))
- .thenReturn(luxLevelsResource);
- }
if (luxLevelsIdle.length > 0) {
int[] luxLevelsIdleResource = Arrays.copyOfRange(luxLevelsIdle, 1,
luxLevelsIdle.length);
@@ -516,10 +503,6 @@ public class BrightnessMappingStrategyTest {
com.android.internal.R.array.config_autoBrightnessLcdBacklightValues))
.thenReturn(brightnessLevelsBacklight);
- TypedArray mockBrightnessLevelNits = createFloatTypedArray(brightnessLevelsNits);
- when(mockResources.obtainTypedArray(
- com.android.internal.R.array.config_autoBrightnessDisplayValuesNits))
- .thenReturn(mockBrightnessLevelNits);
TypedArray mockBrightnessLevelNitsIdle = createFloatTypedArray(brightnessLevelsNitsIdle);
when(mockResources.obtainTypedArray(
com.android.internal.R.array.config_autoBrightnessDisplayValuesNitsIdle))
@@ -549,6 +532,18 @@ public class BrightnessMappingStrategyTest {
DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class);
when(mockDdc.getNits()).thenReturn(nitsArray);
when(mockDdc.getBrightness()).thenReturn(backlightArray);
+ when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(LUX_LEVELS);
+ when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(EMPTY_FLOAT_ARRAY);
+ return mockDdc;
+ }
+
+ private DisplayDeviceConfig createDdc(float[] nitsArray, float[] backlightArray,
+ float[] luxLevelsFloat, float[] brightnessLevelsNits) {
+ DisplayDeviceConfig mockDdc = mock(DisplayDeviceConfig.class);
+ when(mockDdc.getNits()).thenReturn(nitsArray);
+ when(mockDdc.getBrightness()).thenReturn(backlightArray);
+ when(mockDdc.getAutoBrightnessBrighteningLevelsLux()).thenReturn(luxLevelsFloat);
+ when(mockDdc.getAutoBrightnessBrighteningLevelsNits()).thenReturn(brightnessLevelsNits);
return mockDdc;
}
@@ -590,8 +585,10 @@ public class BrightnessMappingStrategyTest {
final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
- Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
- DisplayDeviceConfig ddc = createDdc();
+ Resources resources = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX,
+ GAMMA_CORRECTION_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
mMockDwbc);
// Let's start with a validity check:
@@ -619,8 +616,10 @@ public class BrightnessMappingStrategyTest {
final float y1 = GAMMA_CORRECTION_SPLINE.interpolate(x1);
final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
- Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
- DisplayDeviceConfig ddc = createDdc();
+ Resources resources = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX,
+ GAMMA_CORRECTION_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
mMockDwbc);
// Validity check:
@@ -645,8 +644,10 @@ public class BrightnessMappingStrategyTest {
public void testGammaCorrectionExtremeChangeAtCenter() {
// Extreme changes (e.g. setting brightness to 0.0 or 1.0) can't be gamma corrected, so we
// just make sure the adjustment reflects the change.
- Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
- DisplayDeviceConfig ddc = createDdc();
+ Resources resources = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX,
+ GAMMA_CORRECTION_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
mMockDwbc);
assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */);
@@ -667,8 +668,10 @@ public class BrightnessMappingStrategyTest {
final float y0 = GAMMA_CORRECTION_SPLINE.interpolate(x0);
final float y2 = GAMMA_CORRECTION_SPLINE.interpolate(x2);
final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4);
- Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
- DisplayDeviceConfig ddc = createDdc();
+ Resources resources = createResources(EMPTY_INT_ARRAY);
+ DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS,
+ DISPLAY_LEVELS_RANGE_BACKLIGHT_FLOAT, GAMMA_CORRECTION_LUX,
+ GAMMA_CORRECTION_NITS);
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
mMockDwbc);
// Validity, as per tradition:
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
index 6a6cd6c914a2..800f60bec828 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -35,7 +35,6 @@ import android.os.PowerManager;
import android.os.Temperature;
import android.os.Temperature.ThrottlingStatus;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -57,7 +56,6 @@ import java.util.ArrayList;
import java.util.List;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class BrightnessThrottlerTest {
private static final float EPSILON = 0.000001f;
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
index 26a83a23de33..53d8de0c2bbb 100644
--- a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
@@ -24,7 +24,6 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.hardware.display.DisplayManagerInternal;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -39,7 +38,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class ColorFadeTest {
private static final int DISPLAY_ID = 123;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
index 66420ad4572e..04702c448152 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
@@ -27,7 +27,6 @@ import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -44,7 +43,6 @@ import java.nio.file.Files;
import java.nio.file.Path;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public final class DisplayDeviceConfigTest {
private DisplayDeviceConfig mDisplayDeviceConfig;
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 3eb1dea80c7f..3c7bb2ac51d6 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -53,12 +53,10 @@ import android.hardware.display.DisplayedContentSamplingAttributes;
import android.hardware.display.IDisplayManagerCallback;
import android.hardware.display.IVirtualDisplayCallback;
import android.hardware.display.VirtualDisplayConfig;
-import android.hardware.input.InputManagerInternal;
import android.os.Handler;
import android.os.IBinder;
import android.os.MessageQueue;
import android.os.Process;
-import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayCutout;
import android.view.DisplayEventReceiver;
@@ -78,6 +76,7 @@ import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayManagerService.SyncRoot;
+import com.android.server.input.InputManagerInternal;
import com.android.server.lights.LightsManager;
import com.android.server.sensors.SensorManagerInternal;
import com.android.server.wm.WindowManagerInternal;
@@ -106,7 +105,6 @@ import java.util.concurrent.TimeUnit;
import java.util.stream.LongStream;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class DisplayManagerServiceTest {
private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 53fa3e2db376..a1e5ce74014b 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -22,14 +22,10 @@ import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_HDR;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
-
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
-import static com.android.server.display.AutomaticBrightnessController
- .AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
-
+import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
import static com.android.server.display.DisplayDeviceConfig.HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
-
import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
import static org.junit.Assert.assertEquals;
@@ -51,7 +47,6 @@ import android.os.PowerManager;
import android.os.Temperature;
import android.os.Temperature.ThrottlingStatus;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import android.test.mock.MockContentResolver;
import android.util.MathUtils;
@@ -76,7 +71,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class HighBrightnessModeControllerTest {
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
index cc68ba88f76e..0b33c30fd7e8 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -45,7 +45,6 @@ import android.os.IThermalService;
import android.os.PowerManager;
import android.os.Process;
import android.os.test.TestLooper;
-import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayAddress;
import android.view.DisplayInfo;
@@ -67,7 +66,6 @@ import java.util.Arrays;
import java.util.Set;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public class LogicalDisplayMapperTest {
private static int sUniqueTestDisplayId = 0;
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
index b0738fdb78d0..5a43530d44dd 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
@@ -26,7 +26,6 @@ import static org.mockito.Mockito.when;
import android.app.PropertyInvalidatedCache;
import android.graphics.Point;
-import android.platform.test.annotations.Presubmit;
import android.view.DisplayInfo;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -40,7 +39,6 @@ import java.io.InputStream;
import java.io.OutputStream;
@SmallTest
-@Presubmit
public class LogicalDisplayTest {
private static final int DISPLAY_ID = 0;
private static final int LAYER_STACK = 0;
diff --git a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
index 9f1a209d2ee1..92d8abd4f173 100644
--- a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
+++ b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
@@ -3,18 +3,10 @@
{
"name": "FrameworksServicesTests",
"options": [
- {
- "include-filter": "com.android.server.display."
- },
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- }
+ {"include-filter": "com.android.server.display"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "org.junit.Ignore"}
]
}
]
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
index f69c5c2df6ce..fabf535b729a 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
@@ -19,7 +19,6 @@ package com.android.server.display.brightness;
import static org.junit.Assert.assertEquals;
import android.hardware.display.BrightnessInfo;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -29,7 +28,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public final class BrightnessEventTest {
private BrightnessEvent mBrightnessEvent;
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
index ffc2e0dfe26c..57aa61a0ebd2 100644
--- a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
@@ -18,7 +18,6 @@ package com.android.server.display.brightness;
import static org.junit.Assert.assertEquals;
-import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -28,7 +27,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
-@Presubmit
@RunWith(AndroidJUnit4.class)
public final class BrightnessReasonTest {
private BrightnessReason mBrightnessReason;
diff --git a/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
index 4ef156e239d3..e0bef1a83821 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
@@ -18,21 +18,26 @@ package com.android.server.display.color;
import static com.google.common.truth.Truth.assertWithMessage;
-import androidx.test.InstrumentationRegistry;
+import static org.mockito.Mockito.mock;
-import java.lang.System;
-import java.util.Arrays;
+import android.hardware.display.DisplayManagerInternal;
+
+import androidx.test.InstrumentationRegistry;
import org.junit.Before;
import org.junit.Test;
+import java.util.Arrays;
+
public class DisplayWhiteBalanceTintControllerTest {
private DisplayWhiteBalanceTintController mDisplayWhiteBalanceTintController;
@Before
public void setUp() {
- mDisplayWhiteBalanceTintController = new DisplayWhiteBalanceTintController();
+ DisplayManagerInternal displayManagerInternal = mock(DisplayManagerInternal.class);
+ mDisplayWhiteBalanceTintController =
+ new DisplayWhiteBalanceTintController(displayManagerInternal);
mDisplayWhiteBalanceTintController.setUp(InstrumentationRegistry.getContext(), true);
mDisplayWhiteBalanceTintController.setActivated(true);
}
diff --git a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
index 6e422fa7af7f..40757182890c 100644
--- a/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/BatteryControllerTests.kt
@@ -20,16 +20,20 @@ import android.content.Context
import android.content.ContextWrapper
import android.hardware.BatteryState.STATUS_CHARGING
import android.hardware.BatteryState.STATUS_FULL
+import android.hardware.BatteryState.STATUS_UNKNOWN
import android.hardware.input.IInputDeviceBatteryListener
+import android.hardware.input.IInputDevicesChangedListener
import android.hardware.input.IInputManager
-import android.hardware.input.InputDeviceCountryCode
import android.hardware.input.InputManager
import android.os.Binder
import android.os.IBinder
+import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.view.InputDevice
import androidx.test.InstrumentationRegistry
+import com.android.server.input.BatteryController.UEventManager
import org.junit.After
+import org.junit.Assert.assertEquals
import org.junit.Assert.fail
import org.junit.Before
import org.junit.Rule
@@ -39,15 +43,27 @@ import org.mockito.ArgumentMatchers.notNull
import org.mockito.Mock
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.eq
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
+private fun createInputDevice(deviceId: Int, hasBattery: Boolean = true): InputDevice =
+ InputDevice.Builder()
+ .setId(deviceId)
+ .setName("Device $deviceId")
+ .setDescriptor("descriptor $deviceId")
+ .setExternal(true)
+ .setHasBattery(hasBattery)
+ .setGeneration(0)
+ .build()
+
/**
* Tests for {@link InputDeviceBatteryController}.
*
@@ -60,6 +76,7 @@ class BatteryControllerTests {
const val PID = 42
const val DEVICE_ID = 13
const val SECOND_DEVICE_ID = 11
+ const val TIMESTAMP = 123456789L
}
@get:Rule
@@ -69,13 +86,19 @@ class BatteryControllerTests {
private lateinit var native: NativeInputManagerService
@Mock
private lateinit var iInputManager: IInputManager
+ @Mock
+ private lateinit var uEventManager: UEventManager
private lateinit var batteryController: BatteryController
private lateinit var context: Context
+ private lateinit var testLooper: TestLooper
+ private lateinit var devicesChangedListener: IInputDevicesChangedListener
+ private val deviceGenerationMap = mutableMapOf<Int /*deviceId*/, Int /*generation*/>()
@Before
fun setup() {
context = spy(ContextWrapper(InstrumentationRegistry.getContext()))
+ testLooper = TestLooper()
val inputManager = InputManager.resetInstance(iInputManager)
`when`(context.getSystemService(eq(Context.INPUT_SERVICE))).thenReturn(inputManager)
`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID, SECOND_DEVICE_ID))
@@ -83,17 +106,18 @@ class BatteryControllerTests {
`when`(iInputManager.getInputDevice(SECOND_DEVICE_ID))
.thenReturn(createInputDevice(SECOND_DEVICE_ID))
- batteryController = BatteryController(context, native)
+ batteryController = BatteryController(context, native, testLooper.looper, uEventManager)
+ batteryController.systemRunning()
+ val listenerCaptor = ArgumentCaptor.forClass(IInputDevicesChangedListener::class.java)
+ verify(iInputManager).registerInputDevicesChangedListener(listenerCaptor.capture())
+ devicesChangedListener = listenerCaptor.value
}
- private fun createInputDevice(deviceId: Int): InputDevice =
- InputDevice.Builder()
- .setId(deviceId)
- .setName("Device $deviceId")
- .setDescriptor("descriptor $deviceId")
- .setExternal(true)
- .setHasBattery(true)
- .build()
+ private fun notifyDeviceChanged(deviceId: Int) {
+ deviceGenerationMap[deviceId] = deviceGenerationMap[deviceId]?.plus(1) ?: 1
+ val list = deviceGenerationMap.flatMap { listOf(it.key, it.value) }
+ devicesChangedListener.onInputDevicesChanged(list.toIntArray())
+ }
@After
fun tearDown() {
@@ -169,4 +193,129 @@ class BatteryControllerTests {
verify(listener).onBatteryStateChanged(eq(SECOND_DEVICE_ID), eq(true /*isPresent*/),
eq(STATUS_CHARGING), eq(0.78f), anyLong())
}
+
+ @Test
+ fun testListenersNotifiedOnUEventNotification() {
+ `when`(native.getBatteryDevicePath(DEVICE_ID)).thenReturn("/test/device1")
+ `when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
+ val listener = createMockListener()
+ val uEventListener = ArgumentCaptor.forClass(UEventManager.UEventListener::class.java)
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
+ verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
+ eq(STATUS_CHARGING), eq(0.78f), anyLong())
+
+ // If the battery state has changed when an UEvent is sent, the listeners are notified.
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
+ uEventListener.value!!.onUEvent(TIMESTAMP)
+ verify(listener).onBatteryStateChanged(DEVICE_ID, true /*isPresent*/, STATUS_CHARGING,
+ 0.80f, TIMESTAMP)
+
+ // If the battery state has not changed when an UEvent is sent, the listeners are not
+ // notified.
+ clearInvocations(listener)
+ uEventListener.value!!.onUEvent(TIMESTAMP + 1)
+ verifyNoMoreInteractions(listener)
+
+ batteryController.unregisterBatteryListener(DEVICE_ID, listener, PID)
+ verify(uEventManager).removeListener(uEventListener.capture())
+ assertEquals("The same observer must be registered and unregistered",
+ uEventListener.allValues[0], uEventListener.allValues[1])
+ }
+
+ @Test
+ fun testBatteryPresenceChanged() {
+ `when`(native.getBatteryDevicePath(DEVICE_ID)).thenReturn("/test/device1")
+ `when`(native.getBatteryStatus(DEVICE_ID)).thenReturn(STATUS_CHARGING)
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
+ val listener = createMockListener()
+ val uEventListener = ArgumentCaptor.forClass(UEventManager.UEventListener::class.java)
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(uEventManager).addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
+ verify(listener).onBatteryStateChanged(
+ eq(DEVICE_ID), eq(true /*isPresent*/),
+ eq(STATUS_CHARGING), eq(0.78f), anyLong()
+ )
+
+ // If the battery presence for the InputDevice changes, the listener is notified.
+ `when`(iInputManager.getInputDevice(DEVICE_ID))
+ .thenReturn(createInputDevice(DEVICE_ID, hasBattery = false))
+ notifyDeviceChanged(DEVICE_ID)
+ testLooper.dispatchNext()
+ verify(listener).onBatteryStateChanged(
+ eq(DEVICE_ID), eq(false /*isPresent*/),
+ eq(STATUS_UNKNOWN), eq(Float.NaN), anyLong()
+ )
+ // Since the battery is no longer present, the UEventListener should be removed.
+ verify(uEventManager).removeListener(uEventListener.value)
+
+ // If the battery becomes present again, the listener is notified.
+ `when`(iInputManager.getInputDevice(DEVICE_ID))
+ .thenReturn(createInputDevice(DEVICE_ID, hasBattery = true))
+ notifyDeviceChanged(DEVICE_ID)
+ testLooper.dispatchNext()
+ verify(listener, times(2)).onBatteryStateChanged(
+ eq(DEVICE_ID), eq(true /*isPresent*/),
+ eq(STATUS_CHARGING), eq(0.78f), anyLong()
+ )
+ // Ensure that a new UEventListener was added.
+ verify(uEventManager, times(2))
+ .addListener(uEventListener.capture(), eq("DEVPATH=/test/device1"))
+ }
+
+ fun testStartPollingWhenListenerIsRegistered() {
+ val listener = createMockListener()
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/), anyInt(),
+ eq(0.78f), anyLong())
+
+ // Assume there is a change in the battery state. Ensure the listener is not notified
+ // while the polling period has not elapsed.
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
+ testLooper.moveTimeForward(1)
+ testLooper.dispatchAll()
+ verify(listener, never()).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
+ anyInt(), eq(0.80f), anyLong())
+
+ // Move the time forward so that the polling period has elapsed.
+ // The listener should be notified.
+ testLooper.moveTimeForward(BatteryController.POLLING_PERIOD_MILLIS - 1)
+ testLooper.dispatchNext()
+ verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/), anyInt(),
+ eq(0.80f), anyLong())
+ }
+
+ @Test
+ fun testNoPollingWhenTheDeviceIsNotInteractive() {
+ batteryController.onInteractiveChanged(false /*interactive*/)
+
+ val listener = createMockListener()
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(78)
+ batteryController.registerBatteryListener(DEVICE_ID, listener, PID)
+ verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/), anyInt(),
+ eq(0.78f), anyLong())
+
+ // The battery state changed, but we should not be polling for battery changes when the
+ // device is not interactive.
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(80)
+ testLooper.moveTimeForward(BatteryController.POLLING_PERIOD_MILLIS)
+ testLooper.dispatchAll()
+ verify(listener, never()).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
+ anyInt(), eq(0.80f), anyLong())
+
+ // The device is now interactive. Battery state polling begins immediately.
+ batteryController.onInteractiveChanged(true /*interactive*/)
+ testLooper.dispatchNext()
+ verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
+ anyInt(), eq(0.80f), anyLong())
+
+ // Ensure that we continue to poll for battery changes.
+ `when`(native.getBatteryCapacity(DEVICE_ID)).thenReturn(90)
+ testLooper.moveTimeForward(BatteryController.POLLING_PERIOD_MILLIS)
+ testLooper.dispatchNext()
+ verify(listener).onBatteryStateChanged(eq(DEVICE_ID), eq(true /*isPresent*/),
+ anyInt(), eq(0.90f), anyLong())
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
index 844f5d4cd3eb..e390bccf41d8 100644
--- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
@@ -19,13 +19,14 @@ package com.android.server.input
import android.content.Context
import android.content.ContextWrapper
import android.hardware.display.DisplayViewport
-import android.hardware.input.InputManagerInternal
import android.os.IInputConstants
import android.os.test.TestLooper
import android.platform.test.annotations.Presubmit
import android.view.Display
import android.view.PointerIcon
import androidx.test.InstrumentationRegistry
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
@@ -35,7 +36,6 @@ import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.never
@@ -43,9 +43,8 @@ import org.mockito.Mockito.spy
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.`when`
import org.mockito.junit.MockitoJUnit
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
/**
* Tests for {@link InputManagerService}.
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ConcurrentLinkedEvictingDequeTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ConcurrentLinkedEvictingDequeTest.java
new file mode 100644
index 000000000000..9169a0df4a64
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ConcurrentLinkedEvictingDequeTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.contexthub;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class ConcurrentLinkedEvictingDequeTest {
+ @Test
+ public void testMaxSize() {
+ int maxSize = 20;
+ ConcurrentLinkedEvictingDeque<Integer> deque =
+ new ConcurrentLinkedEvictingDeque<Integer>(maxSize);
+
+ // add items to the queue
+ for (int i = 0; i < maxSize; i++) {
+ deque.add(i);
+ }
+
+ // test the max size
+ deque.add(maxSize);
+ assertThat(deque.peek() == 0).isFalse();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEventLoggerTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEventLoggerTest.java
new file mode 100644
index 000000000000..8863d2796acf
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEventLoggerTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location.contexthub;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.location.NanoAppMessage;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class ContextHubEventLoggerTest {
+ private static final ContextHubEventLogger sInstance = ContextHubEventLogger.getInstance();
+
+ @Test
+ public void testLogNanoappLoad() {
+ ContextHubEventLogger.NanoappLoadEvent[] events =
+ new ContextHubEventLogger.NanoappLoadEvent[] {
+ new ContextHubEventLogger.NanoappLoadEvent(0, -1, 42, -34, 100, false),
+ new ContextHubEventLogger.NanoappLoadEvent(0, 0, 123, 321, 001, true)
+ };
+ String[] eventStrings = generateEventDumpStrings(events);
+
+ // log events and test sInstance.toString() contains event details
+ sInstance.clear();
+ sInstance.logNanoappLoad(-1, 42, -34, 100, false);
+ sInstance.logNanoappLoad(0, 123, 321, 001, true);
+ String sInstanceDump = sInstance.toString();
+ for (String eventString: eventStrings) {
+ assertThat(eventString.length() > 0).isTrue();
+ assertThat(sInstanceDump.contains(eventString)).isTrue();
+ }
+ }
+
+ @Test
+ public void testLogNanoappUnload() {
+ ContextHubEventLogger.NanoappUnloadEvent[] events =
+ new ContextHubEventLogger.NanoappUnloadEvent[] {
+ new ContextHubEventLogger.NanoappUnloadEvent(0, -1, 47, false),
+ new ContextHubEventLogger.NanoappUnloadEvent(0, 1, 0xFFFFFFFF, true)
+ };
+ String[] eventStrings = generateEventDumpStrings(events);
+
+ // log events and test sInstance.toString() contains event details
+ sInstance.clear();
+ sInstance.logNanoappUnload(-1, 47, false);
+ sInstance.logNanoappUnload(1, 0xFFFFFFFF, true);
+ String sInstanceDump = sInstance.toString();
+ for (String eventString: eventStrings) {
+ assertThat(eventString.length() > 0).isTrue();
+ assertThat(sInstanceDump.contains(eventString)).isTrue();
+ }
+ }
+
+ @Test
+ public void testLogMessageFromNanoapp() {
+ NanoAppMessage message1 = NanoAppMessage.createMessageFromNanoApp(1, 0,
+ new byte[] {0x00, 0x11, 0x22, 0x33}, false);
+ NanoAppMessage message2 = NanoAppMessage.createMessageFromNanoApp(0, 1,
+ new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF}, true);
+ ContextHubEventLogger.NanoappMessageEvent[] events =
+ new ContextHubEventLogger.NanoappMessageEvent[] {
+ new ContextHubEventLogger.NanoappMessageEvent(8, -123, message1, false),
+ new ContextHubEventLogger.NanoappMessageEvent(9, 321, message2, true)
+ };
+ String[] eventStrings = generateEventDumpStrings(events);
+
+ // log events and test sInstance.toString() contains event details
+ sInstance.clear();
+ sInstance.logMessageFromNanoapp(-123, message1, false);
+ sInstance.logMessageFromNanoapp(321, message2, true);
+ String sInstanceDump = sInstance.toString();
+ for (String eventString: eventStrings) {
+ assertThat(eventString.length() > 0).isTrue();
+ assertThat(sInstanceDump.contains(eventString)).isTrue();
+ }
+ }
+
+ @Test
+ public void testLogMessageToNanoapp() {
+ NanoAppMessage message1 = NanoAppMessage.createMessageToNanoApp(1, 0,
+ new byte[] {0x00, 0x11, 0x22, 0x33});
+ NanoAppMessage message2 = NanoAppMessage.createMessageToNanoApp(0, 1,
+ new byte[] {(byte) 0xDE, (byte) 0xAD, (byte) 0xBE, (byte) 0xEF});
+ ContextHubEventLogger.NanoappMessageEvent[] events =
+ new ContextHubEventLogger.NanoappMessageEvent[] {
+ new ContextHubEventLogger.NanoappMessageEvent(23, 888, message1, true),
+ new ContextHubEventLogger.NanoappMessageEvent(34, 999, message2, false)
+ };
+ String[] eventStrings = generateEventDumpStrings(events);
+
+ // log events and test sInstance.toString() contains event details
+ sInstance.clear();
+ sInstance.logMessageToNanoapp(888, message1, true);
+ sInstance.logMessageToNanoapp(999, message2, false);
+ String sInstanceDump = sInstance.toString();
+ for (String eventString: eventStrings) {
+ assertThat(eventString.length() > 0).isTrue();
+ assertThat(sInstanceDump.contains(eventString)).isTrue();
+ }
+ }
+
+ @Test
+ public void testLogContextHubRestart() {
+ ContextHubEventLogger.ContextHubRestartEvent[] events =
+ new ContextHubEventLogger.ContextHubRestartEvent[] {
+ new ContextHubEventLogger.ContextHubRestartEvent(0, 1),
+ new ContextHubEventLogger.ContextHubRestartEvent(1, 2)
+ };
+ String[] eventStrings = generateEventDumpStrings(events);
+
+ // log events and test sInstance.toString() contains event details
+ sInstance.clear();
+ sInstance.logContextHubRestart(1);
+ sInstance.logContextHubRestart(2);
+ String sInstanceDump = sInstance.toString();
+ for (String eventString: eventStrings) {
+ assertThat(eventString.length() > 0).isTrue();
+ assertThat(sInstanceDump.contains(eventString)).isTrue();
+ }
+ }
+
+ /**
+ * Generates the part of the event's toString() method that should be contained in the dump
+ * output (everything without the timestamp).
+ *
+ * @param events the events
+ * @return the string representation of the events
+ */
+ private String[] generateEventDumpStrings(ContextHubEventLogger.ContextHubEventBase[] events) {
+ return (String[]) Arrays.stream(events)
+ .map(event -> event.toString().split(event.getClass().getSimpleName(), 2)[1])
+ .toArray(String[]::new);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java
index 6a27f391ac5b..2cd5314e961f 100644
--- a/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/logcat/LogcatManagerServiceTest.java
@@ -77,7 +77,7 @@ public class LogcatManagerServiceTest {
private ILogd mLogdMock;
private LogcatManagerService mService;
- private LogcatManagerService.LogcatManagerServiceInternal mLocalService;
+ private LogcatManagerService.LogAccessDialogCallback mDialogCallback;
private ContextWrapper mContextSpy;
private OffsettableClock mClock;
private TestLooper mTestLooper;
@@ -118,7 +118,7 @@ public class LogcatManagerServiceTest {
return mLogdMock;
}
});
- mLocalService = mService.getLocalService();
+ mDialogCallback = mService.getDialogCallback();
mService.onStart();
}
@@ -207,7 +207,7 @@ public class LogcatManagerServiceTest {
mTestLooper.dispatchAll();
verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM));
- mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
+ mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
mTestLooper.dispatchAll();
verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1);
@@ -222,7 +222,7 @@ public class LogcatManagerServiceTest {
mTestLooper.dispatchAll();
verify(mContextSpy, times(1)).startActivityAsUser(any(), eq(UserHandle.SYSTEM));
- mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
+ mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
mTestLooper.dispatchAll();
verify(mLogdMock, never()).approve(APP1_UID, APP1_GID, APP1_PID, FD1);
@@ -240,7 +240,7 @@ public class LogcatManagerServiceTest {
verify(mLogdMock, never()).approve(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt());
verify(mLogdMock, never()).decline(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt());
- mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
+ mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
mTestLooper.dispatchAll();
verify(mLogdMock, times(1)).approve(APP1_UID, APP1_GID, APP1_PID, FD1);
@@ -260,7 +260,7 @@ public class LogcatManagerServiceTest {
verify(mLogdMock, never()).approve(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt());
verify(mLogdMock, never()).decline(eq(APP1_UID), eq(APP1_GID), eq(APP1_PID), anyInt());
- mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
+ mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
mTestLooper.dispatchAll();
verify(mLogdMock, times(1)).decline(APP1_UID, APP1_GID, APP1_PID, FD1);
@@ -275,7 +275,7 @@ public class LogcatManagerServiceTest {
ActivityManager.PROCESS_STATE_TOP);
mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1);
mTestLooper.dispatchAll();
- mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
+ mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
mTestLooper.dispatchAll();
mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2);
@@ -293,7 +293,7 @@ public class LogcatManagerServiceTest {
ActivityManager.PROCESS_STATE_TOP);
mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1);
mTestLooper.dispatchAll();
- mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
+ mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
mTestLooper.dispatchAll();
mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD2);
@@ -313,7 +313,7 @@ public class LogcatManagerServiceTest {
ActivityManager.PROCESS_STATE_TOP);
mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1);
mTestLooper.dispatchAll();
- mLocalService.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
+ mDialogCallback.approveAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
mTestLooper.dispatchAll();
mService.getBinderService().startThread(APP2_UID, APP2_GID, APP2_PID, FD2);
@@ -330,7 +330,7 @@ public class LogcatManagerServiceTest {
ActivityManager.PROCESS_STATE_TOP);
mService.getBinderService().startThread(APP1_UID, APP1_GID, APP1_PID, FD1);
mTestLooper.dispatchAll();
- mLocalService.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
+ mDialogCallback.declineAccessForClient(APP1_UID, APP1_PACKAGE_NAME);
mTestLooper.dispatchAll();
advanceTime(LogcatManagerService.STATUS_EXPIRATION_TIMEOUT_MILLIS);
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 67eeb4eafb76..68310f476139 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -1011,10 +1011,10 @@ public class PackageParserTest {
.addUsesPermission(new ParsedUsesPermissionImpl("foo7", 0))
.addImplicitPermission("foo25")
.addProtectedBroadcast("foo8")
- .setSdkLibName("sdk12")
+ .setSdkLibraryName("sdk12")
.setSdkLibVersionMajor(42)
.addUsesSdkLibrary("sdk23", 200, new String[]{"digest2"})
- .setStaticSharedLibName("foo23")
+ .setStaticSharedLibraryName("foo23")
.setStaticSharedLibVersion(100)
.addUsesStaticLibrary("foo23", 100, new String[]{"digest"})
.addLibraryName("foo10")
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 084f4f1684ee..6f3249e3ec4b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ScanTests.java
@@ -241,7 +241,7 @@ public class ScanTests {
@Test
public void installSdkLibrary() throws Exception {
final ParsedPackage pkg = ((ParsedPackage) createBasicPackage("ogl.sdk_123")
- .setSdkLibName("ogl.sdk")
+ .setSdkLibraryName("ogl.sdk")
.setSdkLibVersionMajor(123)
.hideAsParsed())
.setPackageName("ogl.sdk_123")
@@ -272,7 +272,7 @@ public class ScanTests {
@Test
public void installStaticSharedLibrary() throws Exception {
final ParsedPackage pkg = ((ParsedPackage) createBasicPackage("static.lib.pkg")
- .setStaticSharedLibName("static.lib")
+ .setStaticSharedLibraryName("static.lib")
.setStaticSharedLibVersion(123L)
.hideAsParsed())
.setPackageName("static.lib.pkg.123")
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index e04edc6d7db2..96707fde8edb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -216,6 +216,25 @@ public class UserManagerServiceTest {
assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
}
+ @Test
+ public void assertIsUserSwitcherEnabled() throws Exception {
+ int userId = ActivityManager.getCurrentUser();
+ setMaxSupportedUsers(8);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(true, userId)).isTrue();
+
+ setUserSwitch(false);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(true, userId)).isFalse();
+
+ setUserSwitch(true);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(false, userId)).isTrue();
+
+ mUserManagerService.setUserRestriction(UserManager.DISALLOW_ADD_USER, true, userId);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(false, userId)).isFalse();
+
+ mUserManagerService.setUserRestriction(UserManager.DISALLOW_ADD_USER, false, userId);
+ setMaxSupportedUsers(1);
+ assertThat(mUserManagerService.isUserSwitcherEnabled(true, userId)).isFalse();
+ }
@Test
public void assertIsUserSwitcherEnabledOnShowMultiuserUI() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
index 90b19a450f48..04ba7d3df38d 100644
--- a/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/policy/DeviceStateProviderImplTest.java
@@ -35,13 +35,13 @@ import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorManager;
-import android.hardware.input.InputManagerInternal;
import androidx.annotation.NonNull;
import com.android.server.LocalServices;
import com.android.server.devicestate.DeviceState;
import com.android.server.devicestate.DeviceStateProvider;
+import com.android.server.input.InputManagerInternal;
import org.junit.After;
import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index d5b923a81e4a..4100ff1ea977 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -148,6 +148,25 @@ public class BatteryStatsHistoryTest {
}
@Test
+ public void testAtraceInstantEvent() {
+ mHistory.forceRecordAllHistory();
+
+ InOrder inOrder = Mockito.inOrder(mTracer);
+ Mockito.when(mTracer.tracingEnabled()).thenReturn(true);
+
+ mHistory.recordEvent(mClock.elapsedRealtime(), mClock.uptimeMillis(),
+ HistoryItem.EVENT_WAKEUP_AP, "", 1234);
+ mHistory.recordEvent(mClock.elapsedRealtime(), mClock.uptimeMillis(),
+ HistoryItem.EVENT_JOB_START, "jobname", 2468);
+ mHistory.recordEvent(mClock.elapsedRealtime(), mClock.uptimeMillis(),
+ HistoryItem.EVENT_JOB_FINISH, "jobname", 2468);
+
+ inOrder.verify(mTracer).traceInstantEvent("battery_stats.wakeupap", "wakeupap=1234:\"\"");
+ inOrder.verify(mTracer).traceInstantEvent("battery_stats.job", "+job=2468:\"jobname\"");
+ inOrder.verify(mTracer).traceInstantEvent("battery_stats.job", "-job=2468:\"jobname\"");
+ }
+
+ @Test
public void testConstruct() {
createActiveFile(mHistory);
verifyFileNumbers(mHistory, Arrays.asList(0));
diff --git a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
index 99a12c2c4ff3..508e7b0f5918 100644
--- a/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/servicestests/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -161,13 +161,8 @@ public class VibrationSettingsTest {
return null;
}).when(mVirtualDeviceManagerInternalMock).registerAppsOnVirtualDeviceListener(any());
- LocalServices.removeServiceForTest(PowerManagerInternal.class);
- LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
- LocalServices.removeServiceForTest(PackageManagerInternal.class);
- LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
- LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
- LocalServices.addService(VirtualDeviceManagerInternal.class,
- mVirtualDeviceManagerInternalMock);
+ removeServicesForTest();
+ addServicesForTest();
setDefaultIntensity(VIBRATION_INTENSITY_MEDIUM);
mAudioManager = mContextSpy.getSystemService(AudioManager.class);
@@ -185,9 +180,34 @@ public class VibrationSettingsTest {
mVibrationSettings.onSystemReady();
}
+ private void removeServicesForTest() {
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.removeServiceForTest(VirtualDeviceManagerInternal.class);
+ }
+
+ private void addServicesForTest() {
+ LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
+ LocalServices.addService(VirtualDeviceManagerInternal.class,
+ mVirtualDeviceManagerInternalMock);
+ }
+
@After
public void tearDown() throws Exception {
- LocalServices.removeServiceForTest(PowerManagerInternal.class);
+ removeServicesForTest();
+ }
+
+ @Test
+ public void create_withOnlyRequiredSystemServices() {
+ // The only core services that we depend on are PowerManager and PackageManager
+ removeServicesForTest();
+ LocalServices.addService(PowerManagerInternal.class, mPowerManagerInternalMock);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternalMock);
+
+ VibrationSettings minimalVibrationSettings = new VibrationSettings(mContextSpy,
+ new Handler(mTestLooper.getLooper()), mVibrationConfigMock);
+ minimalVibrationSettings.onSystemReady();
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 49879efe4b51..798604306b43 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -1704,8 +1704,8 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
- public void testInfoIsPermittedForProfile_notAllowed() {
- when(mUserProfiles.canProfileUseBoundServices(anyInt())).thenReturn(false);
+ public void testInfoIsPermittedForProfile_notProfile() {
+ when(mUserProfiles.isProfileUser(anyInt())).thenReturn(false);
IInterface service = mock(IInterface.class);
when(service.asBinder()).thenReturn(mock(IBinder.class));
@@ -1714,12 +1714,12 @@ public class ManagedServicesTest extends UiServiceTestCase {
services.registerSystemService(service, null, 10, 1000);
ManagedServices.ManagedServiceInfo info = services.checkServiceTokenLocked(service);
- assertFalse(info.isPermittedForProfile(0));
+ assertTrue(info.isPermittedForProfile(0));
}
@Test
- public void testInfoIsPermittedForProfile_allows() {
- when(mUserProfiles.canProfileUseBoundServices(anyInt())).thenReturn(true);
+ public void testInfoIsPermittedForProfile_profileAndDpmAllows() {
+ when(mUserProfiles.isProfileUser(anyInt())).thenReturn(true);
when(mDpm.isNotificationListenerServicePermitted(anyString(), anyInt())).thenReturn(true);
IInterface service = mock(IInterface.class);
@@ -1734,6 +1734,22 @@ public class ManagedServicesTest extends UiServiceTestCase {
}
@Test
+ public void testInfoIsPermittedForProfile_profileAndDpmDenies() {
+ when(mUserProfiles.isProfileUser(anyInt())).thenReturn(true);
+ when(mDpm.isNotificationListenerServicePermitted(anyString(), anyInt())).thenReturn(false);
+
+ IInterface service = mock(IInterface.class);
+ when(service.asBinder()).thenReturn(mock(IBinder.class));
+ ManagedServices services = new TestManagedServices(getContext(), mLock, mUserProfiles,
+ mIpm, APPROVAL_BY_PACKAGE);
+ services.registerSystemService(service, null, 10, 1000);
+ ManagedServices.ManagedServiceInfo info = services.checkServiceTokenLocked(service);
+ info.component = new ComponentName("a","b");
+
+ assertFalse(info.isPermittedForProfile(0));
+ }
+
+ @Test
public void testUserProfiles_canProfileUseBoundServices_managedProfile() {
List<UserInfo> users = new ArrayList<>();
UserInfo profile = new UserInfo(ActivityManager.getCurrentUser(), "current", 0);
@@ -1750,9 +1766,9 @@ public class ManagedServicesTest extends UiServiceTestCase {
ManagedServices.UserProfiles profiles = new ManagedServices.UserProfiles();
profiles.updateCache(mContext);
- assertTrue(profiles.canProfileUseBoundServices(ActivityManager.getCurrentUser()));
- assertFalse(profiles.canProfileUseBoundServices(12));
- assertFalse(profiles.canProfileUseBoundServices(13));
+ assertFalse(profiles.isProfileUser(ActivityManager.getCurrentUser()));
+ assertTrue(profiles.isProfileUser(12));
+ assertTrue(profiles.isProfileUser(13));
}
private void resetComponentsAndPackages() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index 68551d9813d3..1e945776cf40 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -45,19 +45,16 @@ import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.ComponentName;
-import android.content.Context;
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.content.pm.VersionedPackage;
import android.os.Bundle;
-import android.os.IBinder;
-import android.os.IInterface;
import android.os.UserHandle;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
-import android.service.notification.NotificationStats;
import android.service.notification.NotificationRankingUpdate;
+import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.testing.TestableContext;
import android.util.ArraySet;
@@ -408,64 +405,113 @@ public class NotificationListenersTest extends UiServiceTestCase {
@Test
public void testNotifyPostedLockedInLockdownMode() {
- NotificationRecord r = mock(NotificationRecord.class);
- NotificationRecord old = mock(NotificationRecord.class);
-
- // before the lockdown mode
- when(mNm.isInLockDownMode()).thenReturn(false);
- mListeners.notifyPostedLocked(r, old, true);
- mListeners.notifyPostedLocked(r, old, false);
- verify(r, atLeast(2)).getSbn();
-
- // in the lockdown mode
- reset(r);
- reset(old);
- when(mNm.isInLockDownMode()).thenReturn(true);
- mListeners.notifyPostedLocked(r, old, true);
- mListeners.notifyPostedLocked(r, old, false);
- verify(r, never()).getSbn();
- }
-
- @Test
- public void testnotifyRankingUpdateLockedInLockdownMode() {
- List chn = mock(List.class);
-
- // before the lockdown mode
- when(mNm.isInLockDownMode()).thenReturn(false);
- mListeners.notifyRankingUpdateLocked(chn);
- verify(chn, atLeast(1)).size();
-
- // in the lockdown mode
- reset(chn);
- when(mNm.isInLockDownMode()).thenReturn(true);
- mListeners.notifyRankingUpdateLocked(chn);
- verify(chn, never()).size();
+ NotificationRecord r0 = mock(NotificationRecord.class);
+ NotificationRecord old0 = mock(NotificationRecord.class);
+ UserHandle uh0 = mock(UserHandle.class);
+
+ NotificationRecord r1 = mock(NotificationRecord.class);
+ NotificationRecord old1 = mock(NotificationRecord.class);
+ UserHandle uh1 = mock(UserHandle.class);
+
+ // Neither user0 and user1 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(false);
+
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
+
+ mListeners.notifyPostedLocked(r0, old0, true);
+ mListeners.notifyPostedLocked(r0, old0, false);
+ verify(r0, atLeast(2)).getSbn();
+
+ mListeners.notifyPostedLocked(r1, old1, true);
+ mListeners.notifyPostedLocked(r1, old1, false);
+ verify(r1, atLeast(2)).getSbn();
+
+ // Reset
+ reset(r0);
+ reset(old0);
+ reset(r1);
+ reset(old1);
+
+ // Only user 0 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(true);
+
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
+
+ mListeners.notifyPostedLocked(r0, old0, true);
+ mListeners.notifyPostedLocked(r0, old0, false);
+ verify(r0, never()).getSbn();
+
+ mListeners.notifyPostedLocked(r1, old1, true);
+ mListeners.notifyPostedLocked(r1, old1, false);
+ verify(r1, atLeast(2)).getSbn();
}
@Test
public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException {
- NotificationRecord r = mock(NotificationRecord.class);
- NotificationStats rs = mock(NotificationStats.class);
+ NotificationRecord r0 = mock(NotificationRecord.class);
+ NotificationStats rs0 = mock(NotificationStats.class);
+ UserHandle uh0 = mock(UserHandle.class);
+
+ NotificationRecord r1 = mock(NotificationRecord.class);
+ NotificationStats rs1 = mock(NotificationStats.class);
+ UserHandle uh1 = mock(UserHandle.class);
+
StatusBarNotification sbn = mock(StatusBarNotification.class);
FieldSetter.setField(mNm,
NotificationManagerService.class.getDeclaredField("mHandler"),
mock(NotificationManagerService.WorkerHandler.class));
- // before the lockdown mode
- when(mNm.isInLockDownMode()).thenReturn(false);
- when(r.getSbn()).thenReturn(sbn);
- mListeners.notifyRemovedLocked(r, 0, rs);
- mListeners.notifyRemovedLocked(r, 0, rs);
- verify(r, atLeast(2)).getSbn();
-
- // in the lockdown mode
- reset(r);
- reset(rs);
- when(mNm.isInLockDownMode()).thenReturn(true);
- when(r.getSbn()).thenReturn(sbn);
- mListeners.notifyRemovedLocked(r, 0, rs);
- mListeners.notifyRemovedLocked(r, 0, rs);
- verify(r, never()).getSbn();
+ // Neither user0 and user1 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(false);
+ when(r0.getSbn()).thenReturn(sbn);
+
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
+ when(r1.getSbn()).thenReturn(sbn);
+
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ verify(r0, atLeast(2)).getSbn();
+
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ verify(r1, atLeast(2)).getSbn();
+
+ // Reset
+ reset(r0);
+ reset(rs0);
+ reset(r1);
+ reset(rs1);
+
+ // Only user 0 is in the lockdown mode
+ when(r0.getUser()).thenReturn(uh0);
+ when(uh0.getIdentifier()).thenReturn(0);
+ when(mNm.isInLockDownMode(0)).thenReturn(true);
+ when(r0.getSbn()).thenReturn(sbn);
+
+ when(r1.getUser()).thenReturn(uh1);
+ when(uh1.getIdentifier()).thenReturn(1);
+ when(mNm.isInLockDownMode(1)).thenReturn(false);
+ when(r1.getSbn()).thenReturn(sbn);
+
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ mListeners.notifyRemovedLocked(r0, 0, rs0);
+ verify(r0, never()).getSbn();
+
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ mListeners.notifyRemovedLocked(r1, 0, rs1);
+ verify(r1, atLeast(2)).getSbn();
}
@Test
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 cae8fd947800..92761427184b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -174,6 +174,7 @@ import android.service.notification.Adjustment;
import android.service.notification.ConversationChannelWrapper;
import android.service.notification.NotificationListenerFilter;
import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationRankingUpdate;
import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.service.notification.ZenPolicy;
@@ -9837,10 +9838,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
- assertTrue(mStrongAuthTracker.isInLockDownMode());
- mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
+ assertTrue(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
+ mStrongAuthTracker.setGetStrongAuthForUserReturnValue(mContext.getUserId());
mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
- assertFalse(mStrongAuthTracker.isInLockDownMode());
+ assertFalse(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
}
@Test
@@ -9856,8 +9857,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// when entering the lockdown mode, cancel the 2 notifications.
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
- mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
- assertTrue(mStrongAuthTracker.isInLockDownMode());
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertTrue(mStrongAuthTracker.isInLockDownMode(0));
// the notifyRemovedLocked function is called twice due to REASON_LOCKDOWN.
ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
@@ -9866,10 +9867,46 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// exit lockdown mode.
mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
- mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertFalse(mStrongAuthTracker.isInLockDownMode(0));
// the notifyPostedLocked function is called twice.
- verify(mListeners, times(2)).notifyPostedLocked(any(), any());
+ verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong());
+ //verify(mListeners, times(2)).notifyPostedLocked(any(), any());
+ }
+
+ @Test
+ public void testMakeRankingUpdateLockedInLockDownMode() {
+ // post 2 notifications from a same package
+ NotificationRecord pkgA = new NotificationRecord(mContext,
+ generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
+ mService.addNotification(pkgA);
+ NotificationRecord pkgB = new NotificationRecord(mContext,
+ generateSbn("a", 1000, 9, 1), mTestNotificationChannel);
+ mService.addNotification(pkgB);
+
+ mService.setIsVisibleToListenerReturnValue(true);
+ NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(null);
+ assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
+
+ // when only user 0 entering the lockdown mode, its notification will be suppressed.
+ mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
+ STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertTrue(mStrongAuthTracker.isInLockDownMode(0));
+ assertFalse(mStrongAuthTracker.isInLockDownMode(1));
+
+ nru = mService.makeRankingUpdateLocked(null);
+ assertEquals(1, nru.getRankingMap().getOrderedKeys().length);
+
+ // User 0 exits lockdown mode. Its notification will be resumed.
+ mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
+ mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+ assertFalse(mStrongAuthTracker.isInLockDownMode(0));
+ assertFalse(mStrongAuthTracker.isInLockDownMode(1));
+
+ nru = mService.makeRankingUpdateLocked(null);
+ assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index b49e5cbfa9dc..8cf74fbf88b7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -19,10 +19,12 @@ package com.android.server.notification;
import android.companion.ICompanionDeviceManager;
import android.content.ComponentName;
import android.content.Context;
+import android.service.notification.StatusBarNotification;
import androidx.annotation.Nullable;
import com.android.internal.logging.InstanceIdSequence;
+import com.android.server.notification.ManagedServices.ManagedServiceInfo;
import java.util.HashSet;
import java.util.Set;
@@ -37,6 +39,9 @@ public class TestableNotificationManagerService extends NotificationManagerServi
@Nullable
NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;
+ @Nullable
+ Boolean mIsVisibleToListenerReturnValue = null;
+
TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
InstanceIdSequence notificationInstanceIdSequence) {
super(context, logger, notificationInstanceIdSequence);
@@ -119,6 +124,19 @@ public class TestableNotificationManagerService extends NotificationManagerServi
mShowReviewPermissionsNotification = setting;
}
+ protected void setIsVisibleToListenerReturnValue(boolean value) {
+ mIsVisibleToListenerReturnValue = value;
+ }
+
+ @Override
+ boolean isVisibleToListener(StatusBarNotification sbn, int notificationType,
+ ManagedServiceInfo listener) {
+ if (mIsVisibleToListenerReturnValue != null) {
+ return mIsVisibleToListenerReturnValue;
+ }
+ return super.isVisibleToListener(sbn, notificationType, listener);
+ }
+
public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
private int mGetStrongAuthForUserReturnValue = 0;
StrongAuthTrackerFake(Context context) {
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 2918365b94c8..107bbe1c79e3 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -79,7 +79,10 @@
<activity android:name="com.android.server.wm.ActivityOptionsTest$MainActivity"
android:turnScreenOn="true"
android:showWhenLocked="true" />
- <activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity" />
+ <activity android:name="com.android.server.wm.ScreenshotTests$ScreenshotActivity"
+ android:theme="@style/WhiteBackgroundTheme"
+ android:turnScreenOn="true"
+ android:showWhenLocked="true"/>
<activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
<service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml b/services/tests/wmtests/res/values/styles.xml
index 23b51cc06f04..6857ff99e9b8 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/values/styles.xml
+++ b/services/tests/wmtests/res/values/styles.xml
@@ -1,4 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2022 The Android Open Source Project
~
@@ -16,19 +15,10 @@
-->
<resources>
- <style name="DefaultTheme" parent="@android:style/Theme.DeviceDefault">
- <item name="android:windowBackground">@android:color/darker_gray</item>
- </style>
-
- <style name="CutoutDefault" parent="@style/DefaultTheme">
- <item name="android:windowLayoutInDisplayCutoutMode">default</item>
- </style>
-
- <style name="CutoutShortEdges" parent="@style/DefaultTheme">
+ <style name="WhiteBackgroundTheme" parent="@android:style/Theme.DeviceDefault">
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowFullscreen">true</item>
+ <item name="android:windowIsTranslucent">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
-
- <style name="CutoutNever" parent="@style/DefaultTheme">
- <item name="android:windowLayoutInDisplayCutoutMode">never</item>
- </style>
-</resources> \ No newline at end of file
+</resources>
diff --git a/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
new file mode 100644
index 000000000000..ad47773747d4
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/policy/ModifierShortcutTests.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.policy;
+
+import static android.view.KeyEvent.KEYCODE_A;
+import static android.view.KeyEvent.KEYCODE_ALT_LEFT;
+import static android.view.KeyEvent.KEYCODE_B;
+import static android.view.KeyEvent.KEYCODE_C;
+import static android.view.KeyEvent.KEYCODE_CTRL_LEFT;
+import static android.view.KeyEvent.KEYCODE_E;
+import static android.view.KeyEvent.KEYCODE_L;
+import static android.view.KeyEvent.KEYCODE_M;
+import static android.view.KeyEvent.KEYCODE_META_LEFT;
+import static android.view.KeyEvent.KEYCODE_N;
+import static android.view.KeyEvent.KEYCODE_P;
+import static android.view.KeyEvent.KEYCODE_S;
+import static android.view.KeyEvent.KEYCODE_SLASH;
+import static android.view.KeyEvent.KEYCODE_SPACE;
+import static android.view.KeyEvent.KEYCODE_TAB;
+import static android.view.KeyEvent.KEYCODE_Z;
+
+import android.content.Intent;
+import android.os.RemoteException;
+import android.util.SparseArray;
+
+import org.junit.Test;
+
+public class ModifierShortcutTests extends ShortcutKeyTestBase {
+ private static final SparseArray<String> META_SHORTCUTS = new SparseArray<>();
+ static {
+ META_SHORTCUTS.append(KEYCODE_A, Intent.CATEGORY_APP_CALCULATOR);
+ META_SHORTCUTS.append(KEYCODE_B, Intent.CATEGORY_APP_BROWSER);
+ META_SHORTCUTS.append(KEYCODE_C, Intent.CATEGORY_APP_CONTACTS);
+ META_SHORTCUTS.append(KEYCODE_E, Intent.CATEGORY_APP_EMAIL);
+ META_SHORTCUTS.append(KEYCODE_L, Intent.CATEGORY_APP_CALENDAR);
+ META_SHORTCUTS.append(KEYCODE_M, Intent.CATEGORY_APP_MAPS);
+ META_SHORTCUTS.append(KEYCODE_P, Intent.CATEGORY_APP_MUSIC);
+ META_SHORTCUTS.append(KEYCODE_S, Intent.CATEGORY_APP_MESSAGING);
+ }
+
+ /**
+ * Test meta+ shortcuts defined in bookmarks.xml.
+ */
+ @Test
+ public void testMetaShortcuts() {
+ for (int i = 0; i < META_SHORTCUTS.size(); i++) {
+ final int keyCode = META_SHORTCUTS.keyAt(i);
+ final String category = META_SHORTCUTS.valueAt(i);
+
+ sendKeyCombination(new int[]{KEYCODE_META_LEFT, keyCode}, 0);
+ mPhoneWindowManager.assertLaunchCategory(category);
+ }
+ }
+
+ /**
+ * ALT + TAB to show recent apps.
+ */
+ @Test
+ public void testAltTab() {
+ mPhoneWindowManager.overrideStatusBarManagerInternal();
+ sendKeyCombination(new int[]{KEYCODE_ALT_LEFT, KEYCODE_TAB}, 0);
+ mPhoneWindowManager.assertShowRecentApps();
+ }
+
+ /**
+ * CTRL + SPACE to switch keyboard layout.
+ */
+ @Test
+ public void testCtrlSpace() {
+ sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_SPACE}, 0);
+ mPhoneWindowManager.assertSwitchKeyboardLayout();
+ }
+
+ /**
+ * META + SPACE to switch keyboard layout.
+ */
+ @Test
+ public void testMetaSpace() {
+ sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SPACE}, 0);
+ mPhoneWindowManager.assertSwitchKeyboardLayout();
+ }
+
+ /**
+ * CTRL + ALT + Z to enable accessibility service.
+ */
+ @Test
+ public void testCtrlAltZ() {
+ sendKeyCombination(new int[]{KEYCODE_CTRL_LEFT, KEYCODE_ALT_LEFT, KEYCODE_Z}, 0);
+ mPhoneWindowManager.assertAccessibilityKeychordCalled();
+ }
+
+ /**
+ * META + CTRL+ S to take screenshot.
+ */
+ @Test
+ public void testMetaCtrlS() {
+ sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_CTRL_LEFT, KEYCODE_S}, 0);
+ mPhoneWindowManager.assertTakeScreenshotCalled();
+ }
+
+ /**
+ * META + N to expand notification panel.
+ */
+ @Test
+ public void testMetaN() throws RemoteException {
+ mPhoneWindowManager.overrideExpandNotificationsPanel();
+ sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_N}, 0);
+ mPhoneWindowManager.assertExpandNotification();
+ }
+
+ /**
+ * META + SLASH to toggle shortcuts menu.
+ */
+ @Test
+ public void testMetaSlash() {
+ mPhoneWindowManager.overrideStatusBarManagerInternal();
+ sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_SLASH}, 0);
+ mPhoneWindowManager.assertToggleShortcutsMenu();
+ }
+
+ /**
+ * META + ALT to toggle Cap Lock.
+ */
+ @Test
+ public void testMetaAlt() {
+ sendKeyCombination(new int[]{KEYCODE_META_LEFT, KEYCODE_ALT_LEFT}, 0);
+ mPhoneWindowManager.assertToggleCapsLock();
+ }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
index ee11ac8caf53..a76b82babe08 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -32,6 +32,7 @@ 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.spy;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_ASSISTANT;
import static com.android.server.policy.PhoneWindowManager.LONG_PRESS_POWER_GLOBAL_ACTIONS;
@@ -53,15 +54,16 @@ import android.app.AppOpsManager;
import android.app.NotificationManager;
import android.app.SearchManager;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManagerInternal;
-import android.hardware.input.InputManagerInternal;
import android.media.AudioManagerInternal;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PowerManager;
import android.os.PowerManagerInternal;
+import android.os.RemoteException;
import android.os.Vibrator;
import android.service.dreams.DreamManagerInternal;
import android.telecom.TelecomManager;
@@ -73,12 +75,17 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSession;
import com.android.internal.accessibility.AccessibilityShortcutController;
import com.android.server.GestureLauncherService;
import com.android.server.LocalServices;
+import com.android.server.input.InputManagerInternal;
+import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.DisplayPolicy;
import com.android.server.wm.DisplayRotation;
import com.android.server.wm.WindowManagerInternal;
+import junit.framework.Assert;
+
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockSettings;
import org.mockito.Mockito;
@@ -118,6 +125,8 @@ class TestPhoneWindowManager {
@Mock private GlobalActions mGlobalActions;
@Mock private AccessibilityShortcutController mAccessibilityShortcutController;
+ @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
+
private StaticMockitoSession mMockitoSession;
private HandlerThread mHandlerThread;
private Handler mHandler;
@@ -226,6 +235,8 @@ class TestPhoneWindowManager {
mPhoneWindowManager.systemBooted();
overrideLaunchAccessibility();
+ doReturn(false).when(mPhoneWindowManager).keyguardOn();
+ doNothing().when(mContext).startActivityAsUser(any(), any());
}
void tearDown() {
@@ -310,6 +321,22 @@ class TestPhoneWindowManager {
doReturn(true).when(mTelecomManager).endCall();
}
+ void overrideExpandNotificationsPanel() {
+ // Can't directly mock on IStatusbarService, use spyOn and override the specific api.
+ mPhoneWindowManager.getStatusBarService();
+ spyOn(mPhoneWindowManager.mStatusBarService);
+ try {
+ doNothing().when(mPhoneWindowManager.mStatusBarService).expandNotificationsPanel();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ void overrideStatusBarManagerInternal() {
+ doReturn(mStatusBarManagerInternal).when(
+ () -> LocalServices.getService(eq(StatusBarManagerInternal.class)));
+ }
+
/**
* Below functions will check the policy behavior could be invoked.
*/
@@ -368,4 +395,46 @@ class TestPhoneWindowManager {
waitForIdle();
verify(mSearchManager, timeout(SHORTCUT_KEY_DELAY_MILLIS)).launchAssist(any());
}
+
+ void assertLaunchCategory(String category) {
+ waitForIdle();
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).startActivityAsUser(intentCaptor.capture(), any());
+ Assert.assertTrue(intentCaptor.getValue().getSelector().hasCategory(category));
+ // Reset verifier for next call.
+ Mockito.reset(mContext);
+ }
+
+ void assertShowRecentApps() {
+ waitForIdle();
+ verify(mStatusBarManagerInternal).showRecentApps(anyBoolean());
+ }
+
+ void assertSwitchKeyboardLayout() {
+ waitForIdle();
+ verify(mWindowManagerFuncsImpl).switchKeyboardLayout(anyInt(), anyInt());
+ }
+
+ void assertTakeBugreport() {
+ waitForIdle();
+ ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mContext).sendOrderedBroadcastAsUser(intentCaptor.capture(), any(), any(), any(),
+ any(), anyInt(), any(), any());
+ Assert.assertTrue(intentCaptor.getValue().getAction() == Intent.ACTION_BUG_REPORT);
+ }
+
+ void assertExpandNotification() throws RemoteException {
+ waitForIdle();
+ verify(mPhoneWindowManager.mStatusBarService).expandNotificationsPanel();
+ }
+
+ void assertToggleShortcutsMenu() {
+ waitForIdle();
+ verify(mStatusBarManagerInternal).toggleKeyboardShortcutsMenu(anyInt());
+ }
+
+ void assertToggleCapsLock() {
+ waitForIdle();
+ verify(mInputManagerInternal).toggleCapsLock(anyInt());
+ }
}
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 1d52b7f540a1..376399a3d363 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -384,7 +384,7 @@ public class ActivityMetricsLaunchObserverTests extends WindowTestsBase {
}
private ActivityMetricsLogger.TransitionInfoSnapshot notifyWindowsDrawn(ActivityRecord r) {
- return mActivityMetricsLogger.notifyWindowsDrawn(r, SystemClock.elapsedRealtimeNanos());
+ return mActivityMetricsLogger.notifyWindowsDrawn(r);
}
@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 333be7bd8c61..2f23e7fac75f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2990,7 +2990,8 @@ public class ActivityRecordTests extends WindowTestsBase {
.setSystemDecorations(true).build();
// Add a decor insets provider window.
final WindowState navbar = createNavBarWithProvidedInsets(squareDisplay);
- squareDisplay.getDisplayPolicy().updateDecorInsetsInfoIfNeeded(navbar);
+ assertTrue(navbar.providesNonDecorInsets()
+ && squareDisplay.getDisplayPolicy().updateDecorInsetsInfo());
squareDisplay.sendNewConfiguration();
final Task task = new TaskBuilder(mSupervisor).setDisplay(squareDisplay).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 85e5bfd84f9f..2b0e76cbc9bd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -379,6 +379,8 @@ public class ActivityStarterTests extends WindowTestsBase {
doReturn(false).when(mMockPackageManager).isInstantAppInstallerComponent(any());
doReturn(null).when(mMockPackageManager).resolveIntent(any(), any(), anyLong(), anyLong(),
anyInt(), anyBoolean(), anyInt());
+ doReturn(null).when(mMockPackageManager).resolveIntentExported(any(), any(),
+ anyLong(), anyLong(), anyInt(), anyBoolean(), anyInt());
doReturn(new ComponentName("", "")).when(mMockPackageManager).getSystemUiServiceComponent();
// Never review permissions
diff --git a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
index e2c94c5b4b3d..49fd1ab60e09 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ContentRecorderTests.java
@@ -296,6 +296,14 @@ public class ContentRecorderTests extends WindowTestsBase {
}
@Test
+ public void testRemoveTask_stopsRecording_nullSessionShouldNotThrowExceptions() {
+ mContentRecorder.setContentRecordingSession(mTaskSession);
+ mContentRecorder.updateRecording();
+ mContentRecorder.setContentRecordingSession(null);
+ mTask.removeImmediately();
+ }
+
+ @Test
public void testUpdateMirroredSurface_capturedAreaResized() {
mContentRecorder.setContentRecordingSession(mDisplaySession);
mContentRecorder.updateRecording();
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 44471f49668c..11ae5d4abaf8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -139,6 +139,7 @@ import android.view.View;
import android.view.WindowManager;
import android.window.DisplayAreaInfo;
import android.window.IDisplayAreaOrganizer;
+import android.window.ScreenCapture;
import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
@@ -2109,8 +2110,8 @@ public class DisplayContentTests extends WindowTestsBase {
// Preparation: Simulate snapshot IME surface.
spyOn(mWm.mTaskSnapshotController);
- SurfaceControl.ScreenshotHardwareBuffer mockHwBuffer = mock(
- SurfaceControl.ScreenshotHardwareBuffer.class);
+ ScreenCapture.ScreenshotHardwareBuffer mockHwBuffer = mock(
+ ScreenCapture.ScreenshotHardwareBuffer.class);
doReturn(mock(HardwareBuffer.class)).when(mockHwBuffer).getHardwareBuffer();
doReturn(mockHwBuffer).when(mWm.mTaskSnapshotController).snapshotImeFromAttachedTask(any());
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 262b141e6005..a980765711d1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -292,12 +292,16 @@ public class DisplayPolicyTests extends WindowTestsBase {
final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
final DisplayInfo di = mDisplayContent.getDisplayInfo();
final int prevScreenHeightDp = mDisplayContent.getConfiguration().screenHeightDp;
- assertTrue(displayPolicy.updateDecorInsetsInfoIfNeeded(navbar));
+ assertTrue(navbar.providesNonDecorInsets() && displayPolicy.updateDecorInsetsInfo());
assertEquals(NAV_BAR_HEIGHT, displayPolicy.getDecorInsetsInfo(di.rotation,
di.logicalWidth, di.logicalHeight).mConfigInsets.bottom);
mDisplayContent.sendNewConfiguration();
assertNotEquals(prevScreenHeightDp, mDisplayContent.getConfiguration().screenHeightDp);
- assertFalse(displayPolicy.updateDecorInsetsInfoIfNeeded(navbar));
+ assertFalse(navbar.providesNonDecorInsets() && displayPolicy.updateDecorInsetsInfo());
+
+ navbar.removeIfPossible();
+ assertEquals(0, displayPolicy.getDecorInsetsInfo(di.rotation, di.logicalWidth,
+ di.logicalHeight).mNonDecorInsets.bottom);
}
@SetupWindows(addWindows = { W_NAVIGATION_BAR, W_INPUT_METHOD })
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
index 2956c14155b9..ac3d0f0d3f28 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -46,6 +46,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.NonNull;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.os.Binder;
@@ -55,6 +56,8 @@ import android.window.IDisplayAreaOrganizer;
import androidx.test.filters.SmallTest;
+import com.google.android.collect.Lists;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -90,7 +93,11 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
@Before
public void setUp() {
// Let the Display to be created with the DualDisplay policy.
- final DisplayAreaPolicy.Provider policyProvider = new DualDisplayTestPolicyProvider();
+ setupDisplay(new DualDisplayTestPolicyProvider(mWm));
+ }
+
+ /** Populates fields for the test display. */
+ private void setupDisplay(@NonNull DisplayAreaPolicy.Provider policyProvider) {
doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
// Display: 1920x1200 (landscape). First and second display are both 860x1200 (portrait).
@@ -383,6 +390,36 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
}
@Test
+ public void testPlaceImeContainer_noReparentIfRootDoesNotHaveImePlaceholder() {
+ // Define the DualDisplayArea hierarchy without IME_PLACEHOLDER in DAGs.
+ setupDisplay(new DualDisplayTestPolicyProvider(new ArrayList<>(), new ArrayList<>()));
+ setupImeWindow();
+ final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer();
+ final WindowToken imeToken = tokenOfType(TYPE_INPUT_METHOD);
+
+ // By default, the ime container is attached to DC as defined in DAPolicy.
+ assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay);
+
+ // firstActivityWin should be the target
+ final WindowState firstActivityWin =
+ createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity,
+ "firstActivityWin");
+ spyOn(firstActivityWin);
+ doReturn(true).when(firstActivityWin).canBeImeTarget();
+ WindowState imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */);
+
+ // There is no IME_PLACEHOLDER in the firstRoot, so the ImeContainer will not be reparented.
+ assertThat(imeTarget).isEqualTo(firstActivityWin);
+ verify(mFirstRoot).placeImeContainer(imeContainer);
+ assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay);
+ assertThat(imeContainer.getParent().asDisplayArea().mFeatureId)
+ .isEqualTo(FEATURE_IME_PLACEHOLDER);
+ assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer);
+ assertThat(mFirstRoot.findAreaForTokenInLayer(imeToken)).isNull();
+ assertThat(mSecondRoot.findAreaForTokenInLayer(imeToken)).isNull();
+ }
+
+ @Test
public void testResizableFixedOrientationApp_fixedOrientationLetterboxing() {
mFirstRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
mSecondRoot.setIgnoreOrientationRequest(false /* ignoreOrientationRequest */);
@@ -523,9 +560,37 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
/** Policy to create a dual {@link DisplayAreaGroup} policy in test. */
static class DualDisplayTestPolicyProvider implements DisplayAreaPolicy.Provider {
+ @NonNull
+ private final List<DisplayAreaPolicyBuilder.Feature> mFirstRootFeatures = new ArrayList<>();
+ @NonNull
+ private final List<DisplayAreaPolicyBuilder.Feature> mSecondRootFeatures =
+ new ArrayList<>();
+
+ DualDisplayTestPolicyProvider(@NonNull WindowManagerService wmService) {
+ // Add IME_PLACEHOLDER by default.
+ this(Lists.newArrayList(new DisplayAreaPolicyBuilder.Feature.Builder(
+ wmService.mPolicy,
+ "ImePlaceholder", FEATURE_IME_PLACEHOLDER)
+ .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
+ .build()),
+ Lists.newArrayList(new DisplayAreaPolicyBuilder.Feature.Builder(
+ wmService.mPolicy,
+ "ImePlaceholder", FEATURE_IME_PLACEHOLDER)
+ .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
+ .build()));
+ }
+
+ DualDisplayTestPolicyProvider(
+ @NonNull List<DisplayAreaPolicyBuilder.Feature> firstRootFeatures,
+ @NonNull List<DisplayAreaPolicyBuilder.Feature> secondRootFeatures) {
+ mFirstRootFeatures.addAll(firstRootFeatures);
+ mSecondRootFeatures.addAll(secondRootFeatures);
+ }
+
@Override
- public DisplayAreaPolicy instantiate(WindowManagerService wmService, DisplayContent content,
- RootDisplayArea root, DisplayArea.Tokens imeContainer) {
+ public DisplayAreaPolicy instantiate(@NonNull WindowManagerService wmService,
+ @NonNull DisplayContent content, @NonNull RootDisplayArea root,
+ @NonNull DisplayArea.Tokens imeContainer) {
// Root
// Include FEATURE_WINDOWED_MAGNIFICATION because it will be used as the screen rotation
// layer
@@ -554,12 +619,10 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
firstTdaList.add(firstTaskDisplayArea);
DisplayAreaPolicyBuilder.HierarchyBuilder firstHierarchy =
new DisplayAreaPolicyBuilder.HierarchyBuilder(firstRoot)
- .setTaskDisplayAreas(firstTdaList)
- .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(
- wmService.mPolicy,
- "ImePlaceholder", FEATURE_IME_PLACEHOLDER)
- .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
- .build());
+ .setTaskDisplayAreas(firstTdaList);
+ for (DisplayAreaPolicyBuilder.Feature feature : mFirstRootFeatures) {
+ firstHierarchy.addFeature(feature);
+ }
// Second
final RootDisplayArea secondRoot = new DisplayAreaGroup(wmService, "SecondRoot",
@@ -570,12 +633,10 @@ public class DualDisplayAreaGroupPolicyTest extends WindowTestsBase {
secondTdaList.add(secondTaskDisplayArea);
DisplayAreaPolicyBuilder.HierarchyBuilder secondHierarchy =
new DisplayAreaPolicyBuilder.HierarchyBuilder(secondRoot)
- .setTaskDisplayAreas(secondTdaList)
- .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(
- wmService.mPolicy,
- "ImePlaceholder", FEATURE_IME_PLACEHOLDER)
- .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
- .build());
+ .setTaskDisplayAreas(secondTdaList);
+ for (DisplayAreaPolicyBuilder.Feature feature : mSecondRootFeatures) {
+ secondHierarchy.addFeature(feature);
+ }
return new DisplayAreaPolicyBuilder()
.setRootHierarchy(rootHierarchy)
diff --git a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
index d48711375d5c..3090590938c0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InputMethodDialogWindowContextTest.java
@@ -78,7 +78,7 @@ public class InputMethodDialogWindowContextTest extends WindowTestsBase {
public void setUp() throws Exception {
// Let the Display be created with the DualDisplay policy.
final DisplayAreaPolicy.Provider policyProvider =
- new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
+ new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider(mWm);
Mockito.doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
mWindowContext = new InputMethodDialogWindowContext();
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 adf694c2a88d..170b3881edd7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1532,7 +1532,7 @@ public class RecentTasksTest extends WindowTestsBase {
@Override
void getTasks(int maxNum, List<RunningTaskInfo> list, int flags, RecentTasks recentTasks,
- WindowContainer root, int callingUid, ArraySet<Integer> profileIds) {
+ WindowContainer<?> root, int callingUid, ArraySet<Integer> profileIds) {
mLastAllowed = (flags & FLAG_ALLOWED) == FLAG_ALLOWED;
super.getTasks(maxNum, list, flags, recentTasks, root, callingUid, profileIds);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
index f542e290c701..736f8f7a5ed3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ScreenshotTests.java
@@ -16,6 +16,10 @@
package com.android.server.wm;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.WindowInsets.Type.displayCutout;
+import static android.view.WindowInsets.Type.statusBars;
+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertNotNull;
@@ -23,19 +27,33 @@ import static org.junit.Assert.assertTrue;
import android.app.Activity;
import android.app.Instrumentation;
+import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.ColorSpace;
import android.graphics.GraphicBuffer;
+import android.graphics.Insets;
import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.DataSpace;
+import android.hardware.HardwareBuffer;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.ServiceManager;
import android.platform.test.annotations.Presubmit;
+import android.util.Pair;
+import android.view.IWindowManager;
import android.view.PointerIcon;
import android.view.SurfaceControl;
-import android.view.WindowManager;
+import android.view.cts.surfacevalidator.BitmapPixelChecker;
+import android.view.cts.surfacevalidator.PixelColor;
+import android.view.cts.surfacevalidator.SaveBitmapHelper;
+import android.window.ScreenCapture;
+import android.window.ScreenCapture.ScreenCaptureListener;
+import android.window.ScreenCapture.ScreenshotHardwareBuffer;
+import android.window.ScreenCapture.ScreenshotSync;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
@@ -44,6 +62,7 @@ import androidx.test.rule.ActivityTestRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -59,6 +78,8 @@ public class ScreenshotTests {
private static final int BUFFER_HEIGHT = 100;
private final Instrumentation mInstrumentation = getInstrumentation();
+ @Rule
+ public TestName mTestName = new TestName();
@Rule
public ActivityTestRule<ScreenshotActivity> mActivityRule =
@@ -94,15 +115,15 @@ public class ScreenshotTests {
buffer.unlockCanvasAndPost(canvas);
t.show(secureSC)
- .setBuffer(secureSC, buffer)
- .setColorSpace(secureSC, ColorSpace.get(ColorSpace.Named.SRGB))
+ .setBuffer(secureSC, HardwareBuffer.createFromGraphicBuffer(buffer))
+ .setDataSpace(secureSC, DataSpace.DATASPACE_SRGB)
.apply(true);
- SurfaceControl.LayerCaptureArgs args = new SurfaceControl.LayerCaptureArgs.Builder(secureSC)
+ ScreenCapture.LayerCaptureArgs args = new ScreenCapture.LayerCaptureArgs.Builder(secureSC)
.setCaptureSecureLayers(true)
.setChildrenOnly(false)
.build();
- SurfaceControl.ScreenshotHardwareBuffer hardwareBuffer = SurfaceControl.captureLayers(args);
+ ScreenCapture.ScreenshotHardwareBuffer hardwareBuffer = ScreenCapture.captureLayers(args);
assertNotNull(hardwareBuffer);
Bitmap screenshot = hardwareBuffer.asBitmap();
@@ -111,15 +132,70 @@ public class ScreenshotTests {
Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
screenshot.recycle();
- int numMatchingPixels = PixelChecker.getNumMatchingPixels(swBitmap,
- new PixelColor(PixelColor.RED));
- long sizeOfBitmap = swBitmap.getWidth() * swBitmap.getHeight();
+ BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(PixelColor.RED);
+ Rect bounds = new Rect(0, 0, swBitmap.getWidth(), swBitmap.getHeight());
+ int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
+ int sizeOfBitmap = bounds.width() * bounds.height();
boolean success = numMatchingPixels == sizeOfBitmap;
swBitmap.recycle();
assertTrue(success);
}
+ @Test
+ public void testCaptureDisplay() throws Exception {
+ IWindowManager windowManager = IWindowManager.Stub.asInterface(
+ ServiceManager.getService(Context.WINDOW_SERVICE));
+ SurfaceControl sc = new SurfaceControl.Builder()
+ .setName("Layer")
+ .setCallsite("testCaptureDisplay")
+ .build();
+
+ SurfaceControl.Transaction t = mActivity.addChildSc(sc);
+ mInstrumentation.waitForIdleSync();
+
+ GraphicBuffer buffer = GraphicBuffer.create(BUFFER_WIDTH, BUFFER_HEIGHT,
+ PixelFormat.RGBA_8888,
+ GraphicBuffer.USAGE_HW_TEXTURE | GraphicBuffer.USAGE_HW_COMPOSER
+ | GraphicBuffer.USAGE_SW_WRITE_RARELY);
+
+ Canvas canvas = buffer.lockCanvas();
+ canvas.drawColor(Color.RED);
+ buffer.unlockCanvasAndPost(canvas);
+
+ Point point = mActivity.getPositionBelowStatusBar();
+ t.show(sc)
+ .setBuffer(sc, HardwareBuffer.createFromGraphicBuffer(buffer))
+ .setDataSpace(sc, DataSpace.DATASPACE_SRGB)
+ .setPosition(sc, point.x, point.y)
+ .apply(true);
+
+ Pair<ScreenCaptureListener, ScreenshotSync> syncScreenCapture =
+ ScreenCapture.createSyncCaptureListener();
+ windowManager.captureDisplay(DEFAULT_DISPLAY, null, syncScreenCapture.first);
+ ScreenshotHardwareBuffer hardwareBuffer = syncScreenCapture.second.get();
+ assertNotNull(hardwareBuffer);
+
+ Bitmap screenshot = hardwareBuffer.asBitmap();
+ assertNotNull(screenshot);
+
+ Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
+ screenshot.recycle();
+
+ BitmapPixelChecker bitmapPixelChecker = new BitmapPixelChecker(PixelColor.RED);
+ Rect bounds = new Rect(point.x, point.y, BUFFER_WIDTH + point.x, BUFFER_HEIGHT + point.y);
+ int numMatchingPixels = bitmapPixelChecker.getNumMatchingPixels(swBitmap, bounds);
+ int pixelMatchSize = bounds.width() * bounds.height();
+ boolean success = numMatchingPixels == pixelMatchSize;
+
+ if (!success) {
+ SaveBitmapHelper.saveBitmap(swBitmap, getClass(), mTestName, "failedImage");
+ }
+ swBitmap.recycle();
+ assertTrue("numMatchingPixels=" + numMatchingPixels + " pixelMatchSize=" + pixelMatchSize,
+ success);
+ }
+
public static class ScreenshotActivity extends Activity {
private static final long WAIT_TIMEOUT_S = 5;
private final Handler mHandler = new Handler(Looper.getMainLooper());
@@ -129,7 +205,6 @@ public class ScreenshotTests {
super.onCreate(savedInstanceState);
getWindow().getDecorView().setPointerIcon(
PointerIcon.getSystemIcon(this, PointerIcon.TYPE_NULL));
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
SurfaceControl.Transaction addChildSc(SurfaceControl surfaceControl) {
@@ -147,88 +222,14 @@ public class ScreenshotTests {
}
return t;
}
- }
- public abstract static class PixelChecker {
- static int getNumMatchingPixels(Bitmap bitmap, PixelColor pixelColor) {
- int numMatchingPixels = 0;
- for (int x = 0; x < bitmap.getWidth(); x++) {
- for (int y = 0; y < bitmap.getHeight(); y++) {
- int color = bitmap.getPixel(x, y);
- if (matchesColor(pixelColor, color)) {
- numMatchingPixels++;
- }
- }
- }
- return numMatchingPixels;
- }
-
- static boolean matchesColor(PixelColor expectedColor, int color) {
- final float red = Color.red(color);
- final float green = Color.green(color);
- final float blue = Color.blue(color);
- final float alpha = Color.alpha(color);
-
- return alpha <= expectedColor.mMaxAlpha
- && alpha >= expectedColor.mMinAlpha
- && red <= expectedColor.mMaxRed
- && red >= expectedColor.mMinRed
- && green <= expectedColor.mMaxGreen
- && green >= expectedColor.mMinGreen
- && blue <= expectedColor.mMaxBlue
- && blue >= expectedColor.mMinBlue;
- }
- }
-
- public static class PixelColor {
- public static final int BLACK = 0xFF000000;
- public static final int RED = 0xFF0000FF;
- public static final int GREEN = 0xFF00FF00;
- public static final int BLUE = 0xFFFF0000;
- public static final int YELLOW = 0xFF00FFFF;
- public static final int MAGENTA = 0xFFFF00FF;
- public static final int WHITE = 0xFFFFFFFF;
-
- public static final int TRANSPARENT_RED = 0x7F0000FF;
- public static final int TRANSPARENT_BLUE = 0x7FFF0000;
- public static final int TRANSPARENT = 0x00000000;
-
- // Default to black
- public short mMinAlpha;
- public short mMaxAlpha;
- public short mMinRed;
- public short mMaxRed;
- public short mMinBlue;
- public short mMaxBlue;
- public short mMinGreen;
- public short mMaxGreen;
-
- public PixelColor(int color) {
- short alpha = (short) ((color >> 24) & 0xFF);
- short blue = (short) ((color >> 16) & 0xFF);
- short green = (short) ((color >> 8) & 0xFF);
- short red = (short) (color & 0xFF);
-
- mMinAlpha = (short) getMinValue(alpha);
- mMaxAlpha = (short) getMaxValue(alpha);
- mMinRed = (short) getMinValue(red);
- mMaxRed = (short) getMaxValue(red);
- mMinBlue = (short) getMinValue(blue);
- mMaxBlue = (short) getMaxValue(blue);
- mMinGreen = (short) getMinValue(green);
- mMaxGreen = (short) getMaxValue(green);
- }
-
- public PixelColor() {
- this(BLACK);
- }
-
- private int getMinValue(short color) {
- return Math.max(color - 4, 0);
- }
+ public Point getPositionBelowStatusBar() {
+ Insets statusBarInsets = getWindow()
+ .getDecorView()
+ .getRootWindowInsets()
+ .getInsets(statusBars() | displayCutout());
- private int getMaxValue(short color) {
- return Math.min(color + 4, 0xFF);
+ return new Point(statusBarInsets.left, statusBarInsets.top);
}
}
}
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 9f7f3411b2b3..54b33e9794b6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -210,10 +210,8 @@ public class SizeCompatTests extends WindowTestsBase {
assertFitted();
// After the orientation of activity is changed, the display is rotated, the aspect
- // ratio should be the same (bounds=[100, 0 - 800, 583], appBounds=[100, 0 - 800, 583]).
+ // ratio should be the same (bounds=[0, 0 - 800, 583], appBounds=[100, 0 - 800, 583]).
assertEquals(appBounds.width(), appBounds.height() * aspectRatio, 0.5f /* delta */);
- // The notch is no longer on top.
- assertEquals(appBounds, mActivity.getBounds());
// Activity max bounds are sandboxed.
assertActivityMaxBoundsSandboxed();
@@ -467,8 +465,6 @@ public class SizeCompatTests extends WindowTestsBase {
assertEquals(ROTATION_270, mTask.getWindowConfiguration().getRotation());
assertEquals(origBounds.width(), currentBounds.width());
- // The notch is on horizontal side, so current height changes from 1460 to 1400.
- assertEquals(origBounds.height() - notchHeight, currentBounds.height());
// Make sure the app size is the same
assertEquals(origAppBounds.width(), appBounds.width());
assertEquals(origAppBounds.height(), appBounds.height());
@@ -2271,7 +2267,7 @@ public class SizeCompatTests extends WindowTestsBase {
prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
// Bounds are letterboxed to respect the provided max aspect ratio.
- assertEquals(mActivity.getBounds(), new Rect(0, 850, 1000, 1950));
+ assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 1100));
// Move activity to split screen which has landscape size.
mTask.reparent(organizer.mPrimary, POSITION_TOP, /* moveParents */ false, "test");
@@ -2338,6 +2334,8 @@ public class SizeCompatTests extends WindowTestsBase {
@Test
public void testLetterboxDetailsForStatusBar_letterboxNotOverlappingStatusBar() {
+ // Align to center so that we don't overlap with the status bar
+ mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
.setNotch(100)
.build();
@@ -2354,7 +2352,7 @@ public class SizeCompatTests extends WindowTestsBase {
mActivity.mRootWindowContainer.performSurfacePlacement();
Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
- assertEquals(mBounds, new Rect(0, 750, 1000, 1950));
+ assertEquals(mBounds, new Rect(0, 900, 1000, 2000));
DisplayPolicy displayPolicy = mActivity.getDisplayContent().getDisplayPolicy();
LetterboxDetails[] expectedLetterboxDetails = {new LetterboxDetails(
@@ -2454,7 +2452,7 @@ public class SizeCompatTests extends WindowTestsBase {
// At launch.
/* fixedOrientationLetterbox */ new Rect(0, 0, 700, 1400),
// After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(0, 700, 700, 2100),
+ /* sizeCompatUnscaled */ new Rect(0, 0, 700, 1400),
// After the display is resized to (700, 1400).
/* sizeCompatScaled */ new Rect(0, 0, 350, 700));
}
@@ -2467,7 +2465,7 @@ public class SizeCompatTests extends WindowTestsBase {
// At launch.
/* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
// After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(350, 700, 1050, 2100),
+ /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
// After the display is resized to (700, 1400).
/* sizeCompatScaled */ new Rect(525, 0, 875, 700));
}
@@ -2482,7 +2480,7 @@ public class SizeCompatTests extends WindowTestsBase {
// At launch.
/* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
// After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(350, 700, 1050, 2100),
+ /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
// After the display is resized to (700, 1400).
/* sizeCompatScaled */ new Rect(525, 0, 875, 700));
@@ -2492,7 +2490,7 @@ public class SizeCompatTests extends WindowTestsBase {
// At launch.
/* fixedOrientationLetterbox */ new Rect(1050, 0, 1750, 1400),
// After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(350, 700, 1050, 2100),
+ /* sizeCompatUnscaled */ new Rect(350, 0, 1050, 1400),
// After the display is resized to (700, 1400).
/* sizeCompatScaled */ new Rect(525, 0, 875, 700));
}
@@ -2505,7 +2503,7 @@ public class SizeCompatTests extends WindowTestsBase {
// At launch.
/* fixedOrientationLetterbox */ new Rect(2100, 0, 2800, 1400),
// After 90 degree rotation.
- /* sizeCompatUnscaled */ new Rect(700, 700, 1400, 2100),
+ /* sizeCompatUnscaled */ new Rect(700, 0, 1400, 1400),
// After the display is resized to (700, 1400).
/* sizeCompatScaled */ new Rect(1050, 0, 1400, 700));
}
@@ -2534,6 +2532,64 @@ public class SizeCompatTests extends WindowTestsBase {
}
@Test
+ public void testApplyAspectRatio_activityAlignWithParentAppVertical() {
+ // The display's app bounds will be (0, 100, 1000, 2350)
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500)
+ .setCanRotate(false)
+ .setCutout(0, 100, 0, 150)
+ .build();
+
+ setUpApp(display);
+ prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ // The activity height is 2100 and the display's app bounds height is 2250, so the activity
+ // can be aligned inside parentAppBounds
+ assertEquals(mActivity.getBounds(), new Rect(0, 0, 1000, 2200));
+ }
+ @Test
+ public void testApplyAspectRatio_activityCannotAlignWithParentAppVertical() {
+ // The display's app bounds will be (0, 100, 1000, 2150)
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2300)
+ .setCanRotate(false)
+ .setCutout(0, 100, 0, 150)
+ .build();
+
+ setUpApp(display);
+ prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ // The activity height is 2100 and the display's app bounds height is 2050, so the activity
+ // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
+ assertEquals(mActivity.getBounds(), display.getBounds());
+ }
+
+ @Test
+ public void testApplyAspectRatio_activityAlignWithParentAppHorizontal() {
+ // The display's app bounds will be (100, 0, 2350, 1000)
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2500, 1000)
+ .setCanRotate(false)
+ .setCutout(100, 0, 150, 0)
+ .build();
+
+ setUpApp(display);
+ prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ // The activity width is 2100 and the display's app bounds width is 2250, so the activity
+ // can be aligned inside parentAppBounds
+ assertEquals(mActivity.getBounds(), new Rect(175, 0, 2275, 1000));
+ }
+ @Test
+ public void testApplyAspectRatio_activityCannotAlignWithParentAppHorizontal() {
+ // The display's app bounds will be (100, 0, 2150, 1000)
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2300, 1000)
+ .setCanRotate(false)
+ .setCutout(100, 0, 150, 0)
+ .build();
+
+ setUpApp(display);
+ prepareUnresizable(mActivity, 2.1f /* maxAspect */, SCREEN_ORIENTATION_UNSPECIFIED);
+ // The activity width is 2100 and the display's app bounds width is 2050, so the activity
+ // cannot be aligned inside parentAppBounds and it will fill the parentBounds of the display
+ assertEquals(mActivity.getBounds(), display.getBounds());
+ }
+
+ @Test
public void testUpdateResolvedBoundsHorizontalPosition_activityFillParentWidth() {
// When activity width equals parent width, multiplier shouldn't have any effect.
assertHorizontalPositionForDifferentDisplayConfigsForLandscapeActivity(
@@ -2608,6 +2664,25 @@ public class SizeCompatTests extends WindowTestsBase {
/* sizeCompatScaled */ new Rect(0, 1050, 700, 1400));
}
+ @Test
+ public void testUpdateResolvedBoundsPosition_alignToTop() {
+ final int notchHeight = 100;
+ final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2800)
+ .setNotch(notchHeight)
+ .build();
+ setUpApp(display);
+
+ // Prepare unresizable activity with max aspect ratio
+ prepareUnresizable(mActivity, /* maxAspect */ 1.1f, SCREEN_ORIENTATION_UNSPECIFIED);
+
+ Rect mBounds = new Rect(mActivity.getWindowConfiguration().getBounds());
+ Rect appBounds = new Rect(mActivity.getWindowConfiguration().getAppBounds());
+ // The insets should be cut for aspect ratio and then added back because the appBounds
+ // are aligned to the top of the parentAppBounds
+ assertEquals(mBounds, new Rect(0, 0, 1000, 1200));
+ assertEquals(appBounds, new Rect(0, notchHeight, 1000, 1200));
+ }
+
private void assertVerticalPositionForDifferentDisplayConfigsForLandscapeActivity(
float letterboxVerticalPositionMultiplier, Rect fixedOrientationLetterbox,
Rect sizeCompatUnscaled, Rect sizeCompatScaled) {
@@ -2955,7 +3030,7 @@ public class SizeCompatTests extends WindowTestsBase {
rotateDisplay(mActivity.mDisplayContent, ROTATION_90);
assertTrue(mActivity.inSizeCompatMode());
// Activity is in size compat mode but not scaled.
- assertEquals(new Rect(0, 1050, 1400, 1750), mActivity.getBounds());
+ assertEquals(new Rect(0, 0, 1400, 700), mActivity.getBounds());
}
private void assertVerticalPositionForDifferentDisplayConfigsForPortraitActivity(
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index ff0063c4ffa0..6c8a7ac0c613 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -361,7 +361,7 @@ public class SurfaceAnimatorTest extends WindowTestsBase {
@Override
public Builder makeAnimationLeash() {
- return new SurfaceControl.Builder(mSession) {
+ return new Builder(mSession) {
@Override
public SurfaceControl build() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index f5fc5c13eb4c..70e6f2947c36 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -39,6 +39,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static org.mockito.Mockito.CALLS_REAL_METHODS;
+import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
import android.app.ActivityManagerInternal;
@@ -81,6 +82,7 @@ import com.android.server.am.ActivityManagerService;
import com.android.server.display.color.ColorDisplayService;
import com.android.server.firewall.IntentFirewall;
import com.android.server.input.InputManagerService;
+import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserManagerService;
import com.android.server.policy.PermissionPolicyInternal;
import com.android.server.policy.WindowManagerPolicy;
@@ -93,6 +95,7 @@ import org.junit.runners.model.Statement;
import org.mockito.MockSettings;
import org.mockito.Mockito;
import org.mockito.quality.Strictness;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -283,6 +286,16 @@ public class SystemServicesTestRule implements TestRule {
// StatusBarManagerInternal
final StatusBarManagerInternal sbmi = mock(StatusBarManagerInternal.class);
doReturn(sbmi).when(() -> LocalServices.getService(eq(StatusBarManagerInternal.class)));
+
+ // UserManagerInternal
+ final UserManagerInternal umi = mock(UserManagerInternal.class);
+ doReturn(umi).when(() -> LocalServices.getService(UserManagerInternal.class));
+ Answer<Boolean> isUserVisibleAnswer = invocation -> {
+ int userId = invocation.getArgument(0);
+ return userId == mWmService.mCurrentUserId;
+ };
+ when(umi.isUserVisible(anyInt())).thenAnswer(isUserVisibleAnswer);
+ when(umi.isUserVisible(anyInt(), anyInt())).thenAnswer(isUserVisibleAnswer);
}
private void setUpActivityTaskManagerService() {
@@ -403,6 +416,7 @@ public class SystemServicesTestRule implements TestRule {
LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+ LocalServices.removeServiceForTest(UserManagerInternal.class);
}
Description getDescription() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
index 9bdf750767b3..1404de253476 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.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_PINNED;
+import static android.view.Display.DEFAULT_DISPLAY;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_OP_TYPE;
import static android.window.TaskFragmentOrganizer.KEY_ERROR_CALLBACK_THROWABLE;
import static android.window.TaskFragmentOrganizer.getTransitionType;
@@ -76,6 +77,7 @@ import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
import android.window.TaskFragmentOrganizer;
import android.window.TaskFragmentOrganizerToken;
+import android.window.TaskFragmentParentInfo;
import android.window.TaskFragmentTransaction;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
@@ -271,7 +273,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
@Test
public void testOnTaskFragmentParentInfoChanged() {
setupMockParent(mTaskFragment, mTask);
- mTask.getConfiguration().smallestScreenWidthDp = 10;
+ mTask.getTaskFragmentParentInfo().getConfiguration().smallestScreenWidthDp = 10;
mController.onTaskFragmentAppeared(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
@@ -295,7 +297,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Trigger callback if the size is changed.
clearInvocations(mOrganizer);
- mTask.getConfiguration().smallestScreenWidthDp = 100;
+ mTask.getTaskFragmentParentInfo().getConfiguration().smallestScreenWidthDp = 100;
mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
@@ -304,7 +306,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
// Trigger callback if the windowing mode is changed.
clearInvocations(mOrganizer);
- mTask.getConfiguration().windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
+ mTask.getTaskFragmentParentInfo().getConfiguration().windowConfiguration
+ .setWindowingMode(WINDOWING_MODE_PINNED);
mController.onTaskFragmentInfoChanged(
mTaskFragment.getTaskFragmentOrganizer(), mTaskFragment);
mController.dispatchPendingEvents();
@@ -727,6 +730,16 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
}
@Test
+ public void testApplyTransaction_finishActivity() {
+ final ActivityRecord activity = createActivityRecord(mDisplayContent);
+
+ mTransaction.finishActivity(activity.token);
+ assertApplyTransactionAllowed(mTransaction);
+
+ assertTrue(activity.finishing);
+ }
+
+ @Test
public void testApplyTransaction_skipTransactionForUnregisterOrganizer() {
mController.unregisterOrganizer(mIOrganizer);
final ActivityRecord ownerActivity = createActivityRecord(mDisplayContent);
@@ -1268,7 +1281,7 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
final TaskFragmentTransaction.Change change = changes.get(0);
assertEquals(TYPE_TASK_FRAGMENT_PARENT_INFO_CHANGED, change.getType());
assertEquals(task.mTaskId, change.getTaskId());
- assertEquals(task.getConfiguration(), change.getTaskConfiguration());
+ assertEquals(task.getTaskFragmentParentInfo(), change.getTaskFragmentParentInfo());
}
/** Asserts that there will be a transaction for TaskFragment error. */
@@ -1316,8 +1329,8 @@ public class TaskFragmentOrganizerControllerTest extends WindowTestsBase {
/** Setups the mock Task as the parent of the given TaskFragment. */
private static void setupMockParent(TaskFragment taskFragment, Task mockParent) {
doReturn(mockParent).when(taskFragment).getTask();
- final Configuration taskConfig = new Configuration();
- doReturn(taskConfig).when(mockParent).getConfiguration();
+ doReturn(new TaskFragmentParentInfo(new Configuration(), DEFAULT_DISPLAY, true))
+ .when(mockParent).getTaskFragmentParentInfo();
// Task needs to be visible
mockParent.lastActiveTime = 100;
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 aa3ca18073c3..bf1d1fa98249 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -143,11 +143,24 @@ class TestDisplayContent extends DisplayContent {
mInfo.ownerUid = ownerUid;
return this;
}
- Builder setNotch(int height) {
+ Builder setCutout(int left, int top, int right, int bottom) {
+ final int cutoutFillerSize = 80;
+ Rect boundLeft = left != 0 ? new Rect(0, 0, left, cutoutFillerSize) : null;
+ Rect boundTop = top != 0 ? new Rect(0, 0, cutoutFillerSize, top) : null;
+ Rect boundRight = right != 0 ? new Rect(mInfo.logicalWidth - right, 0,
+ mInfo.logicalWidth, cutoutFillerSize) : null;
+ Rect boundBottom = bottom != 0
+ ? new Rect(0, mInfo.logicalHeight - bottom, cutoutFillerSize,
+ mInfo.logicalHeight) : null;
+
mInfo.displayCutout = new DisplayCutout(
- Insets.of(0, height, 0, 0), null, new Rect(20, 0, 80, height), null, null);
+ Insets.of(left, top, right, bottom),
+ boundLeft, boundTop, boundRight, boundBottom);
return this;
}
+ Builder setNotch(int height) {
+ return setCutout(0, height, 0, 0);
+ }
Builder setStatusBarHeight(int height) {
mStatusBarHeight = height;
return this;
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 048cd7c68aa6..51894f32883d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -19,6 +19,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_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
@@ -31,7 +32,9 @@ import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_TO_BACK;
-import static android.window.TransitionInfo.FLAG_IS_EMBEDDED;
+import static android.window.TransitionInfo.FLAG_FILLS_TASK;
+import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
@@ -730,8 +733,13 @@ public class TransitionTests extends WindowTestsBase {
assertTrue(ime.mToken.inTransition());
assertTrue(task.inTransition());
assertTrue(asyncRotationController.isTargetToken(decorToken));
- assertTrue(asyncRotationController.shouldFreezeInsetsPosition(navBar));
+ assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
+ if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) {
+ // Only seamless window syncs its draw transaction with transition.
+ assertFalse(asyncRotationController.handleFinishDrawing(statusBar, mMockT));
+ assertTrue(asyncRotationController.handleFinishDrawing(screenDecor, mMockT));
+ }
screenDecor.setOrientationChanging(false);
// Status bar finishes drawing before the start transaction. Its fade-in animation will be
// executed until the transaction is committed, so it is still in target tokens.
@@ -745,6 +753,7 @@ public class TransitionTests extends WindowTestsBase {
// The transaction is committed, so fade-in animation for status bar is consumed.
transactionCommittedListener.onTransactionCommitted();
assertFalse(asyncRotationController.isTargetToken(statusBar.mToken));
+ assertShouldFreezeInsetsPosition(asyncRotationController, navBar, false);
// Navigation bar finishes drawing after the start transaction, so its fade-in animation
// can execute directly.
@@ -780,7 +789,7 @@ public class TransitionTests extends WindowTestsBase {
final AsyncRotationController asyncRotationController =
mDisplayContent.getAsyncRotationController();
assertNotNull(asyncRotationController);
- assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar));
+ assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
statusBar.setOrientationChanging(true);
player.startTransition();
@@ -826,7 +835,7 @@ public class TransitionTests extends WindowTestsBase {
final AsyncRotationController asyncRotationController =
mDisplayContent.getAsyncRotationController();
assertNotNull(asyncRotationController);
- assertTrue(asyncRotationController.shouldFreezeInsetsPosition(statusBar));
+ assertShouldFreezeInsetsPosition(asyncRotationController, statusBar, true);
assertTrue(app.getTask().inTransition());
player.start();
@@ -861,6 +870,15 @@ public class TransitionTests extends WindowTestsBase {
assertNull(mDisplayContent.getAsyncRotationController());
}
+ private static void assertShouldFreezeInsetsPosition(AsyncRotationController controller,
+ WindowState w, boolean freeze) {
+ if (TransitionController.SYNC_METHOD != BLASTSyncEngine.METHOD_BLAST) {
+ // Non blast sync should never freeze insets position.
+ freeze = false;
+ }
+ assertEquals(freeze, controller.shouldFreezeInsetsPosition(w));
+ }
+
@Test
public void testDeferRotationForTransientLaunch() {
final TestTransitionPlayer player = registerTestTransitionPlayer();
@@ -1065,12 +1083,47 @@ public class TransitionTests extends WindowTestsBase {
}
@Test
- public void testIsEmbeddedChange() {
+ public void testIsBehindStartingWindowChange() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task task = createTask(mDisplayContent);
+ final ActivityRecord activity0 = createActivityRecord(task);
+ final ActivityRecord activity1 = createActivityRecord(task);
+ doReturn(true).when(activity1).hasStartingWindow();
+
+ // Start states.
+ changes.put(activity0, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(activity1, new Transition.ChangeInfo(false /* vis */, false /* exChg */));
+ // End states.
+ activity0.mVisibleRequested = false;
+ activity1.mVisibleRequested = true;
+
+ participants.add(activity0);
+ participants.add(activity1);
+ final ArrayList<WindowContainer> targets = Transition.calculateTargets(
+ participants, changes);
+ final TransitionInfo info = Transition.calculateTransitionInfo(
+ transition.mType, 0 /* flags */, targets, changes, mMockT);
+
+ // All windows in the Task should have FLAG_IS_BEHIND_STARTING_WINDOW because the starting
+ // window should cover the whole Task.
+ assertEquals(2, info.getChanges().size());
+ assertTrue(info.getChanges().get(0).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW));
+ assertTrue(info.getChanges().get(1).hasFlags(FLAG_IS_BEHIND_STARTING_WINDOW));
+
+ }
+
+ @Test
+ public void testFlagInTaskWithEmbeddedActivity() {
final Transition transition = createTestTransition(TRANSIT_OPEN);
final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
final ArraySet<WindowContainer> participants = transition.mParticipants;
final Task task = createTask(mDisplayContent);
+ final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
+ assertFalse(nonEmbeddedActivity.isEmbedded());
final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
mAtm.mTaskFragmentOrganizerController.registerOrganizer(
ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
@@ -1085,20 +1138,133 @@ public class TransitionTests extends WindowTestsBase {
changes.put(embeddedTf, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
changes.put(closingActivity, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
changes.put(openingActivity, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(true /* vis */,
+ false /* exChg */));
// End states.
closingActivity.mVisibleRequested = false;
openingActivity.mVisibleRequested = true;
+ nonEmbeddedActivity.mVisibleRequested = false;
participants.add(closingActivity);
participants.add(openingActivity);
+ participants.add(nonEmbeddedActivity);
+ final ArrayList<WindowContainer> targets = Transition.calculateTargets(
+ participants, changes);
+ final TransitionInfo info = Transition.calculateTransitionInfo(
+ transition.mType, 0 /* flags */, targets, changes, mMockT);
+
+ // All windows in the Task should have FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY because the Task
+ // contains embedded activity.
+ assertEquals(3, info.getChanges().size());
+ assertTrue(info.getChanges().get(0).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
+ assertTrue(info.getChanges().get(1).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
+ assertTrue(info.getChanges().get(2).hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY));
+ }
+
+ @Test
+ public void testFlagFillsTask_embeddingNotFillingTask() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task task = createTask(mDisplayContent);
+ // Set to multi-windowing mode in order to set bounds.
+ task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ final Rect taskBounds = new Rect(0, 0, 500, 1000);
+ task.setBounds(taskBounds);
+ final ActivityRecord nonEmbeddedActivity = createActivityRecord(task);
+ final TaskFragmentOrganizer organizer = new TaskFragmentOrganizer(Runnable::run);
+ mAtm.mTaskFragmentOrganizerController.registerOrganizer(
+ ITaskFragmentOrganizer.Stub.asInterface(organizer.getOrganizerToken().asBinder()));
+ final TaskFragment embeddedTf = new TaskFragmentBuilder(mAtm)
+ .setParentTask(task)
+ .createActivityCount(1)
+ .setOrganizer(organizer)
+ .build();
+ final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
+ // Start states.
+ changes.put(task, new Transition.ChangeInfo(true /* vis */, false /* exChg */));
+ changes.put(nonEmbeddedActivity, new Transition.ChangeInfo(true /* vis */,
+ false /* exChg */));
+ changes.put(embeddedTf, new Transition.ChangeInfo(false /* vis */, true /* exChg */));
+ // End states.
+ nonEmbeddedActivity.mVisibleRequested = false;
+ embeddedActivity.mVisibleRequested = true;
+ embeddedTf.setBounds(new Rect(0, 0, 500, 500));
+
+ participants.add(nonEmbeddedActivity);
+ participants.add(embeddedTf);
final ArrayList<WindowContainer> targets = Transition.calculateTargets(
participants, changes);
final TransitionInfo info = Transition.calculateTransitionInfo(
transition.mType, 0 /* flags */, targets, changes, mMockT);
+ // The embedded with bounds overridden should not have the flag.
assertEquals(2, info.getChanges().size());
- assertTrue((info.getChanges().get(0).getFlags() & FLAG_IS_EMBEDDED) != 0);
- assertTrue((info.getChanges().get(1).getFlags() & FLAG_IS_EMBEDDED) != 0);
+ assertFalse(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
+ assertEquals(embeddedTf.getBounds(), info.getChanges().get(0).getEndAbsBounds());
+ assertFalse(info.getChanges().get(1).hasFlags(FLAG_FILLS_TASK));
+ }
+
+ @Test
+ public void testFlagFillsTask_openActivityFillingTask() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task task = createTask(mDisplayContent);
+ // Set to multi-windowing mode in order to set bounds.
+ task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ final Rect taskBounds = new Rect(0, 0, 500, 1000);
+ task.setBounds(taskBounds);
+ final ActivityRecord activity = createActivityRecord(task);
+ // Start states: set bounds to make sure the start bounds is ignored if it is not visible.
+ activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500));
+ activity.mVisibleRequested = false;
+ changes.put(activity, new Transition.ChangeInfo(activity));
+ // End states: reset bounds to fill Task.
+ activity.getConfiguration().windowConfiguration.setBounds(taskBounds);
+ activity.mVisibleRequested = true;
+
+ participants.add(activity);
+ final ArrayList<WindowContainer> targets = Transition.calculateTargets(
+ participants, changes);
+ final TransitionInfo info = Transition.calculateTransitionInfo(
+ transition.mType, 0 /* flags */, targets, changes, mMockT);
+
+ // Opening activity that is filling Task after transition should have the flag.
+ assertEquals(1, info.getChanges().size());
+ assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
+ }
+
+ @Test
+ public void testFlagFillsTask_closeActivityFillingTask() {
+ final Transition transition = createTestTransition(TRANSIT_CLOSE);
+ final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ final Task task = createTask(mDisplayContent);
+ // Set to multi-windowing mode in order to set bounds.
+ task.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ final Rect taskBounds = new Rect(0, 0, 500, 1000);
+ task.setBounds(taskBounds);
+ final ActivityRecord activity = createActivityRecord(task);
+ // Start states: fills Task without override.
+ activity.mVisibleRequested = true;
+ changes.put(activity, new Transition.ChangeInfo(activity));
+ // End states: set bounds to make sure the start bounds is ignored if it is not visible.
+ activity.getConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 250, 500));
+ activity.mVisibleRequested = false;
+
+ participants.add(activity);
+ final ArrayList<WindowContainer> targets = Transition.calculateTargets(
+ participants, changes);
+ final TransitionInfo info = Transition.calculateTransitionInfo(
+ transition.mType, 0 /* flags */, targets, changes, mMockT);
+
+ // Closing activity that is filling Task before transition should have the flag.
+ assertEquals(1, info.getChanges().size());
+ assertTrue(info.getChanges().get(0).hasFlags(FLAG_FILLS_TASK));
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
index 168567332795..f6d0bf110047 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContextListenerControllerTests.java
@@ -213,7 +213,7 @@ public class WindowContextListenerControllerTests extends WindowTestsBase {
public void testImeSwitchDialogWindowTokenRemovedOnDualDisplayContent_ListenToImeContainer() {
// Let the Display to be created with the DualDisplay policy.
final DisplayAreaPolicy.Provider policyProvider =
- new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider();
+ new DualDisplayAreaGroupPolicyTest.DualDisplayTestPolicyProvider(mWm);
doReturn(policyProvider).when(mWm).getDisplayAreaPolicyProvider();
// Create a DisplayContent with dual RootDisplayArea
DualDisplayAreaGroupPolicyTest.DualDisplayContent dualDisplayContent =
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 46b4b76dc12f..8b63904baf31 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -41,6 +41,7 @@ import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_BACKGROUND_
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.assertNull;
import static org.mockito.ArgumentMatchers.any;
@@ -68,6 +69,7 @@ import android.view.SurfaceControl;
import android.view.View;
import android.view.WindowManager;
import android.window.ClientWindowFrames;
+import android.window.ScreenCapture;
import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
@@ -423,6 +425,45 @@ public class WindowManagerServiceTests extends WindowTestsBase {
LETTERBOX_BACKGROUND_SOLID_COLOR)).isFalse();
}
+ @Test
+ public void testCaptureDisplay() {
+ Rect displayBounds = new Rect(0, 0, 100, 200);
+ spyOn(mDisplayContent);
+ when(mDisplayContent.getBounds()).thenReturn(displayBounds);
+
+ // Null captureArgs
+ ScreenCapture.LayerCaptureArgs resultingArgs =
+ mWm.getCaptureArgs(DEFAULT_DISPLAY, null /* captureArgs */);
+ assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+ // Non null captureArgs, didn't set rect
+ ScreenCapture.CaptureArgs captureArgs = new ScreenCapture.CaptureArgs.Builder<>().build();
+ resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+ assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+ // Non null captureArgs, invalid rect
+ captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+ .setSourceCrop(new Rect(0, 0, -1, -1))
+ .build();
+ resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+ assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+ // Non null captureArgs, null rect
+ captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+ .setSourceCrop(null)
+ .build();
+ resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+ assertEquals(displayBounds, resultingArgs.mSourceCrop);
+
+ // Non null captureArgs, valid rect
+ Rect validRect = new Rect(0, 0, 10, 50);
+ captureArgs = new ScreenCapture.CaptureArgs.Builder<>()
+ .setSourceCrop(validRect)
+ .build();
+ resultingArgs = mWm.getCaptureArgs(DEFAULT_DISPLAY, captureArgs);
+ assertEquals(validRect, resultingArgs.mSourceCrop);
+ }
+
private void setupActivityWithLaunchCookie(IBinder launchCookie, WindowContainerToken wct) {
final WindowContainer.RemoteToken remoteToken = mock(WindowContainer.RemoteToken.class);
when(remoteToken.toWindowContainerToken()).thenReturn(wct);
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 353b757e985e..9c9f5db68d09 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -97,6 +97,7 @@ import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.DisplayImePolicy;
import android.window.ITransitionPlayer;
+import android.window.ScreenCapture;
import android.window.StartingWindowInfo;
import android.window.StartingWindowRemovalInfo;
import android.window.TaskFragmentOrganizer;
@@ -241,7 +242,7 @@ class WindowTestsBase extends SystemServiceTestsBase {
// Ensure letterbox vertical position multiplier is not overridden on any device target.
// {@link com.android.internal.R.dimen.config_letterboxHorizontalPositionMultiplier},
// may be set on some device form factors.
- mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.5f);
+ mAtm.mWindowManager.mLetterboxConfiguration.setLetterboxVerticalPositionMultiplier(0.0f);
// Ensure letterbox horizontal reachability treatment isn't overridden on any device target.
// {@link com.android.internal.R.bool.config_letterboxIsHorizontalReachabilityEnabled},
// may be set on some device form factors.
@@ -946,8 +947,8 @@ class WindowTestsBase extends SystemServiceTestsBase {
/** Mocks the behavior of taking a snapshot. */
void mockSurfaceFreezerSnapshot(SurfaceFreezer surfaceFreezer) {
- final SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
- mock(SurfaceControl.ScreenshotHardwareBuffer.class);
+ final ScreenCapture.ScreenshotHardwareBuffer screenshotBuffer =
+ mock(ScreenCapture.ScreenshotHardwareBuffer.class);
final HardwareBuffer hardwareBuffer = mock(HardwareBuffer.class);
spyOn(surfaceFreezer);
doReturn(screenshotBuffer).when(surfaceFreezer)
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 77fca451547d..a95fa5a4e978 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -147,7 +147,7 @@ public class ZOrderingTests extends WindowTestsBase {
}
private static class HierarchyRecordingBuilderFactory implements Function<SurfaceSession,
- SurfaceControl.Builder> {
+ SurfaceControl.Builder> {
private LayerRecordingTransaction mTransaction;
HierarchyRecordingBuilderFactory(LayerRecordingTransaction transaction) {
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index eafcef2f1d38..1e74451a8d4d 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -210,21 +210,15 @@ final class TranslationManagerServiceImpl extends
final int translatedAppUid =
getAppUidByComponentName(getContext(), componentName, getUserId());
final String packageName = componentName.getPackageName();
- if (activityDestroyed) {
- // In the Activity destroy case, we only calls onTranslationFinished() in
- // non-finisTranslation() state. If there is a finisTranslation() calls by apps, we
- // should remove the waiting callback to avoid callback twice.
+ // In the Activity destroyed case, we only call onTranslationFinished() in
+ // non-finishTranslation() state. If there is a finishTranslation() call by apps, we
+ // should remove the waiting callback to avoid invoking callbacks twice.
+ if (activityDestroyed || mWaitingFinishedCallbackActivities.contains(token)) {
invokeCallbacks(STATE_UI_TRANSLATION_FINISHED,
/* sourceSpec= */ null, /* targetSpec= */ null,
packageName, translatedAppUid);
mWaitingFinishedCallbackActivities.remove(token);
- } else {
- if (mWaitingFinishedCallbackActivities.contains(token)) {
- invokeCallbacks(STATE_UI_TRANSLATION_FINISHED,
- /* sourceSpec= */ null, /* targetSpec= */ null,
- packageName, translatedAppUid);
- mWaitingFinishedCallbackActivities.remove(token);
- }
+ mActiveTranslations.remove(token);
}
}
@@ -237,6 +231,9 @@ final class TranslationManagerServiceImpl extends
// Activity is the new Activity, the original Activity is paused in the same task.
// To make sure the operation still work, we use the token to find the target Activity in
// this task, not the top Activity only.
+ //
+ // Note: getAttachedNonFinishingActivityForTask() takes the shareable activity token. We
+ // call this method so that we can get the regular activity token below.
ActivityTokens candidateActivityTokens =
mActivityTaskManagerInternal.getAttachedNonFinishingActivityForTask(taskId, token);
if (candidateActivityTokens == null) {
@@ -263,27 +260,27 @@ final class TranslationManagerServiceImpl extends
getAppUidByComponentName(getContext(), componentName, getUserId());
String packageName = componentName.getPackageName();
- invokeCallbacksIfNecessaryLocked(state, sourceSpec, targetSpec, packageName, activityToken,
+ invokeCallbacksIfNecessaryLocked(state, sourceSpec, targetSpec, packageName, token,
translatedAppUid);
- updateActiveTranslationsLocked(state, sourceSpec, targetSpec, packageName, activityToken,
+ updateActiveTranslationsLocked(state, sourceSpec, targetSpec, packageName, token,
translatedAppUid);
}
@GuardedBy("mLock")
private void updateActiveTranslationsLocked(int state, TranslationSpec sourceSpec,
- TranslationSpec targetSpec, String packageName, IBinder activityToken,
+ TranslationSpec targetSpec, String packageName, IBinder shareableActivityToken,
int translatedAppUid) {
// We keep track of active translations and their state so that we can:
// 1. Trigger callbacks that are registered after translation has started.
// See registerUiTranslationStateCallbackLocked().
// 2. NOT trigger callbacks when the state didn't change.
// See invokeCallbacksIfNecessaryLocked().
- ActiveTranslation activeTranslation = mActiveTranslations.get(activityToken);
+ ActiveTranslation activeTranslation = mActiveTranslations.get(shareableActivityToken);
switch (state) {
case STATE_UI_TRANSLATION_STARTED: {
if (activeTranslation == null) {
try {
- activityToken.linkToDeath(this, /* flags= */ 0);
+ shareableActivityToken.linkToDeath(this, /* flags= */ 0);
} catch (RemoteException e) {
Slog.w(TAG, "Failed to call linkToDeath for translated app with uid="
+ translatedAppUid + "; activity is already dead", e);
@@ -294,7 +291,7 @@ final class TranslationManagerServiceImpl extends
packageName, translatedAppUid);
return;
}
- mActiveTranslations.put(activityToken,
+ mActiveTranslations.put(shareableActivityToken,
new ActiveTranslation(sourceSpec, targetSpec, translatedAppUid,
packageName));
}
@@ -317,7 +314,7 @@ final class TranslationManagerServiceImpl extends
case STATE_UI_TRANSLATION_FINISHED: {
if (activeTranslation != null) {
- mActiveTranslations.remove(activityToken);
+ mActiveTranslations.remove(shareableActivityToken);
}
break;
}
@@ -332,12 +329,12 @@ final class TranslationManagerServiceImpl extends
@GuardedBy("mLock")
private void invokeCallbacksIfNecessaryLocked(int state, TranslationSpec sourceSpec,
- TranslationSpec targetSpec, String packageName, IBinder activityToken,
+ TranslationSpec targetSpec, String packageName, IBinder shareableActivityToken,
int translatedAppUid) {
boolean shouldInvokeCallbacks = true;
int stateForCallbackInvocation = state;
- ActiveTranslation activeTranslation = mActiveTranslations.get(activityToken);
+ ActiveTranslation activeTranslation = mActiveTranslations.get(shareableActivityToken);
if (activeTranslation == null) {
if (state != STATE_UI_TRANSLATION_STARTED) {
shouldInvokeCallbacks = false;
@@ -403,14 +400,6 @@ final class TranslationManagerServiceImpl extends
}
}
- if (DEBUG) {
- Slog.d(TAG,
- (shouldInvokeCallbacks ? "" : "NOT ")
- + "Invoking callbacks for translation state="
- + stateForCallbackInvocation + " for app with uid=" + translatedAppUid
- + " packageName=" + packageName);
- }
-
if (shouldInvokeCallbacks) {
invokeCallbacks(stateForCallbackInvocation, sourceSpec, targetSpec, packageName,
translatedAppUid);
@@ -448,7 +437,7 @@ final class TranslationManagerServiceImpl extends
pw.println(waitingFinishCallbackSize);
for (IBinder activityToken : mWaitingFinishedCallbackActivities) {
pw.print(prefix);
- pw.print("activityToken: ");
+ pw.print("shareableActivityToken: ");
pw.println(activityToken);
}
}
@@ -458,7 +447,14 @@ final class TranslationManagerServiceImpl extends
int state, TranslationSpec sourceSpec, TranslationSpec targetSpec, String packageName,
int translatedAppUid) {
Bundle result = createResultForCallback(state, sourceSpec, targetSpec, packageName);
- if (mCallbacks.getRegisteredCallbackCount() == 0) {
+ int registeredCallbackCount = mCallbacks.getRegisteredCallbackCount();
+ if (DEBUG) {
+ Slog.d(TAG, "Invoking " + registeredCallbackCount + " callbacks for translation state="
+ + state + " for app with uid=" + translatedAppUid
+ + " packageName=" + packageName);
+ }
+
+ if (registeredCallbackCount == 0) {
return;
}
List<InputMethodInfo> enabledInputMethods = getEnabledInputMethods();
@@ -521,8 +517,10 @@ final class TranslationManagerServiceImpl extends
@GuardedBy("mLock")
public void registerUiTranslationStateCallbackLocked(IRemoteCallback callback, int sourceUid) {
mCallbacks.register(callback, sourceUid);
-
- if (mActiveTranslations.size() == 0) {
+ int numActiveTranslations = mActiveTranslations.size();
+ Slog.i(TAG, "New registered callback for sourceUid=" + sourceUid + " with currently "
+ + numActiveTranslations + " active translations");
+ if (numActiveTranslations == 0) {
return;
}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 046ff663f6c9..7f5beb1f6cf0 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -233,6 +233,7 @@ public class UsageStatsService extends SystemService implements
final SparseArray<ActivityData> mVisibleActivities = new SparseArray();
@GuardedBy("mLock")
private final SparseArray<LaunchTimeAlarmQueue> mLaunchTimeAlarmQueues = new SparseArray<>();
+ @GuardedBy("mUsageEventListeners") // Don't hold the main lock when calling out
private final ArraySet<UsageStatsManagerInternal.UsageEventListener> mUsageEventListeners =
new ArraySet<>();
private final CopyOnWriteArraySet<UsageStatsManagerInternal.EstimatedLaunchTimeChangedListener>
@@ -1189,9 +1190,11 @@ public class UsageStatsService extends SystemService implements
service.reportEvent(event);
}
- final int size = mUsageEventListeners.size();
- for (int i = 0; i < size; ++i) {
- mUsageEventListeners.valueAt(i).onUsageEvent(userId, event);
+ synchronized (mUsageEventListeners) {
+ final int size = mUsageEventListeners.size();
+ for (int i = 0; i < size; ++i) {
+ mUsageEventListeners.valueAt(i).onUsageEvent(userId, event);
+ }
}
}
@@ -1682,7 +1685,7 @@ public class UsageStatsService extends SystemService implements
* Called via the local interface.
*/
private void registerListener(@NonNull UsageStatsManagerInternal.UsageEventListener listener) {
- synchronized (mLock) {
+ synchronized (mUsageEventListeners) {
mUsageEventListeners.add(listener);
}
}
@@ -1692,7 +1695,7 @@ public class UsageStatsService extends SystemService implements
*/
private void unregisterListener(
@NonNull UsageStatsManagerInternal.UsageEventListener listener) {
- synchronized (mLock) {
+ synchronized (mUsageEventListeners) {
mUsageEventListeners.remove(listener);
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 9562c1a5bcf9..e90a376e90f8 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -514,6 +514,8 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
private boolean mConfigured;
private boolean mAudioAccessoryConnected;
private boolean mAudioAccessorySupported;
+ private boolean mConnectedToDataDisabledPort;
+ private int mPowerBrickConnectionStatus;
private UsbAccessory mCurrentAccessory;
private int mUsbNotificationId;
@@ -952,12 +954,19 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
&& status.isRoleCombinationSupported(POWER_ROLE_SOURCE,
DATA_ROLE_DEVICE)
&& status.isRoleCombinationSupported(POWER_ROLE_SINK, DATA_ROLE_DEVICE);
+
+ boolean usbDataDisabled =
+ status.getUsbDataStatus() != UsbPortStatus.DATA_STATUS_ENABLED;
+ mConnectedToDataDisabledPort = status.isConnected() && usbDataDisabled;
+ mPowerBrickConnectionStatus = status.getPowerBrickConnectionStatus();
} else {
mHostConnected = false;
mSourcePower = false;
mSinkPower = false;
mAudioAccessoryConnected = false;
mSupportsAllCombinations = false;
+ mConnectedToDataDisabledPort = false;
+ mPowerBrickConnectionStatus = UsbPortStatus.POWER_BRICK_STATUS_UNKNOWN;
}
if (mHostConnected) {
@@ -1265,6 +1274,12 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser
} else if (mHostConnected && mSinkPower && (mUsbCharging || mUsbAccessoryConnected)) {
titleRes = com.android.internal.R.string.usb_charging_notification_title;
id = SystemMessage.NOTE_USB_CHARGING;
+ } else if (mSinkPower && mConnectedToDataDisabledPort
+ && mPowerBrickConnectionStatus != UsbPortStatus.POWER_BRICK_STATUS_CONNECTED) {
+ // Show charging notification when USB Data is disabled on the port, and not
+ // connected to a wall charger.
+ titleRes = com.android.internal.R.string.usb_charging_notification_title;
+ id = SystemMessage.NOTE_USB_CHARGING;
}
if (id != mUsbNotificationId || force) {
// clear notification if title needs changing
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index a1310849d84f..921f6e20c3ad 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -22,6 +22,7 @@ import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN;
import static android.service.voice.HotwordDetectedResult.EXTRA_PROXIMITY_METERS;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_EXTERNAL;
import static android.service.voice.HotwordDetectionService.AUDIO_SOURCE_MICROPHONE;
+import static android.service.voice.HotwordDetectionService.ENABLE_PROXIMITY_RESULT;
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_SUCCESS;
import static android.service.voice.HotwordDetectionService.INITIALIZATION_STATUS_UNKNOWN;
import static android.service.voice.HotwordDetectionService.KEY_INITIALIZATION_STATUS;
@@ -189,7 +190,7 @@ final class HotwordDetectionConnection {
final int mUser;
final Context mContext;
- @Nullable final AttentionManagerInternal mAttentionManagerInternal;
+ @Nullable AttentionManagerInternal mAttentionManagerInternal = null;
final AttentionManagerInternal.ProximityUpdateCallbackInternal mProximityCallbackInternal =
this::setProximityMeters;
@@ -244,9 +245,11 @@ final class HotwordDetectionConnection {
mServiceConnectionFactory = new ServiceConnectionFactory(intent, bindInstantServiceAllowed);
mRemoteHotwordDetectionService = mServiceConnectionFactory.createLocked();
- mAttentionManagerInternal = LocalServices.getService(AttentionManagerInternal.class);
- if (mAttentionManagerInternal != null) {
- mAttentionManagerInternal.onStartProximityUpdates(mProximityCallbackInternal);
+ if (ENABLE_PROXIMITY_RESULT) {
+ mAttentionManagerInternal = LocalServices.getService(AttentionManagerInternal.class);
+ if (mAttentionManagerInternal != null) {
+ mAttentionManagerInternal.onStartProximityUpdates(mProximityCallbackInternal);
+ }
}
mLastRestartInstant = Instant.now();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 4b9b4a9781de..be7c1122ae6e 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -95,6 +95,7 @@ import com.android.internal.app.IVoiceInteractor;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.LatencyTracker;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -135,9 +136,6 @@ public class VoiceInteractionManagerService extends SystemService {
private final RemoteCallbackList<IVoiceInteractionSessionListener>
mVoiceInteractionSessionListeners = new RemoteCallbackList<>();
- // TODO(b/226201975): remove once RoleService supports pre-created users
- private final ArrayList<UserHandle> mIgnoredPreCreatedUsers = new ArrayList<>();
-
public VoiceInteractionManagerService(Context context) {
super(context);
mContext = context;
@@ -191,6 +189,8 @@ public class VoiceInteractionManagerService extends SystemService {
mSoundTriggerInternal = LocalServices.getService(SoundTriggerInternal.class);
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
mServiceStub.systemRunning(isSafeMode());
+ } else if (phase == PHASE_BOOT_COMPLETED) {
+ mServiceStub.registerVoiceInteractionSessionListener(mLatencyLoggingListener);
}
}
@@ -306,24 +306,14 @@ public class VoiceInteractionManagerService extends SystemService {
return hotwordDetectionConnection.mIdentity;
}
+ // TODO(b/226201975): remove this method once RoleService supports pre-created users
@Override
public void onPreCreatedUserConversion(int userId) {
- Slogf.d(TAG, "onPreCreatedUserConversion(%d)", userId);
-
- for (int i = 0; i < mIgnoredPreCreatedUsers.size(); i++) {
- UserHandle preCreatedUser = mIgnoredPreCreatedUsers.get(i);
- if (preCreatedUser.getIdentifier() == userId) {
- Slogf.d(TAG, "Updating role on pre-created user %d", userId);
- mServiceStub.mRoleObserver.onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT,
- preCreatedUser);
- mIgnoredPreCreatedUsers.remove(i);
- return;
- }
- }
- Slogf.w(TAG, "onPreCreatedUserConversion(%d): not available on "
- + "mIgnoredPreCreatedUserIds (%s)", userId, mIgnoredPreCreatedUsers);
+ Slogf.d(TAG, "onPreCreatedUserConversion(%d): calling onRoleHoldersChanged() again",
+ userId);
+ mServiceStub.mRoleObserver.onRoleHoldersChanged(RoleManager.ROLE_ASSISTANT,
+ UserHandle.of(userId));
}
-
}
// implementation entry point and binder service
@@ -806,8 +796,10 @@ public class VoiceInteractionManagerService extends SystemService {
if (TextUtils.isEmpty(curInteractor)) {
return null;
}
- if (DEBUG) Slog.d(TAG, "getCurInteractor curInteractor=" + curInteractor
+ if (DEBUG) {
+ Slog.d(TAG, "getCurInteractor curInteractor=" + curInteractor
+ " user=" + userHandle);
+ }
return ComponentName.unflattenFromString(curInteractor);
}
@@ -815,8 +807,9 @@ public class VoiceInteractionManagerService extends SystemService {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.VOICE_INTERACTION_SERVICE,
comp != null ? comp.flattenToShortString() : "", userHandle);
- if (DEBUG) Slog.d(TAG, "setCurInteractor comp=" + comp
- + " user=" + userHandle);
+ if (DEBUG) {
+ Slog.d(TAG, "setCurInteractor comp=" + comp + " user=" + userHandle);
+ }
}
ComponentName findAvailRecognizer(String prefPackage, int userHandle) {
@@ -1284,12 +1277,13 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
- @android.annotation.EnforcePermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD})
@Override
public void startListeningFromMic(
AudioFormat audioFormat,
IMicrophoneHotwordDetectionVoiceInteractionCallback callback)
throws RemoteException {
+ enforceCallingPermission(Manifest.permission.RECORD_AUDIO);
+ enforceCallingPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD);
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
@@ -1349,12 +1343,13 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
- @android.annotation.EnforcePermission(allOf={android.Manifest.permission.RECORD_AUDIO, android.Manifest.permission.CAPTURE_AUDIO_HOTWORD})
@Override
public void triggerHardwareRecognitionEventForTest(
SoundTrigger.KeyphraseRecognitionEvent event,
IHotwordRecognitionStatusCallback callback)
throws RemoteException {
+ enforceCallingPermission(Manifest.permission.RECORD_AUDIO);
+ enforceCallingPermission(Manifest.permission.CAPTURE_AUDIO_HOTWORD);
synchronized (this) {
enforceIsCurrentVoiceInteractionService();
@@ -1912,7 +1907,6 @@ public class VoiceInteractionManagerService extends SystemService {
pw.println(" mTemporarilyDisabled: " + mTemporarilyDisabled);
pw.println(" mCurUser: " + mCurUser);
pw.println(" mCurUserSupported: " + mCurUserSupported);
- pw.println(" mIgnoredPreCreatedUsers: " + mIgnoredPreCreatedUsers);
dumpSupportedUsers(pw, " ");
mDbHelper.dump(pw);
if (mImpl == null) {
@@ -2026,6 +2020,11 @@ public class VoiceInteractionManagerService extends SystemService {
List<String> roleHolders = mRm.getRoleHoldersAsUser(roleName, user);
+ if (DEBUG) {
+ Slogf.d(TAG, "onRoleHoldersChanged(%s, %s): roleHolders=%s", roleName, user,
+ roleHolders);
+ }
+
// TODO(b/226201975): this method is beling called when a pre-created user is added,
// at which point it doesn't have any role holders. But it's not called again when
// the actual user is added (i.e., when the pre-created user is converted), so we
@@ -2036,9 +2035,9 @@ public class VoiceInteractionManagerService extends SystemService {
if (roleHolders.isEmpty()) {
UserInfo userInfo = mUserManagerInternal.getUserInfo(user.getIdentifier());
if (userInfo != null && userInfo.preCreated) {
- Slogf.d(TAG, "onRoleHoldersChanged(): ignoring pre-created user %s for now",
- userInfo.toFullString());
- mIgnoredPreCreatedUsers.add(user);
+ Slogf.d(TAG, "onRoleHoldersChanged(): ignoring pre-created user %s for now,"
+ + " this method will be called again when it's converted to a real"
+ + " user", userInfo.toFullString());
return;
}
}
@@ -2337,4 +2336,36 @@ public class VoiceInteractionManagerService extends SystemService {
}
};
}
+
+ /**
+ * End the latency tracking log for keyphrase hotword invocation.
+ * The measurement covers from when the SoundTrigger HAL emits an event, captured in
+ * {@link com.android.server.soundtrigger_middleware.SoundTriggerMiddlewareLogging}
+ * to when the {@link android.service.voice.VoiceInteractionSession} system UI view is shown.
+ */
+ private final IVoiceInteractionSessionListener mLatencyLoggingListener =
+ new IVoiceInteractionSessionListener.Stub() {
+ @Override
+ public void onVoiceSessionShown() throws RemoteException {}
+
+ @Override
+ public void onVoiceSessionHidden() throws RemoteException {}
+
+ @Override
+ public void onVoiceSessionWindowVisibilityChanged(boolean visible)
+ throws RemoteException {
+ if (visible) {
+ LatencyTracker.getInstance(mContext)
+ .onActionEnd(LatencyTracker.ACTION_SHOW_VOICE_INTERACTION);
+ }
+ }
+
+ @Override
+ public void onSetUiHints(Bundle args) throws RemoteException {}
+
+ @Override
+ public IBinder asBinder() {
+ return mServiceStub;
+ }
+ };
}
diff --git a/telephony/OWNERS b/telephony/OWNERS
index e0c5f8fa214e..025869dd2999 100644
--- a/telephony/OWNERS
+++ b/telephony/OWNERS
@@ -17,3 +17,6 @@ per-file SubscriptionManager.java=jackyu@google.com,amruthr@google.com
per-file SubscriptionInfo.java=set noparent
per-file SubscriptionInfo.java=jackyu@google.com,amruthr@google.com
+# Requiring TL ownership for new carrier config keys.
+per-file CarrierConfigManager.java=set noparent
+per-file CarrierConfigManager.java=amruthr@google.com,tgunn@google.com,rgreenwalt@google.com,satk@google.com
diff --git a/telephony/java/android/telephony/AnomalyReporter.java b/telephony/java/android/telephony/AnomalyReporter.java
index e7d95e4f53b3..061b71b25275 100644
--- a/telephony/java/android/telephony/AnomalyReporter.java
+++ b/telephony/java/android/telephony/AnomalyReporter.java
@@ -27,6 +27,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.ParcelUuid;
+import android.provider.DeviceConfig;
import com.android.internal.telephony.TelephonyStatsLog;
import com.android.internal.util.IndentingPrintWriter;
@@ -57,6 +58,9 @@ import java.util.concurrent.ConcurrentHashMap;
public final class AnomalyReporter {
private static final String TAG = "AnomalyReporter";
+ private static final String KEY_IS_TELEPHONY_ANOMALY_REPORT_ENABLED =
+ "is_telephony_anomaly_report_enabled";
+
private static Context sContext = null;
private static Map<UUID, Integer> sEvents = new ConcurrentHashMap<>();
@@ -106,6 +110,18 @@ public final class AnomalyReporter {
return;
}
+ // Don't report if the server-side flag isn't loaded, as it implies other anomaly report
+ // related config hasn't loaded.
+ try {
+ boolean isAnomalyReportEnabledFromServer = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_TELEPHONY, KEY_IS_TELEPHONY_ANOMALY_REPORT_ENABLED,
+ false);
+ if (!isAnomalyReportEnabledFromServer) return;
+ } catch (Exception e) {
+ Rlog.w(TAG, "Unable to read device config, dropping event=" + eventId);
+ return;
+ }
+
TelephonyStatsLog.write(
TELEPHONY_ANOMALY_DETECTED,
carrierId,
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 3023ec9661d4..2a1efed63dd2 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -39,7 +39,6 @@ import android.service.carrier.CarrierService;
import android.telecom.TelecomManager;
import android.telephony.AccessNetworkConstants.AccessNetworkType;
import android.telephony.data.ApnSetting;
-import android.telephony.data.DataCallResponse;
import android.telephony.gba.TlsParams;
import android.telephony.gba.UaSecurityProtocolIdentifier;
import android.telephony.ims.ImsReasonInfo;
@@ -53,7 +52,9 @@ import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.telephony.Rlog;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* Provides access to telephony configuration values that are carrier-specific.
@@ -490,7 +491,10 @@ public class CarrierConfigManager {
/**
* Control whether users receive a simplified network settings UI and improved network
* selection.
+ *
+ * @deprecated Never implemented. Has no behavior impact when override. DO NOT USE.
*/
+ @Deprecated
public static final String
KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
@@ -1136,27 +1140,6 @@ public class CarrierConfigManager {
public static final String KEY_DEFAULT_MTU_INT = "default_mtu_int";
/**
- * The data call retry configuration for different types of APN.
- * @hide
- */
- public static final String KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS =
- "carrier_data_call_retry_config_strings";
-
- /**
- * Delay in milliseconds between trying APN from the pool
- * @hide
- */
- public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG =
- "carrier_data_call_apn_delay_default_long";
-
- /**
- * Faster delay in milliseconds between trying APN from the pool
- * @hide
- */
- public static final String KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG =
- "carrier_data_call_apn_delay_faster_long";
-
- /**
* Delay in milliseconds for retrying APN after disconnect
* @hide
*/
@@ -1164,21 +1147,6 @@ public class CarrierConfigManager {
"carrier_data_call_apn_retry_after_disconnect_long";
/**
- * The maximum times for telephony to retry data setup on the same APN requested by
- * network through the data setup response retry timer
- * {@link DataCallResponse#getRetryDurationMillis()}. This is to prevent that network keeps
- * asking device to retry data setup forever and causes power consumption issue. For infinite
- * retring same APN, configure this as 2147483647 (i.e. {@link Integer#MAX_VALUE}).
- *
- * Note if network does not suggest any retry timer, frameworks uses the retry configuration
- * from {@link #KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS}, and the maximum retry times could
- * be configured there.
- * @hide
- */
- public static final String KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT =
- "carrier_data_call_retry_network_requested_max_count_int";
-
- /**
* Data call setup permanent failure causes by the carrier
*/
public static final String KEY_CARRIER_DATA_CALL_PERMANENT_FAILURE_STRINGS =
@@ -1239,19 +1207,6 @@ public class CarrierConfigManager {
"carrier_metered_roaming_apn_types_strings";
/**
- * APN types that are not allowed on cellular
- * @hide
- */
- public static final String KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
- "carrier_wwan_disallowed_apn_types_string_array";
-
- /**
- * APN types that are not allowed on IWLAN
- * @hide
- */
- public static final String KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY =
- "carrier_wlan_disallowed_apn_types_string_array";
- /**
* CDMA carrier ERI (Enhanced Roaming Indicator) file name
* @hide
*/
@@ -8470,7 +8425,6 @@ public class CarrierConfigManager {
* "1800000, maximum_retries=20" means for those capabilities, retry happens in 2.5s, 3s, 5s,
* 10s, 15s, 20s, 40s, 1m, 2m, 4m, 10m, 20m, 30m, 30m, 30m, until reaching 20 retries.
*
- * // TODO: remove KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS
* @hide
*/
public static final String KEY_TELEPHONY_DATA_SETUP_RETRY_RULES_STRING_ARRAY =
@@ -8695,6 +8649,73 @@ public class CarrierConfigManager {
"unthrottle_data_retry_when_tac_changes_bool";
/**
+ * A list of premium capabilities the carrier supports. Applications can prompt users to
+ * purchase these premium capabilities from their carrier for a network boost.
+ * Valid values are any of {@link TelephonyManager.PremiumCapability}.
+ *
+ * This is empty by default, indicating that no premium capabilities are supported.
+ *
+ * @see TelephonyManager#isPremiumCapabilityAvailableForPurchase(int)
+ * @see TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)
+ */
+ public static final String KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY =
+ "supported_premium_capabilities_int_array";
+
+ /**
+ * The amount of time in milliseconds the notification for a network boost via
+ * premium capabilities will be visible to the user after
+ * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+ * requests user action to purchase the boost from the carrier. Once the timeout expires,
+ * the booster notification will be automatically dismissed and the request will fail with
+ * {@link TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT}.
+ *
+ * The default value is 30 minutes.
+ */
+ public static final String KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG =
+ "premium_capability_notification_display_timeout_millis_long";
+
+ /**
+ * The amount of time in milliseconds that the notification for a network boost via
+ * premium capabilities should be blocked when
+ * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+ * returns a failure due to user action or timeout.
+ *
+ * The default value is 30 minutes.
+ *
+ * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED
+ * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT
+ */
+ public static final String
+ KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG =
+ "premium_capability_notification_backoff_hysteresis_time_millis_long";
+
+ /**
+ * The amount of time in milliseconds that the purchase request should be throttled when
+ * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}
+ * returns a failure due to the carrier.
+ *
+ * The default value is 30 minutes.
+ *
+ * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR
+ * @see TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED
+ */
+ public static final String
+ KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG =
+ "premium_capability_purchase_condition_backoff_hysteresis_time_millis_long";
+
+ /**
+ * The URL to redirect to when the user clicks on the notification for a network boost via
+ * premium capabilities after applications call
+ * {@link TelephonyManager#purchasePremiumCapability(int, Executor, Consumer)}.
+ * If the URL is empty or invalid, the purchase request will return
+ * {@link TelephonyManager#PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED}.
+ *
+ * This is empty by default.
+ */
+ public static final String KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING =
+ "premium_capability_purchase_url_string";
+
+ /**
* IWLAN handover rules that determine whether handover is allowed or disallowed between
* cellular and IWLAN.
*
@@ -8717,7 +8738,7 @@ public class CarrierConfigManager {
* or EIMS-->
* <item value="source=EUTRAN, target=IWLAN, type=disallowed, capabilities=IMS|EIMS"/>
* <!-- Handover is always allowed in any condition. -->
- * <item value="source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN,
+ * <item value="source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN|UNKNOWN,
* target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"/>
* </string-array>
*
@@ -8872,27 +8893,13 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL, false);
sDefaults.putInt(KEY_DEFAULT_MTU_INT, 1500);
- sDefaults.putStringArray(KEY_CARRIER_DATA_CALL_RETRY_CONFIG_STRINGS, new String[]{
- "default:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
- + "320000:5000,640000:5000,1280000:5000,1800000:5000",
- "mms:default_randomization=2000,5000,10000,20000,40000,80000:5000,160000:5000,"
- + "320000:5000,640000:5000,1280000:5000,1800000:5000",
- "ims:max_retries=10, 5000, 5000, 5000",
- "others:max_retries=3, 5000, 5000, 5000"});
- sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_DEFAULT_LONG, 20000);
- sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_DELAY_FASTER_LONG, 3000);
sDefaults.putLong(KEY_CARRIER_DATA_CALL_APN_RETRY_AFTER_DISCONNECT_LONG, 3000);
- sDefaults.putInt(KEY_CARRIER_DATA_CALL_RETRY_NETWORK_REQUESTED_MAX_COUNT_INT, 3);
sDefaults.putString(KEY_CARRIER_ERI_FILE_NAME_STRING, "eri.xml");
sDefaults.putInt(KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT, 7200);
sDefaults.putStringArray(KEY_CARRIER_METERED_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
- sDefaults.putStringArray(KEY_CARRIER_WWAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
- new String[]{""});
- sDefaults.putStringArray(KEY_CARRIER_WLAN_DISALLOWED_APN_TYPES_STRING_ARRAY,
- new String[]{""});
sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
new int[] {TelephonyManager.NETWORK_TYPE_CDMA, TelephonyManager.NETWORK_TYPE_1xRTT,
TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyManager.NETWORK_TYPE_EVDO_A,
@@ -9374,6 +9381,15 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_UNTHROTTLE_DATA_RETRY_WHEN_TAC_CHANGES_BOOL, false);
sDefaults.putBoolean(KEY_VONR_SETTING_VISIBILITY_BOOL, true);
sDefaults.putBoolean(KEY_VONR_ENABLED_BOOL, false);
+ sDefaults.putIntArray(KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY, new int[]{});
+ sDefaults.putLong(KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG,
+ TimeUnit.MINUTES.toMillis(30));
+ sDefaults.putLong(KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
+ TimeUnit.MINUTES.toMillis(30));
+ sDefaults.putLong(
+ KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG,
+ TimeUnit.MINUTES.toMillis(30));
+ sDefaults.putString(KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING, null);
sDefaults.putStringArray(KEY_IWLAN_HANDOVER_POLICY_STRING_ARRAY, new String[]{
"source=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, "
+ "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"});
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index 1d6798b7fc6e..a0467c26897f 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -856,6 +856,31 @@ public final class NetworkRegistrationInfo implements Parcelable {
public Builder() {}
/**
+ * Builder from the existing {@link NetworkRegistrationInfo}.
+ *
+ * @param nri The network registration info object.
+ * @hide
+ */
+ public Builder(@NonNull NetworkRegistrationInfo nri) {
+ mDomain = nri.mDomain;
+ mTransportType = nri.mTransportType;
+ mInitialRegistrationState = nri.mInitialRegistrationState;
+ mAccessNetworkTechnology = nri.mAccessNetworkTechnology;
+ mRejectCause = nri.mRejectCause;
+ mEmergencyOnly = nri.mEmergencyOnly;
+ mAvailableServices = new ArrayList<>(nri.mAvailableServices);
+ mCellIdentity = nri.mCellIdentity;
+ if (nri.mDataSpecificInfo != null) {
+ mDataSpecificRegistrationInfo = new DataSpecificRegistrationInfo(
+ nri.mDataSpecificInfo);
+ }
+ if (nri.mVoiceSpecificInfo != null) {
+ mVoiceSpecificRegistrationInfo = new VoiceSpecificRegistrationInfo(
+ nri.mVoiceSpecificInfo);
+ }
+ }
+
+ /**
* Set the network domain.
*
* @param domain Network domain.
diff --git a/telephony/java/android/telephony/NetworkService.java b/telephony/java/android/telephony/NetworkService.java
index c75de42707a6..ac892da765e7 100644
--- a/telephony/java/android/telephony/NetworkService.java
+++ b/telephony/java/android/telephony/NetworkService.java
@@ -265,7 +265,7 @@ public abstract class NetworkService extends Service {
/** @hide */
@Override
public void onDestroy() {
- mHandlerThread.quit();
+ mHandlerThread.quitSafely();
super.onDestroy();
}
diff --git a/telephony/java/android/telephony/SignalThresholdInfo.java b/telephony/java/android/telephony/SignalThresholdInfo.java
index 3c1824539d1f..7053b44df1a2 100644
--- a/telephony/java/android/telephony/SignalThresholdInfo.java
+++ b/telephony/java/android/telephony/SignalThresholdInfo.java
@@ -100,24 +100,34 @@ public final class SignalThresholdInfo implements Parcelable {
*/
public static final int SIGNAL_MEASUREMENT_TYPE_SSSINR = 8;
+ /**
+ * The ratio between the received energy from the pilot signal CPICH per chip (Ec) to the
+ * noise density (No).
+ * Range: -24 dBm to 1 dBm.
+ * Used RAN: {@link AccessNetworkConstants.AccessNetworkType#UTRAN}
+ * Reference: 3GPP TS 25.215 5.1.5
+ */
+ public static final int SIGNAL_MEASUREMENT_TYPE_ECNO = 9;
+
/** @hide */
- @IntDef(prefix = {"SIGNAL_MEASUREMENT_TYPE_"}, value = {
- SIGNAL_MEASUREMENT_TYPE_UNKNOWN,
- SIGNAL_MEASUREMENT_TYPE_RSSI,
- SIGNAL_MEASUREMENT_TYPE_RSCP,
- SIGNAL_MEASUREMENT_TYPE_RSRP,
- SIGNAL_MEASUREMENT_TYPE_RSRQ,
- SIGNAL_MEASUREMENT_TYPE_RSSNR,
- SIGNAL_MEASUREMENT_TYPE_SSRSRP,
- SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
- SIGNAL_MEASUREMENT_TYPE_SSSINR
- })
+ @IntDef(
+ prefix = {"SIGNAL_MEASUREMENT_TYPE_"},
+ value = {
+ SIGNAL_MEASUREMENT_TYPE_UNKNOWN,
+ SIGNAL_MEASUREMENT_TYPE_RSSI,
+ SIGNAL_MEASUREMENT_TYPE_RSCP,
+ SIGNAL_MEASUREMENT_TYPE_RSRP,
+ SIGNAL_MEASUREMENT_TYPE_RSRQ,
+ SIGNAL_MEASUREMENT_TYPE_RSSNR,
+ SIGNAL_MEASUREMENT_TYPE_SSRSRP,
+ SIGNAL_MEASUREMENT_TYPE_SSRSRQ,
+ SIGNAL_MEASUREMENT_TYPE_SSSINR,
+ SIGNAL_MEASUREMENT_TYPE_ECNO
+ })
@Retention(RetentionPolicy.SOURCE)
- public @interface SignalMeasurementType {
- }
+ public @interface SignalMeasurementType {}
- @SignalMeasurementType
- private final int mSignalMeasurementType;
+ @SignalMeasurementType private final int mSignalMeasurementType;
/**
* A hysteresis time in milliseconds to prevent flapping.
@@ -149,8 +159,7 @@ public final class SignalThresholdInfo implements Parcelable {
/**
* The radio access network type associated with the signal thresholds.
*/
- @AccessNetworkConstants.RadioAccessNetworkType
- private final int mRan;
+ @AccessNetworkConstants.RadioAccessNetworkType private final int mRan;
/**
* Indicates the hysteresisMs is disabled.
@@ -166,7 +175,6 @@ public final class SignalThresholdInfo implements Parcelable {
*/
public static final int HYSTERESIS_DB_DISABLED = 0;
-
/**
* Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_RSSI}.
*
@@ -280,6 +288,20 @@ public final class SignalThresholdInfo implements Parcelable {
public static final int SIGNAL_SSSINR_MAX_VALUE = 40;
/**
+ * Minimum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_ECNO}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_ECNO_MIN_VALUE = -24;
+
+ /**
+ * Maximum valid value for {@link #SIGNAL_MEASUREMENT_TYPE_ECNO}.
+ *
+ * @hide
+ */
+ public static final int SIGNAL_ECNO_MAX_VALUE = 1;
+
+ /**
* The minimum number of thresholds allowed in each SignalThresholdInfo.
*
* @hide
@@ -303,9 +325,13 @@ public final class SignalThresholdInfo implements Parcelable {
* @param thresholds threshold value
* @param isEnabled isEnabled
*/
- private SignalThresholdInfo(@AccessNetworkConstants.RadioAccessNetworkType int ran,
- @SignalMeasurementType int signalMeasurementType, int hysteresisMs, int hysteresisDb,
- @NonNull int[] thresholds, boolean isEnabled) {
+ private SignalThresholdInfo(
+ @AccessNetworkConstants.RadioAccessNetworkType int ran,
+ @SignalMeasurementType int signalMeasurementType,
+ int hysteresisMs,
+ int hysteresisDb,
+ @NonNull int[] thresholds,
+ boolean isEnabled) {
Objects.requireNonNull(thresholds, "thresholds must not be null");
validateRanWithMeasurementType(ran, signalMeasurementType);
validateThresholdRange(signalMeasurementType, thresholds);
@@ -399,6 +425,7 @@ public final class SignalThresholdInfo implements Parcelable {
* @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP
* @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ
* @see #SIGNAL_MEASUREMENT_TYPE_SSSINR
+ * @see #SIGNAL_MEASUREMENT_TYPE_ECNO
* @see #getThresholds() for more details on signal strength thresholds
*/
public @NonNull Builder setThresholds(@NonNull int[] thresholds) {
@@ -417,18 +444,20 @@ public final class SignalThresholdInfo implements Parcelable {
*/
public @NonNull Builder setThresholds(@NonNull int[] thresholds, boolean isSystem) {
Objects.requireNonNull(thresholds, "thresholds must not be null");
- if (!isSystem && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
- || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) {
+ if (!isSystem
+ && (thresholds.length < MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+ || thresholds.length > MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED)) {
throw new IllegalArgumentException(
- "thresholds length must between " + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
- + " and " + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
+ "thresholds length must between "
+ + MINIMUM_NUMBER_OF_THRESHOLDS_ALLOWED
+ + " and "
+ + MAXIMUM_NUMBER_OF_THRESHOLDS_ALLOWED);
}
mThresholds = thresholds.clone();
Arrays.sort(mThresholds);
return this;
}
-
/**
* Set if the modem should trigger the report based on the criteria.
*
@@ -451,8 +480,13 @@ public final class SignalThresholdInfo implements Parcelable {
* measurement type
*/
public @NonNull SignalThresholdInfo build() {
- return new SignalThresholdInfo(mRan, mSignalMeasurementType, mHysteresisMs,
- mHysteresisDb, mThresholds, mIsEnabled);
+ return new SignalThresholdInfo(
+ mRan,
+ mSignalMeasurementType,
+ mHysteresisMs,
+ mHysteresisDb,
+ mThresholds,
+ mIsEnabled);
}
}
@@ -508,6 +542,7 @@ public final class SignalThresholdInfo implements Parcelable {
* @see #SIGNAL_MEASUREMENT_TYPE_SSRSRP
* @see #SIGNAL_MEASUREMENT_TYPE_SSRSRQ
* @see #SIGNAL_MEASUREMENT_TYPE_SSSINR
+ * @see #SIGNAL_MEASUREMENT_TYPE_ECNO
*/
public @NonNull int[] getThresholds() {
return mThresholds.clone();
@@ -574,8 +609,13 @@ public final class SignalThresholdInfo implements Parcelable {
@Override
public int hashCode() {
- return Objects.hash(mRan, mSignalMeasurementType, mHysteresisMs, mHysteresisDb,
- Arrays.hashCode(mThresholds), mIsEnabled);
+ return Objects.hash(
+ mRan,
+ mSignalMeasurementType,
+ mHysteresisMs,
+ mHysteresisDb,
+ Arrays.hashCode(mThresholds),
+ mIsEnabled);
}
public static final @NonNull Parcelable.Creator<SignalThresholdInfo> CREATOR =
@@ -594,13 +634,20 @@ public final class SignalThresholdInfo implements Parcelable {
@Override
public String toString() {
return new StringBuilder("SignalThresholdInfo{")
- .append("mRan=").append(mRan)
- .append(" mSignalMeasurementType=").append(mSignalMeasurementType)
- .append(" mHysteresisMs=").append(mHysteresisMs)
- .append(" mHysteresisDb=").append(mHysteresisDb)
- .append(" mThresholds=").append(Arrays.toString(mThresholds))
- .append(" mIsEnabled=").append(mIsEnabled)
- .append("}").toString();
+ .append("mRan=")
+ .append(mRan)
+ .append(" mSignalMeasurementType=")
+ .append(mSignalMeasurementType)
+ .append(" mHysteresisMs=")
+ .append(mHysteresisMs)
+ .append(" mHysteresisDb=")
+ .append(mHysteresisDb)
+ .append(" mThresholds=")
+ .append(Arrays.toString(mThresholds))
+ .append(" mIsEnabled=")
+ .append(mIsEnabled)
+ .append("}")
+ .toString();
}
/**
@@ -624,6 +671,8 @@ public final class SignalThresholdInfo implements Parcelable {
return threshold >= SIGNAL_SSRSRQ_MIN_VALUE && threshold <= SIGNAL_SSRSRQ_MAX_VALUE;
case SIGNAL_MEASUREMENT_TYPE_SSSINR:
return threshold >= SIGNAL_SSSINR_MIN_VALUE && threshold <= SIGNAL_SSSINR_MAX_VALUE;
+ case SIGNAL_MEASUREMENT_TYPE_ECNO:
+ return threshold >= SIGNAL_ECNO_MIN_VALUE && threshold <= SIGNAL_ECNO_MAX_VALUE;
default:
return false;
}
@@ -640,6 +689,7 @@ public final class SignalThresholdInfo implements Parcelable {
return ran == AccessNetworkConstants.AccessNetworkType.GERAN
|| ran == AccessNetworkConstants.AccessNetworkType.CDMA2000;
case SIGNAL_MEASUREMENT_TYPE_RSCP:
+ case SIGNAL_MEASUREMENT_TYPE_ECNO:
return ran == AccessNetworkConstants.AccessNetworkType.UTRAN;
case SIGNAL_MEASUREMENT_TYPE_RSRP:
case SIGNAL_MEASUREMENT_TYPE_RSRQ:
@@ -663,13 +713,15 @@ public final class SignalThresholdInfo implements Parcelable {
}
}
- private void validateThresholdRange(@SignalMeasurementType int signalMeasurement,
- int[] thresholds) {
+ private void validateThresholdRange(
+ @SignalMeasurementType int signalMeasurement, int[] thresholds) {
for (int threshold : thresholds) {
if (!isValidThreshold(signalMeasurement, threshold)) {
throw new IllegalArgumentException(
- "invalid signal measurement type: " + signalMeasurement
- + " with threshold: " + threshold);
+ "invalid signal measurement type: "
+ + signalMeasurement
+ + " with threshold: "
+ + threshold);
}
}
}
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index cb985bf2cda5..eb96d3739119 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -18,6 +18,7 @@ package android.telephony;
import static android.text.TextUtils.formatSimple;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -37,6 +38,9 @@ import android.os.Build;
import android.os.Parcel;
import android.os.ParcelUuid;
import android.os.Parcelable;
+import android.telephony.SubscriptionManager.ProfileClass;
+import android.telephony.SubscriptionManager.SimDisplayNameSource;
+import android.telephony.SubscriptionManager.SubscriptionType;
import android.telephony.SubscriptionManager.UsageSetting;
import android.text.TextUtils;
import android.util.DisplayMetrics;
@@ -55,7 +59,6 @@ import java.util.Objects;
* A Parcelable class for Subscription Information.
*/
public class SubscriptionInfo implements Parcelable {
-
/**
* Size of text to render on the icon.
*/
@@ -65,162 +68,180 @@ public class SubscriptionInfo implements Parcelable {
* Subscription Identifier, this is a device unique number
* and not an index into an array
*/
- private int mId;
+ private final int mId;
/**
- * The GID for a SIM that maybe associated with this subscription, empty if unknown
+ * The ICCID of the SIM that is associated with this subscription, empty if unknown.
*/
- private String mIccId;
+ @NonNull
+ private final String mIccId;
/**
- * The index of the slot that currently contains the subscription
- * and not necessarily unique and maybe INVALID_SLOT_ID if unknown
+ * The index of the SIM slot that currently contains the subscription and not necessarily unique
+ * and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or the subscription
+ * is inactive.
*/
- private int mSimSlotIndex;
+ private final int mSimSlotIndex;
/**
- * The name displayed to the user that identifies this subscription
+ * The name displayed to the user that identifies this subscription. This name is used
+ * in Settings page and can be renamed by the user.
*/
- private CharSequence mDisplayName;
+ @NonNull
+ private final CharSequence mDisplayName;
/**
- * String that identifies SPN/PLMN
- * TODO : Add a new field that identifies only SPN for a sim
+ * The name displayed to the user that identifies subscription provider name. This name is the
+ * SPN displayed in status bar and many other places. Can't be renamed by the user.
*/
- private CharSequence mCarrierName;
+ @NonNull
+ private final CharSequence mCarrierName;
/**
* The subscription carrier id.
+ *
* @see TelephonyManager#getSimCarrierId()
*/
- private int mCarrierId;
+ private final int mCarrierId;
/**
- * The source of the name, NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SPN,
- * NAME_SOURCE_SIM_PNN, or NAME_SOURCE_USER_INPUT.
+ * The source of the {@link #mCarrierName}.
*/
- private int mNameSource;
+ @SimDisplayNameSource
+ private final int mNameSource;
/**
- * The color to be used for tinting the icon when displaying to the user
+ * The color to be used for tinting the icon when displaying to the user.
*/
- private int mIconTint;
+ private final int mIconTint;
/**
- * A number presented to the user identify this subscription
+ * The number presented to the user identify this subscription.
*/
- private String mNumber;
+ @NonNull
+ private final String mNumber;
/**
- * Data roaming state, DATA_ROAMING_ENABLE, DATA_ROAMING_DISABLE
+ * Whether user enables data roaming for this subscription or not. Either
+ * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+ * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
*/
- private int mDataRoaming;
+ private final int mDataRoaming;
/**
- * SIM icon bitmap cache
+ * SIM icon bitmap cache.
*/
- @Nullable private Bitmap mIconBitmap;
+ @Nullable
+ private Bitmap mIconBitmap;
/**
- * Mobile Country Code
+ * Mobile Country Code.
*/
- private String mMcc;
+ @Nullable
+ private final String mMcc;
/**
- * Mobile Network Code
+ * Mobile Network Code.
*/
- private String mMnc;
+ @Nullable
+ private final String mMnc;
/**
- * EHPLMNs associated with the subscription
+ * EHPLMNs associated with the subscription.
*/
- private String[] mEhplmns;
+ @NonNull
+ private final String[] mEhplmns;
/**
- * HPLMNs associated with the subscription
+ * HPLMNs associated with the subscription.
*/
- private String[] mHplmns;
+ @NonNull
+ private final String[] mHplmns;
/**
- * ISO Country code for the subscription's provider
+ * ISO Country code for the subscription's provider.
*/
- private String mCountryIso;
+ @NonNull
+ private final String mCountryIso;
/**
- * Whether the subscription is an embedded one.
+ * Whether the subscription is from eSIM.
*/
- private boolean mIsEmbedded;
+ private final boolean mIsEmbedded;
/**
- * The access rules for this subscription, if it is embedded and defines any.
- * This does not include access rules for non-embedded subscriptions.
+ * The access rules for this subscription, if it is embedded and defines any. This does not
+ * include access rules for non-embedded subscriptions.
*/
@Nullable
- private UiccAccessRule[] mNativeAccessRules;
+ private final UiccAccessRule[] mNativeAccessRules;
/**
* The carrier certificates for this subscription that are saved in carrier configs.
* This does not include access rules from the Uicc, whether embedded or non-embedded.
*/
@Nullable
- private UiccAccessRule[] mCarrierConfigAccessRules;
+ private final UiccAccessRule[] mCarrierConfigAccessRules;
/**
* The string ID of the SIM card. It is the ICCID of the active profile for a UICC card and the
* EID for an eUICC card.
*/
- private String mCardString;
+ @NonNull
+ private final String mCardString;
/**
- * The card ID of the SIM card. This maps uniquely to the card string.
+ * The card ID of the SIM card. This maps uniquely to {@link #mCardString}.
*/
- private int mCardId;
+ private final int mCardId;
/**
* Whether the subscription is opportunistic.
*/
- private boolean mIsOpportunistic;
+ private final boolean mIsOpportunistic;
/**
- * A UUID assigned to the subscription group. It returns null if not assigned.
- * Check {@link SubscriptionManager#createSubscriptionGroup(List)} for more details.
+ * A UUID assigned to the subscription group. {@code null} if not assigned.
+ *
+ * @see SubscriptionManager#createSubscriptionGroup(List)
*/
@Nullable
- private ParcelUuid mGroupUUID;
+ private final ParcelUuid mGroupUuid;
/**
- * A package name that specifies who created the group. Null if mGroupUUID is null.
+ * A package name that specifies who created the group. Empty if not available.
*/
- private String mGroupOwner;
+ @NonNull
+ private final String mGroupOwner;
/**
- * Whether group of the subscription is disabled.
- * This is only useful if it's a grouped opportunistic subscription. In this case, if all
- * primary (non-opportunistic) subscriptions in the group are deactivated (unplugged pSIM
- * or deactivated eSIM profile), we should disable this opportunistic subscription.
+ * Whether group of the subscription is disabled. This is only useful if it's a grouped
+ * opportunistic subscription. In this case, if all primary (non-opportunistic) subscriptions
+ * in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we should disable
+ * this opportunistic subscription.
*/
- private boolean mIsGroupDisabled = false;
+ private final boolean mIsGroupDisabled;
/**
- * Profile class, PROFILE_CLASS_TESTING, PROFILE_CLASS_OPERATIONAL
- * PROFILE_CLASS_PROVISIONING, or PROFILE_CLASS_UNSET.
- * A profile on the eUICC can be defined as test, operational, provisioning, or unset.
- * The profile class will be populated from the profile metadata if present. Otherwise,
- * the profile class defaults to unset if there is no profile metadata or the subscription
- * is not on an eUICC ({@link #isEmbedded} returns false).
+ * The profile class populated from the profile metadata if present. Otherwise,
+ * the profile class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no
+ * profile metadata or the subscription is not on an eUICC ({@link #isEmbedded} returns
+ * {@code false}).
*/
- private int mProfileClass;
+ @ProfileClass
+ private final int mProfileClass;
/**
- * Type of subscription
+ * Type of the subscription.
*/
- private int mSubscriptionType;
+ @SubscriptionType
+ private final int mType;
/**
* Whether uicc applications are configured to enable or disable.
* By default it's true.
*/
- private boolean mAreUiccApplicationsEnabled = true;
+ private final boolean mAreUiccApplicationsEnabled;
/**
* The port index of the Uicc card.
@@ -230,25 +251,16 @@ public class SubscriptionInfo implements Parcelable {
/**
* Subscription's preferred usage setting.
*/
- private @UsageSetting int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
-
- /**
- * Public copy constructor.
- * @hide
- */
- public SubscriptionInfo(SubscriptionInfo info) {
- this(info.mId, info.mIccId, info.mSimSlotIndex, info.mDisplayName, info.mCarrierName,
- info.mNameSource, info.mIconTint, info.mNumber, info.mDataRoaming, info.mIconBitmap,
- info.mMcc, info.mMnc, info.mCountryIso, info.mIsEmbedded, info.mNativeAccessRules,
- info.mCardString, info.mCardId, info.mIsOpportunistic,
- info.mGroupUUID == null ? null : info.mGroupUUID.toString(), info.mIsGroupDisabled,
- info.mCarrierId, info.mProfileClass, info.mSubscriptionType, info.mGroupOwner,
- info.mCarrierConfigAccessRules, info.mAreUiccApplicationsEnabled);
- }
+ @UsageSetting
+ private final int mUsageSetting;
/**
* @hide
+ *
+ * @deprecated Use {@link SubscriptionInfo.Builder}.
*/
+ // TODO: Clean up after external usages moved to builder model.
+ @Deprecated
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@@ -262,7 +274,11 @@ public class SubscriptionInfo implements Parcelable {
/**
* @hide
+ *
+ * @deprecated Use {@link SubscriptionInfo.Builder}.
*/
+ // TODO: Clean up after external usages moved to builder model.
+ @Deprecated
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@@ -276,7 +292,11 @@ public class SubscriptionInfo implements Parcelable {
/**
* @hide
+ *
+ * @deprecated Use {@link SubscriptionInfo.Builder}.
*/
+ // TODO: Clean up after external usages moved to builder model.
+ @Deprecated
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@@ -293,7 +313,11 @@ public class SubscriptionInfo implements Parcelable {
/**
* @hide
+ *
+ * @deprecated Use {@link SubscriptionInfo.Builder}.
*/
+ // TODO: Clean up after external usages moved to builder model.
+ @Deprecated
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@@ -311,49 +335,94 @@ public class SubscriptionInfo implements Parcelable {
/**
* @hide
+ *
+ * @deprecated Use {@link SubscriptionInfo.Builder}.
*/
+ // TODO: Clean up after external usages moved to builder model.
+ @Deprecated
public SubscriptionInfo(int id, String iccId, int simSlotIndex, CharSequence displayName,
CharSequence carrierName, int nameSource, int iconTint, String number, int roaming,
Bitmap icon, String mcc, String mnc, String countryIso, boolean isEmbedded,
@Nullable UiccAccessRule[] nativeAccessRules, String cardString, int cardId,
- boolean isOpportunistic, @Nullable String groupUUID, boolean isGroupDisabled,
+ boolean isOpportunistic, @Nullable String groupUuid, boolean isGroupDisabled,
int carrierId, int profileClass, int subType, @Nullable String groupOwner,
@Nullable UiccAccessRule[] carrierConfigAccessRules,
boolean areUiccApplicationsEnabled, int portIndex, @UsageSetting int usageSetting) {
this.mId = id;
this.mIccId = iccId;
this.mSimSlotIndex = simSlotIndex;
- this.mDisplayName = displayName;
+ this.mDisplayName = displayName;
this.mCarrierName = carrierName;
this.mNameSource = nameSource;
this.mIconTint = iconTint;
this.mNumber = number;
this.mDataRoaming = roaming;
this.mIconBitmap = icon;
- this.mMcc = mcc;
- this.mMnc = mnc;
- this.mCountryIso = countryIso;
+ this.mMcc = TextUtils.emptyIfNull(mcc);
+ this.mMnc = TextUtils.emptyIfNull(mnc);
+ this.mHplmns = null;
+ this.mEhplmns = null;
+ this.mCountryIso = TextUtils.emptyIfNull(countryIso);
this.mIsEmbedded = isEmbedded;
this.mNativeAccessRules = nativeAccessRules;
- this.mCardString = cardString;
+ this.mCardString = TextUtils.emptyIfNull(cardString);
this.mCardId = cardId;
this.mIsOpportunistic = isOpportunistic;
- this.mGroupUUID = groupUUID == null ? null : ParcelUuid.fromString(groupUUID);
+ this.mGroupUuid = groupUuid == null ? null : ParcelUuid.fromString(groupUuid);
this.mIsGroupDisabled = isGroupDisabled;
this.mCarrierId = carrierId;
this.mProfileClass = profileClass;
- this.mSubscriptionType = subType;
- this.mGroupOwner = groupOwner;
+ this.mType = subType;
+ this.mGroupOwner = TextUtils.emptyIfNull(groupOwner);
this.mCarrierConfigAccessRules = carrierConfigAccessRules;
this.mAreUiccApplicationsEnabled = areUiccApplicationsEnabled;
this.mPortIndex = portIndex;
this.mUsageSetting = usageSetting;
}
+
/**
- * @return the subscription ID.
+ * Constructor from builder.
+ *
+ * @param builder Builder of {@link SubscriptionInfo}.
+ */
+ private SubscriptionInfo(@NonNull Builder builder) {
+ this.mId = builder.mId;
+ this.mIccId = builder.mIccId;
+ this.mSimSlotIndex = builder.mSimSlotIndex;
+ this.mDisplayName = builder.mDisplayName;
+ this.mCarrierName = builder.mCarrierName;
+ this.mNameSource = builder.mNameSource;
+ this.mIconTint = builder.mIconTint;
+ this.mNumber = builder.mNumber;
+ this.mDataRoaming = builder.mDataRoaming;
+ this.mIconBitmap = builder.mIconBitmap;
+ this.mMcc = builder.mMcc;
+ this.mMnc = builder.mMnc;
+ this.mEhplmns = builder.mEhplmns;
+ this.mHplmns = builder.mHplmns;
+ this.mCountryIso = builder.mCountryIso;
+ this.mIsEmbedded = builder.mIsEmbedded;
+ this.mNativeAccessRules = builder.mNativeAccessRules;
+ this.mCardString = builder.mCardString;
+ this.mCardId = builder.mCardId;
+ this.mIsOpportunistic = builder.mIsOpportunistic;
+ this.mGroupUuid = builder.mGroupUuid;
+ this.mIsGroupDisabled = builder.mIsGroupDisabled;
+ this.mCarrierId = builder.mCarrierId;
+ this.mProfileClass = builder.mProfileClass;
+ this.mType = builder.mType;
+ this.mGroupOwner = builder.mGroupOwner;
+ this.mCarrierConfigAccessRules = builder.mCarrierConfigAccessRules;
+ this.mAreUiccApplicationsEnabled = builder.mAreUiccApplicationsEnabled;
+ this.mPortIndex = builder.mPortIndex;
+ this.mUsageSetting = builder.mUsageSetting;
+ }
+
+ /**
+ * @return The subscription ID.
*/
public int getSubscriptionId() {
- return this.mId;
+ return mId;
}
/**
@@ -370,78 +439,56 @@ public class SubscriptionInfo implements Parcelable {
* @return the ICC ID, or an empty string if one of these requirements is not met
*/
public String getIccId() {
- return this.mIccId;
+ return mIccId;
}
/**
- * @hide
- */
- public void clearIccId() {
- this.mIccId = "";
- }
-
- /**
- * @return the slot index of this Subscription's SIM card.
+ * @return The index of the SIM slot that currently contains the subscription and not
+ * necessarily unique and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or
+ * the subscription is inactive.
*/
public int getSimSlotIndex() {
- return this.mSimSlotIndex;
+ return mSimSlotIndex;
}
/**
- * @return the carrier id of this Subscription carrier.
+ * @return The carrier id of this subscription carrier.
+ *
* @see TelephonyManager#getSimCarrierId()
*/
public int getCarrierId() {
- return this.mCarrierId;
+ return mCarrierId;
}
/**
- * @return the name displayed to the user that identifies this subscription
+ * @return The name displayed to the user that identifies this subscription. This name is
+ * used in Settings page and can be renamed by the user.
+ *
+ * @see #getCarrierName()
*/
public CharSequence getDisplayName() {
- return this.mDisplayName;
+ return mDisplayName;
}
/**
- * Sets the name displayed to the user that identifies this subscription
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void setDisplayName(CharSequence name) {
- this.mDisplayName = name;
- }
-
- /**
- * @return the name displayed to the user that identifies Subscription provider name
+ * @return The name displayed to the user that identifies subscription provider name. This name
+ * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+ *
+ * @see #getDisplayName()
*/
public CharSequence getCarrierName() {
- return this.mCarrierName;
- }
-
- /**
- * Sets the name displayed to the user that identifies Subscription provider name
- * @hide
- */
- public void setCarrierName(CharSequence name) {
- this.mCarrierName = name;
+ return mCarrierName;
}
/**
- * @return the source of the name, eg NAME_SOURCE_DEFAULT_SOURCE, NAME_SOURCE_SIM_SPN or
- * NAME_SOURCE_USER_INPUT.
+ * @return The source of the {@link #getCarrierName()}.
+ *
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @SimDisplayNameSource
public int getNameSource() {
- return this.mNameSource;
- }
-
- /**
- * @hide
- */
- public void setAssociatedPlmns(String[] ehplmns, String[] hplmns) {
- mEhplmns = ehplmns;
- mHplmns = hplmns;
+ return mNameSource;
}
/**
@@ -499,15 +546,6 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * Sets the color displayed to the user that identifies this subscription
- * @hide
- */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public void setIconTint(int iconTint) {
- this.mIconTint = iconTint;
- }
-
- /**
* Returns the number of this subscription.
*
* Starting with API level 30, returns the number of this subscription if the calling app meets
@@ -533,28 +571,23 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * @hide
- */
- public void clearNumber() {
- mNumber = "";
- }
-
- /**
- * @return the data roaming state for this subscription, either
- * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+ * Whether user enables data roaming for this subscription or not. Either
+ * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+ * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
*/
public int getDataRoaming() {
- return this.mDataRoaming;
+ return mDataRoaming;
}
/**
- * @return the MCC.
+ * @return The mobile country code.
+ *
* @deprecated Use {@link #getMccString()} instead.
*/
@Deprecated
public int getMcc() {
try {
- return this.mMcc == null ? 0 : Integer.valueOf(this.mMcc);
+ return mMcc == null ? 0 : Integer.parseInt(mMcc);
} catch (NumberFormatException e) {
Log.w(SubscriptionInfo.class.getSimpleName(), "MCC string is not a number");
return 0;
@@ -562,13 +595,14 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * @return the MNC.
+ * @return The mobile network code.
+ *
* @deprecated Use {@link #getMncString()} instead.
*/
@Deprecated
public int getMnc() {
try {
- return this.mMnc == null ? 0 : Integer.valueOf(this.mMnc);
+ return mMnc == null ? 0 : Integer.parseInt(mMnc);
} catch (NumberFormatException e) {
Log.w(SubscriptionInfo.class.getSimpleName(), "MNC string is not a number");
return 0;
@@ -576,36 +610,40 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * @return The MCC, as a string.
+ * @return The mobile country code.
*/
- public @Nullable String getMccString() {
- return this.mMcc;
+ @Nullable
+ public String getMccString() {
+ return mMcc;
}
/**
- * @return The MNC, as a string.
+ * @return The mobile network code.
*/
- public @Nullable String getMncString() {
- return this.mMnc;
+ @Nullable
+ public String getMncString() {
+ return mMnc;
}
/**
- * @return the ISO country code
+ * @return The ISO country code. Empty if not available.
*/
public String getCountryIso() {
- return this.mCountryIso;
+ return mCountryIso;
}
- /** @return whether the subscription is an eUICC one. */
+ /**
+ * @return {@code true} if the subscription is from eSIM.
+ */
public boolean isEmbedded() {
- return this.mIsEmbedded;
+ return mIsEmbedded;
}
/**
* An opportunistic subscription connects to a network that is
* limited in functionality and / or coverage.
*
- * @return whether subscription is opportunistic.
+ * @return Whether subscription is opportunistic.
*/
public boolean isOpportunistic() {
return mIsOpportunistic;
@@ -617,23 +655,18 @@ public class SubscriptionInfo implements Parcelable {
* Such that those subscriptions will have some affiliated behaviors such as opportunistic
* subscription may be invisible to the user.
*
- * @return group UUID a String of group UUID if it belongs to a group. Otherwise
- * it will return null.
+ * @return Group UUID a String of group UUID if it belongs to a group. Otherwise
+ * {@code null}.
*/
- public @Nullable ParcelUuid getGroupUuid() {
- return mGroupUUID;
- }
-
- /**
- * @hide
- */
- public void clearGroupUuid() {
- this.mGroupUUID = null;
+ @Nullable
+ public ParcelUuid getGroupUuid() {
+ return mGroupUuid;
}
/**
* @hide
*/
+ @NonNull
public List<String> getEhplmns() {
return mEhplmns == null ? Collections.emptyList() : Arrays.asList(mEhplmns);
}
@@ -641,36 +674,45 @@ public class SubscriptionInfo implements Parcelable {
/**
* @hide
*/
+ @NonNull
public List<String> getHplmns() {
return mHplmns == null ? Collections.emptyList() : Arrays.asList(mHplmns);
}
/**
- * Return owner package of group the subscription belongs to.
+ * @return The owner package of group the subscription belongs to.
*
* @hide
*/
- public @Nullable String getGroupOwner() {
+ @NonNull
+ public String getGroupOwner() {
return mGroupOwner;
}
/**
- * @return the profile class of this subscription.
+ * @return The profile class populated from the profile metadata if present. Otherwise,
+ * the profile class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no
+ * profile metadata or the subscription is not on an eUICC ({@link #isEmbedded} return
+ * {@code false}).
+ *
* @hide
*/
@SystemApi
- public @SubscriptionManager.ProfileClass int getProfileClass() {
- return this.mProfileClass;
+ @ProfileClass
+ public int getProfileClass() {
+ return mProfileClass;
}
/**
* This method returns the type of a subscription. It can be
* {@link SubscriptionManager#SUBSCRIPTION_TYPE_LOCAL_SIM} or
* {@link SubscriptionManager#SUBSCRIPTION_TYPE_REMOTE_SIM}.
- * @return the type of subscription
+ *
+ * @return The type of the subscription.
*/
- public @SubscriptionManager.SubscriptionType int getSubscriptionType() {
- return mSubscriptionType;
+ @SubscriptionType
+ public int getSubscriptionType() {
+ return mType;
}
/**
@@ -679,7 +721,7 @@ public class SubscriptionInfo implements Parcelable {
* returns true).
*
* @param context Context of the application to check.
- * @return whether the app is authorized to manage this subscription per its metadata.
+ * @return Whether the app is authorized to manage this subscription per its metadata.
* @hide
* @deprecated - Do not use.
*/
@@ -700,7 +742,7 @@ public class SubscriptionInfo implements Parcelable {
*/
@Deprecated
public boolean canManageSubscription(Context context, String packageName) {
- List<UiccAccessRule> allAccessRules = getAllAccessRules();
+ List<UiccAccessRule> allAccessRules = getAccessRules();
if (allAccessRules == null) {
return false;
}
@@ -723,27 +765,17 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * @return the {@link UiccAccessRule}s that are stored in Uicc, dictating who
- * is authorized to manage this subscription.
- * TODO and fix it properly in R / master: either deprecate this and have 3 APIs
- * native + carrier + all, or have this return all by default.
+ * @return The {@link UiccAccessRule}s that are stored in Uicc, dictating who is authorized to
+ * manage this subscription.
+ *
* @hide
*/
@SystemApi
- public @Nullable List<UiccAccessRule> getAccessRules() {
- if (mNativeAccessRules == null) return null;
- return Arrays.asList(mNativeAccessRules);
- }
-
- /**
- * @return the {@link UiccAccessRule}s that are both stored on Uicc and in carrierConfigs
- * dictating who is authorized to manage this subscription.
- * @hide
- */
- public @Nullable List<UiccAccessRule> getAllAccessRules() {
+ @Nullable
+ public List<UiccAccessRule> getAccessRules() {
List<UiccAccessRule> merged = new ArrayList<>();
if (mNativeAccessRules != null) {
- merged.addAll(getAccessRules());
+ merged.addAll(Arrays.asList(mNativeAccessRules));
}
if (mCarrierConfigAccessRules != null) {
merged.addAll(Arrays.asList(mCarrierConfigAccessRules));
@@ -762,50 +794,38 @@ public class SubscriptionInfo implements Parcelable {
* href="https://developer.android.com/work/managed-profiles">Work profiles</a>. Profile
* owner access is deprecated and will be removed in a future release.
*
- * @return the card string of the SIM card which contains the subscription or an empty string
+ * @return The card string of the SIM card which contains the subscription or an empty string
* if these requirements are not met. The card string is the ICCID for UICCs or the EID for
* eUICCs.
+ *
* @hide
- * //TODO rename usages in LPA: UiccSlotUtil.java, UiccSlotsManager.java, UiccSlotInfoTest.java
*/
+ @NonNull
public String getCardString() {
- return this.mCardString;
+ return mCardString;
}
/**
- * @hide
- */
- public void clearCardString() {
- this.mCardString = "";
- }
-
- /**
- * Returns the card ID of the SIM card which contains the subscription (see
- * {@link UiccCardInfo#getCardId()}.
- * @return the cardId
+ * @return The card ID of the SIM card which contains the subscription.
+ *
+ * @see UiccCardInfo#getCardId().
*/
public int getCardId() {
- return this.mCardId;
+ return mCardId;
}
/**
- * Returns the port index of the SIM card which contains the subscription.
- *
- * @return the portIndex
+ * @return The port index of the SIM card which contains the subscription.
*/
public int getPortIndex() {
- return this.mPortIndex;
- }
-
- /**
- * Set whether the subscription's group is disabled.
- * @hide
- */
- public void setGroupDisabled(boolean isGroupDisabled) {
- this.mIsGroupDisabled = isGroupDisabled;
+ return mPortIndex;
}
/**
- * Return whether the subscription's group is disabled.
+ * @return {@code true} if the group of the subscription is disabled. This is only useful if
+ * it's a grouped opportunistic subscription. In this case, if all primary (non-opportunistic)
+ * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile), we
+ * should disable this opportunistic subscription.
+ *
* @hide
*/
@SystemApi
@@ -814,7 +834,7 @@ public class SubscriptionInfo implements Parcelable {
}
/**
- * Return whether uicc applications are set to be enabled or disabled.
+ * @return {@code true} if Uicc applications are set to be enabled or disabled.
* @hide
*/
@SystemApi
@@ -825,56 +845,50 @@ public class SubscriptionInfo implements Parcelable {
/**
* Get the usage setting for this subscription.
*
- * @return the usage setting used for this subscription.
+ * @return The usage setting used for this subscription.
*/
- public @UsageSetting int getUsageSetting() {
+ @UsageSetting
+ public int getUsageSetting() {
return mUsageSetting;
}
- public static final @android.annotation.NonNull
- Parcelable.Creator<SubscriptionInfo> CREATOR =
- new Parcelable.Creator<SubscriptionInfo>() {
+ @NonNull
+ public static final Parcelable.Creator<SubscriptionInfo> CREATOR =
+ new Parcelable.Creator<SubscriptionInfo>() {
@Override
public SubscriptionInfo createFromParcel(Parcel source) {
- int id = source.readInt();
- String iccId = source.readString();
- int simSlotIndex = source.readInt();
- CharSequence displayName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- CharSequence carrierName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
- int nameSource = source.readInt();
- int iconTint = source.readInt();
- String number = source.readString();
- int dataRoaming = source.readInt();
- String mcc = source.readString();
- String mnc = source.readString();
- String countryIso = source.readString();
- boolean isEmbedded = source.readBoolean();
- UiccAccessRule[] nativeAccessRules = source.createTypedArray(UiccAccessRule.CREATOR);
- String cardString = source.readString();
- int cardId = source.readInt();
- int portId = source.readInt();
- boolean isOpportunistic = source.readBoolean();
- String groupUUID = source.readString();
- boolean isGroupDisabled = source.readBoolean();
- int carrierid = source.readInt();
- int profileClass = source.readInt();
- int subType = source.readInt();
- String[] ehplmns = source.createStringArray();
- String[] hplmns = source.createStringArray();
- String groupOwner = source.readString();
- UiccAccessRule[] carrierConfigAccessRules = source.createTypedArray(
- UiccAccessRule.CREATOR);
- boolean areUiccApplicationsEnabled = source.readBoolean();
- int usageSetting = source.readInt();
-
- SubscriptionInfo info = new SubscriptionInfo(id, iccId, simSlotIndex, displayName,
- carrierName, nameSource, iconTint, number, dataRoaming, /* icon= */ null,
- mcc, mnc, countryIso, isEmbedded, nativeAccessRules, cardString, cardId,
- isOpportunistic, groupUUID, isGroupDisabled, carrierid, profileClass, subType,
- groupOwner, carrierConfigAccessRules, areUiccApplicationsEnabled,
- portId, usageSetting);
- info.setAssociatedPlmns(ehplmns, hplmns);
- return info;
+ return new Builder()
+ .setId(source.readInt())
+ .setIccId(source.readString())
+ .setSimSlotIndex(source.readInt())
+ .setDisplayName(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source))
+ .setCarrierName(TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source))
+ .setNameSource(source.readInt())
+ .setIconTint(source.readInt())
+ .setNumber(source.readString())
+ .setDataRoaming(source.readInt())
+ .setMcc(source.readString())
+ .setMnc(source.readString())
+ .setCountryIso(source.readString())
+ .setEmbedded(source.readBoolean())
+ .setNativeAccessRules(source.createTypedArray(UiccAccessRule.CREATOR))
+ .setCardString(source.readString())
+ .setCardId(source.readInt())
+ .setPortIndex(source.readInt())
+ .setOpportunistic(source.readBoolean())
+ .setGroupUuid(source.readString8())
+ .setGroupDisabled(source.readBoolean())
+ .setCarrierId(source.readInt())
+ .setProfileClass(source.readInt())
+ .setType(source.readInt())
+ .setEhplmns(source.createStringArray())
+ .setHplmns(source.createStringArray())
+ .setGroupOwner(source.readString())
+ .setCarrierConfigAccessRules(source.createTypedArray(
+ UiccAccessRule.CREATOR))
+ .setUiccApplicationsEnabled(source.readBoolean())
+ .setUsageSetting(source.readInt())
+ .build();
}
@Override
@@ -904,11 +918,11 @@ public class SubscriptionInfo implements Parcelable {
dest.writeInt(mCardId);
dest.writeInt(mPortIndex);
dest.writeBoolean(mIsOpportunistic);
- dest.writeString(mGroupUUID == null ? null : mGroupUUID.toString());
+ dest.writeString8(mGroupUuid == null ? null : mGroupUuid.toString());
dest.writeBoolean(mIsGroupDisabled);
dest.writeInt(mCarrierId);
dest.writeInt(mProfileClass);
- dest.writeInt(mSubscriptionType);
+ dest.writeInt(mType);
dest.writeStringArray(mEhplmns);
dest.writeStringArray(mHplmns);
dest.writeString(mGroupOwner);
@@ -923,6 +937,11 @@ public class SubscriptionInfo implements Parcelable {
}
/**
+ * Get ICCID stripped PII information on user build.
+ *
+ * @param iccId The original ICCID.
+ * @return The stripped string.
+ *
* @hide
*/
public static String givePrintableIccid(String iccId) {
@@ -951,12 +970,12 @@ public class SubscriptionInfo implements Parcelable {
+ " nativeAccessRules=" + Arrays.toString(mNativeAccessRules)
+ " cardString=" + cardStringToPrint + " cardId=" + mCardId
+ " portIndex=" + mPortIndex
- + " isOpportunistic=" + mIsOpportunistic + " groupUUID=" + mGroupUUID
+ + " isOpportunistic=" + mIsOpportunistic + " groupUuid=" + mGroupUuid
+ " isGroupDisabled=" + mIsGroupDisabled
+ " profileClass=" + mProfileClass
+ " ehplmns=" + Arrays.toString(mEhplmns)
+ " hplmns=" + Arrays.toString(mHplmns)
- + " subscriptionType=" + mSubscriptionType
+ + " mType=" + mType
+ " groupOwner=" + mGroupOwner
+ " carrierConfigAccessRules=" + Arrays.toString(mCarrierConfigAccessRules)
+ " areUiccApplicationsEnabled=" + mAreUiccApplicationsEnabled
@@ -966,7 +985,7 @@ public class SubscriptionInfo implements Parcelable {
@Override
public int hashCode() {
return Objects.hash(mId, mSimSlotIndex, mNameSource, mIconTint, mDataRoaming, mIsEmbedded,
- mIsOpportunistic, mGroupUUID, mIccId, mNumber, mMcc, mMnc, mCountryIso, mCardString,
+ mIsOpportunistic, mGroupUuid, mIccId, mNumber, mMcc, mMnc, mCountryIso, mCardString,
mCardId, mDisplayName, mCarrierName, Arrays.hashCode(mNativeAccessRules),
mIsGroupDisabled, mCarrierId, mProfileClass, mGroupOwner,
mAreUiccApplicationsEnabled, mPortIndex, mUsageSetting);
@@ -974,16 +993,9 @@ public class SubscriptionInfo implements Parcelable {
@Override
public boolean equals(Object obj) {
- if (obj == null) return false;
- if (obj == this) return true;
-
- SubscriptionInfo toCompare;
- try {
- toCompare = (SubscriptionInfo) obj;
- } catch (ClassCastException ex) {
- return false;
- }
-
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ SubscriptionInfo toCompare = (SubscriptionInfo) obj;
return mId == toCompare.mId
&& mSimSlotIndex == toCompare.mSimSlotIndex
&& mNameSource == toCompare.mNameSource
@@ -994,7 +1006,7 @@ public class SubscriptionInfo implements Parcelable {
&& mIsGroupDisabled == toCompare.mIsGroupDisabled
&& mAreUiccApplicationsEnabled == toCompare.mAreUiccApplicationsEnabled
&& mCarrierId == toCompare.mCarrierId
- && Objects.equals(mGroupUUID, toCompare.mGroupUUID)
+ && Objects.equals(mGroupUuid, toCompare.mGroupUuid)
&& Objects.equals(mIccId, toCompare.mIccId)
&& Objects.equals(mNumber, toCompare.mNumber)
&& Objects.equals(mMcc, toCompare.mMcc)
@@ -1012,4 +1024,629 @@ public class SubscriptionInfo implements Parcelable {
&& Arrays.equals(mHplmns, toCompare.mHplmns)
&& mUsageSetting == toCompare.mUsageSetting;
}
+
+ /**
+ * The builder class of {@link SubscriptionInfo}.
+ *
+ * @hide
+ */
+ public static class Builder {
+ /**
+ * The subscription id.
+ */
+ private int mId = 0;
+
+ /**
+ * The ICCID of the SIM that is associated with this subscription, empty if unknown.
+ */
+ @NonNull
+ private String mIccId = "";
+
+ /**
+ * The index of the SIM slot that currently contains the subscription and not necessarily
+ * unique and maybe {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if unknown or the
+ * subscription is inactive.
+ */
+ private int mSimSlotIndex = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+
+ /**
+ * The name displayed to the user that identifies this subscription. This name is used
+ * in Settings page and can be renamed by the user.
+ */
+ @NonNull
+ private CharSequence mDisplayName = "";
+
+ /**
+ * The name displayed to the user that identifies subscription provider name. This name
+ * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+ */
+ @NonNull
+ private CharSequence mCarrierName = "";
+
+ /**
+ * The source of the carrier name.
+ */
+ @SimDisplayNameSource
+ private int mNameSource = SubscriptionManager.NAME_SOURCE_CARRIER_ID;
+
+ /**
+ * The color to be used for tinting the icon when displaying to the user.
+ */
+ private int mIconTint = 0;
+
+ /**
+ * The number presented to the user identify this subscription.
+ */
+ @NonNull
+ private String mNumber = "";
+
+ /**
+ * Whether user enables data roaming for this subscription or not. Either
+ * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+ * {@link SubscriptionManager#DATA_ROAMING_DISABLE}.
+ */
+ private int mDataRoaming = SubscriptionManager.DATA_ROAMING_DISABLE;
+
+ /**
+ * SIM icon bitmap cache.
+ */
+ @Nullable
+ private Bitmap mIconBitmap = null;
+
+ /**
+ * The mobile country code.
+ */
+ @Nullable
+ private String mMcc = null;
+
+ /**
+ * The mobile network code.
+ */
+ @Nullable
+ private String mMnc = null;
+
+ /**
+ * EHPLMNs associated with the subscription.
+ */
+ @NonNull
+ private String[] mEhplmns = new String[0];
+
+ /**
+ * HPLMNs associated with the subscription.
+ */
+ @NonNull
+ private String[] mHplmns = new String[0];
+
+ /**
+ * The ISO Country code for the subscription's provider.
+ */
+ @NonNull
+ private String mCountryIso = "";
+
+ /**
+ * Whether the subscription is from eSIM.
+ */
+ private boolean mIsEmbedded = false;
+
+ /**
+ * The native access rules for this subscription, if it is embedded and defines any. This
+ * does not include access rules for non-embedded subscriptions.
+ */
+ @Nullable
+ private UiccAccessRule[] mNativeAccessRules = null;
+
+ /**
+ * The card string of the SIM card.
+ */
+ @NonNull
+ private String mCardString = "";
+
+ /**
+ * The card ID of the SIM card which contains the subscription.
+ */
+ private int mCardId = -1;
+
+ /**
+ * Whether the subscription is opportunistic or not.
+ */
+ private boolean mIsOpportunistic = false;
+
+ /**
+ * The group UUID of the subscription group.
+ */
+ @Nullable
+ private ParcelUuid mGroupUuid = null;
+
+ /**
+ * Whether group of the subscription is disabled. This is only useful if it's a grouped
+ * opportunistic subscription. In this case, if all primary (non-opportunistic)
+ * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
+ * we should disable this opportunistic subscription.
+ */
+ private boolean mIsGroupDisabled = false;
+
+ /**
+ * The carrier id.
+ *
+ * @see TelephonyManager#getSimCarrierId()
+ */
+ private int mCarrierId = TelephonyManager.UNKNOWN_CARRIER_ID;
+
+ /**
+ * The profile class populated from the profile metadata if present. Otherwise, the profile
+ * class defaults to {@link SubscriptionManager#PROFILE_CLASS_UNSET} if there is no profile
+ * metadata or the subscription is not on an eUICC ({@link #isEmbedded} returns
+ * {@code false}).
+ */
+ @ProfileClass
+ private int mProfileClass = SubscriptionManager.PROFILE_CLASS_UNSET;
+
+ /**
+ * The subscription type.
+ */
+ @SubscriptionType
+ private int mType = SubscriptionManager.SUBSCRIPTION_TYPE_LOCAL_SIM;
+
+ /**
+ * The owner package of group the subscription belongs to.
+ */
+ @NonNull
+ private String mGroupOwner = "";
+
+ /**
+ * The carrier certificates for this subscription that are saved in carrier configs.
+ * This does not include access rules from the Uicc, whether embedded or non-embedded.
+ */
+ @Nullable
+ private UiccAccessRule[] mCarrierConfigAccessRules = null;
+
+ /**
+ * Whether Uicc applications are configured to enable or not.
+ */
+ private boolean mAreUiccApplicationsEnabled = true;
+
+ /**
+ * the port index of the Uicc card.
+ */
+ private int mPortIndex = 0;
+
+ /**
+ * Subscription's preferred usage setting.
+ */
+ @UsageSetting
+ private int mUsageSetting = SubscriptionManager.USAGE_SETTING_UNKNOWN;
+
+ /**
+ * Default constructor.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructor from {@link SubscriptionInfo}.
+ *
+ * @param info The subscription info.
+ */
+ public Builder(@NonNull SubscriptionInfo info) {
+ mId = info.mId;
+ mIccId = info.mIccId;
+ mSimSlotIndex = info.mSimSlotIndex;
+ mDisplayName = info.mDisplayName;
+ mCarrierName = info.mCarrierName;
+ mNameSource = info.mNameSource;
+ mIconTint = info.mIconTint;
+ mNumber = info.mNumber;
+ mDataRoaming = info.mDataRoaming;
+ mIconBitmap = info.mIconBitmap;
+ mMcc = info.mMcc;
+ mMnc = info.mMnc;
+ mEhplmns = info.mEhplmns;
+ mHplmns = info.mHplmns;
+ mCountryIso = info.mCountryIso;
+ mIsEmbedded = info.mIsEmbedded;
+ mNativeAccessRules = info.mNativeAccessRules;
+ mCardString = info.mCardString;
+ mCardId = info.mCardId;
+ mIsOpportunistic = info.mIsOpportunistic;
+ mGroupUuid = info.mGroupUuid;
+ mIsGroupDisabled = info.mIsGroupDisabled;
+ mCarrierId = info.mCarrierId;
+ mProfileClass = info.mProfileClass;
+ mType = info.mType;
+ mGroupOwner = info.mGroupOwner;
+ mCarrierConfigAccessRules = info.mCarrierConfigAccessRules;
+ mAreUiccApplicationsEnabled = info.mAreUiccApplicationsEnabled;
+ mPortIndex = info.mPortIndex;
+ mUsageSetting = info.mUsageSetting;
+ }
+
+ /**
+ * Set the subscription id.
+ *
+ * @param id The subscription id.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setId(int id) {
+ mId = id;
+ return this;
+ }
+
+ /**
+ * Set the ICCID of the SIM that is associated with this subscription.
+ *
+ * @param iccId The ICCID of the SIM that is associated with this subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setIccId(@Nullable String iccId) {
+ mIccId = TextUtils.emptyIfNull(iccId);
+ return this;
+ }
+
+ /**
+ * Set the SIM index of the slot that currently contains the subscription. Set to
+ * {@link SubscriptionManager#INVALID_SIM_SLOT_INDEX} if the subscription is inactive.
+ *
+ * @param simSlotIndex The SIM slot index.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setSimSlotIndex(int simSlotIndex) {
+ mSimSlotIndex = simSlotIndex;
+ return this;
+ }
+
+ /**
+ * The name displayed to the user that identifies this subscription. This name is used
+ * in Settings page and can be renamed by the user.
+ *
+ * @param displayName The display name.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setDisplayName(@Nullable CharSequence displayName) {
+ mDisplayName = displayName == null ? "" : displayName;
+ return this;
+ }
+
+ /**
+ * The name displayed to the user that identifies subscription provider name. This name
+ * is the SPN displayed in status bar and many other places. Can't be renamed by the user.
+ *
+ * @param carrierName The carrier name.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCarrierName(@Nullable CharSequence carrierName) {
+ mCarrierName = carrierName == null ? "" : carrierName;
+ return this;
+ }
+
+ /**
+ * Set the source of the carrier name.
+ *
+ * @param nameSource The source of the carrier name.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setNameSource(@SimDisplayNameSource int nameSource) {
+ mNameSource = nameSource;
+ return this;
+ }
+
+ /**
+ * Set the color to be used for tinting the icon when displaying to the user.
+ *
+ * @param iconTint The color to be used for tinting the icon when displaying to the user.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setIconTint(int iconTint) {
+ mIconTint = iconTint;
+ return this;
+ }
+
+ /**
+ * Set the number presented to the user identify this subscription.
+ *
+ * @param number the number presented to the user identify this subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setNumber(@Nullable String number) {
+ mNumber = TextUtils.emptyIfNull(number);
+ return this;
+ }
+
+ /**
+ * Set whether user enables data roaming for this subscription or not.
+ *
+ * @param dataRoaming Data roaming mode. Either
+ * {@link SubscriptionManager#DATA_ROAMING_ENABLE} or
+ * {@link SubscriptionManager#DATA_ROAMING_DISABLE}
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setDataRoaming(int dataRoaming) {
+ mDataRoaming = dataRoaming;
+ return this;
+ }
+
+ /**
+ * Set SIM icon bitmap cache.
+ *
+ * @param iconBitmap SIM icon bitmap cache.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setIcon(@Nullable Bitmap iconBitmap) {
+ mIconBitmap = iconBitmap;
+ return this;
+ }
+
+ /**
+ * Set the mobile country code.
+ *
+ * @param mcc The mobile country code.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setMcc(@Nullable String mcc) {
+ mMcc = mcc;
+ return this;
+ }
+
+ /**
+ * Set the mobile network code.
+ *
+ * @param mnc Mobile network code.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setMnc(@Nullable String mnc) {
+ mMnc = mnc;
+ return this;
+ }
+
+ /**
+ * Set EHPLMNs associated with the subscription.
+ *
+ * @param ehplmns EHPLMNs associated with the subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setEhplmns(@Nullable String[] ehplmns) {
+ mEhplmns = ehplmns == null ? new String[0] : ehplmns;
+ return this;
+ }
+
+ /**
+ * Set HPLMNs associated with the subscription.
+ *
+ * @param hplmns HPLMNs associated with the subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setHplmns(@Nullable String[] hplmns) {
+ mHplmns = hplmns == null ? new String[0] : hplmns;
+ return this;
+ }
+
+ /**
+ * Set the ISO Country code for the subscription's provider.
+ *
+ * @param countryIso The ISO Country code for the subscription's provider.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCountryIso(@Nullable String countryIso) {
+ mCountryIso = TextUtils.emptyIfNull(countryIso);
+ return this;
+ }
+
+ /**
+ * Set whether the subscription is from eSIM or not.
+ *
+ * @param isEmbedded {@code true} if the subscription is from eSIM.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setEmbedded(boolean isEmbedded) {
+ mIsEmbedded = isEmbedded;
+ return this;
+ }
+
+ /**
+ * Set the native access rules for this subscription, if it is embedded and defines any.
+ * This does not include access rules for non-embedded subscriptions.
+ *
+ * @param nativeAccessRules The native access rules for this subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setNativeAccessRules(@Nullable UiccAccessRule[] nativeAccessRules) {
+ mNativeAccessRules = nativeAccessRules;
+ return this;
+ }
+
+ /**
+ * Set the card string of the SIM card.
+ *
+ * @param cardString The card string of the SIM card.
+ * @return The builder.
+ *
+ * @see #getCardString()
+ */
+ @NonNull
+ public Builder setCardString(@Nullable String cardString) {
+ mCardString = TextUtils.emptyIfNull(cardString);
+ return this;
+ }
+
+ /**
+ * Set the card ID of the SIM card which contains the subscription.
+ *
+ * @param cardId The card ID of the SIM card which contains the subscription.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCardId(int cardId) {
+ mCardId = cardId;
+ return this;
+ }
+
+ /**
+ * Set whether the subscription is opportunistic or not.
+ *
+ * @param isOpportunistic {@code true} if the subscription is opportunistic.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setOpportunistic(boolean isOpportunistic) {
+ mIsOpportunistic = isOpportunistic;
+ return this;
+ }
+
+ /**
+ * Set the group UUID of the subscription group.
+ *
+ * @param groupUuid The group UUID.
+ * @return The builder.
+ *
+ * @see #getGroupUuid()
+ */
+ @NonNull
+ public Builder setGroupUuid(@Nullable String groupUuid) {
+ mGroupUuid = groupUuid == null ? null : ParcelUuid.fromString(groupUuid);
+ return this;
+ }
+
+ /**
+ * Whether group of the subscription is disabled. This is only useful if it's a grouped
+ * opportunistic subscription. In this case, if all primary (non-opportunistic)
+ * subscriptions in the group are deactivated (unplugged pSIM or deactivated eSIM profile),
+ * we should disable this opportunistic subscription.
+ *
+ * @param isGroupDisabled {@code true} if group of the subscription is disabled.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setGroupDisabled(boolean isGroupDisabled) {
+ mIsGroupDisabled = isGroupDisabled;
+ return this;
+ }
+
+ /**
+ * Set the subscription carrier id.
+ *
+ * @param carrierId The carrier id.
+ * @return The builder
+ *
+ * @see TelephonyManager#getSimCarrierId()
+ */
+ @NonNull
+ public Builder setCarrierId(int carrierId) {
+ mCarrierId = carrierId;
+ return this;
+ }
+
+ /**
+ * Set the profile class populated from the profile metadata if present.
+ *
+ * @param profileClass the profile class populated from the profile metadata if present.
+ * @return The builder
+ *
+ * @see #getProfileClass()
+ */
+ @NonNull
+ public Builder setProfileClass(@ProfileClass int profileClass) {
+ mProfileClass = profileClass;
+ return this;
+ }
+
+ /**
+ * Set the subscription type.
+ *
+ * @param type Subscription type.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setType(@SubscriptionType int type) {
+ mType = type;
+ return this;
+ }
+
+ /**
+ * Set the owner package of group the subscription belongs to.
+ *
+ * @param groupOwner Owner package of group the subscription belongs to.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setGroupOwner(@Nullable String groupOwner) {
+ mGroupOwner = TextUtils.emptyIfNull(groupOwner);
+ return this;
+ }
+
+ /**
+ * Set the carrier certificates for this subscription that are saved in carrier configs.
+ * This does not include access rules from the Uicc, whether embedded or non-embedded.
+ *
+ * @param carrierConfigAccessRules The carrier certificates for this subscription
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setCarrierConfigAccessRules(
+ @Nullable UiccAccessRule[] carrierConfigAccessRules) {
+ mCarrierConfigAccessRules = carrierConfigAccessRules;
+ return this;
+ }
+
+ /**
+ * Set whether Uicc applications are configured to enable or not.
+ *
+ * @param uiccApplicationsEnabled {@code true} if Uicc applications are configured to
+ * enable.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setUiccApplicationsEnabled(boolean uiccApplicationsEnabled) {
+ mAreUiccApplicationsEnabled = uiccApplicationsEnabled;
+ return this;
+ }
+
+ /**
+ * Set the port index of the Uicc card.
+ *
+ * @param portIndex The port index of the Uicc card.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setPortIndex(int portIndex) {
+ mPortIndex = portIndex;
+ return this;
+ }
+
+ /**
+ * Set subscription's preferred usage setting.
+ *
+ * @param usageSetting Subscription's preferred usage setting.
+ * @return The builder.
+ */
+ @NonNull
+ public Builder setUsageSetting(@UsageSetting int usageSetting) {
+ mUsageSetting = usageSetting;
+ return this;
+ }
+
+ /**
+ * Build the {@link SubscriptionInfo}.
+ *
+ * @return The {@link SubscriptionInfo} instance.
+ */
+ public SubscriptionInfo build() {
+ return new SubscriptionInfo(this);
+ }
+ }
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 4fb65874044f..1871ba62bb0e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -509,18 +509,14 @@ public class SubscriptionManager {
public static final String TP_MESSAGE_REF = SimInfo.COLUMN_TP_MESSAGE_REF;
/**
- * TelephonyProvider column name data_enabled_override_rules.
- * It's a list of rules for overriding data enabled settings. The syntax is
- * For example, "mms=nonDefault" indicates enabling data for mms in non-default subscription.
- * "default=nonDefault&inVoiceCall" indicates enabling data for internet in non-default
- * subscription and while is in voice call.
+ * TelephonyProvider column name enabled_mobile_data_policies.
+ * A list of mobile data policies, each of which represented by an integer and joint by ",".
*
* Default value is empty string.
- *
* @hide
*/
- public static final String DATA_ENABLED_OVERRIDE_RULES =
- SimInfo.COLUMN_DATA_ENABLED_OVERRIDE_RULES;
+ public static final String ENABLED_MOBILE_DATA_POLICIES =
+ SimInfo.COLUMN_ENABLED_MOBILE_DATA_POLICIES;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -1071,6 +1067,13 @@ public class SubscriptionManager {
public static final String ALLOWED_NETWORK_TYPES =
SimInfo.COLUMN_ALLOWED_NETWORK_TYPES_FOR_REASONS;
+ /**
+ * TelephonyProvider column name for user handle associated with a sim.
+ * <P>Type: INTEGER (int)</P>
+ * @hide
+ */
+ public static final String USER_HANDLE = SimInfo.COLUMN_USER_HANDLE;
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = {"USAGE_SETTING_"},
@@ -3099,7 +3102,7 @@ public class SubscriptionManager {
@SystemApi
public boolean canManageSubscription(@NonNull SubscriptionInfo info,
@NonNull String packageName) {
- if (info == null || info.getAllAccessRules() == null || packageName == null) {
+ if (info == null || info.getAccessRules() == null || packageName == null) {
return false;
}
PackageManager packageManager = mContext.getPackageManager();
@@ -3111,7 +3114,7 @@ public class SubscriptionManager {
logd("Unknown package: " + packageName);
return false;
}
- for (UiccAccessRule rule : info.getAllAccessRules()) {
+ for (UiccAccessRule rule : info.getAccessRules()) {
if (rule.getCarrierPrivilegeStatus(packageInfo)
== TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
return true;
@@ -3456,10 +3459,20 @@ public class SubscriptionManager {
* Get subscriptionInfo list of subscriptions that are in the same group of given subId.
* See {@link #createSubscriptionGroup(List)} for more details.
*
- * Caller will either have {@link android.Manifest.permission#READ_PHONE_STATE}
- * permission or had carrier privilege permission on the subscription.
+ * Caller must have {@link android.Manifest.permission#READ_PHONE_STATE}
+ * or carrier privilege permission on the subscription.
* {@link TelephonyManager#hasCarrierPrivileges()}
*
+ * <p>Starting with API level 33, the caller also needs permission to access device identifiers
+ * to get the list of subscriptions associated with a group UUID.
+ * This method can be invoked if one of the following requirements is met:
+ * <ul>
+ * <li>If the app has carrier privilege permission.
+ * {@link TelephonyManager#hasCarrierPrivileges()}
+ * <li>If the app has {@link android.Manifest.permission#READ_PHONE_STATE} permission and
+ * access to device identifiers.
+ * </ul>
+ *
* @throws IllegalStateException if Telephony service is in bad state.
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b4244dd09bbd..8818ac2f6ee0 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17112,6 +17112,266 @@ public class TelephonyManager {
}
/**
+ * A premium capability boosting the network to allow real-time interactive traffic.
+ * Corresponds to NetworkCapabilities#NET_CAPABILITY_REALTIME_INTERACTIVE_TRAFFIC.
+ */
+ // TODO(b/245748544): add @link once NET_CAPABILITY_REALTIME_INTERACTIVE_TRAFFIC is defined.
+ public static final int PREMIUM_CAPABILITY_REALTIME_INTERACTIVE_TRAFFIC = 1;
+
+ /**
+ * Purchasable premium capabilities.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "PREMIUM_CAPABILITY_" }, value = {
+ PREMIUM_CAPABILITY_REALTIME_INTERACTIVE_TRAFFIC})
+ public @interface PremiumCapability {}
+
+ /**
+ * Returns the premium capability {@link PremiumCapability} as a String.
+ *
+ * @param capability The premium capability.
+ * @return The premium capability as a String.
+ * @hide
+ */
+ public static String convertPremiumCapabilityToString(@PremiumCapability int capability) {
+ switch (capability) {
+ case PREMIUM_CAPABILITY_REALTIME_INTERACTIVE_TRAFFIC:
+ return "REALTIME_INTERACTIVE_TRAFFIC";
+ default:
+ return "UNKNOWN (" + capability + ")";
+ }
+ }
+
+ /**
+ * Check whether the given premium capability is available for purchase from the carrier.
+ * If this is {@code true}, the capability can be purchased from the carrier using
+ * {@link #purchasePremiumCapability(int, Executor, Consumer)}.
+ *
+ * @param capability The premium capability to check.
+ * @return Whether the given premium capability is available to purchase.
+ * @throws SecurityException if the caller does not hold permission READ_BASIC_PHONE_STATE.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE)
+ public boolean isPremiumCapabilityAvailableForPurchase(@PremiumCapability int capability) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ throw new IllegalStateException("telephony service is null.");
+ }
+ return telephony.isPremiumCapabilityAvailableForPurchase(capability, getSubId());
+ } catch (RemoteException ex) {
+ ex.rethrowAsRuntimeException();
+ }
+ return false;
+ }
+
+ /**
+ * Purchase premium capability request was successful. Subsequent attempts will return
+ * {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED} until the booster expires.
+ * The expiry time is determined by the type or duration of boost purchased from the carrier,
+ * provided at {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_PURCHASE_URL_STRING}.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS = 1;
+
+ /**
+ * Purchase premium capability failed because the request is throttled for the amount of time
+ * specified by {@link CarrierConfigManager
+ * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
+ * or {@link CarrierConfigManager
+ * #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}.
+ * Subsequent attempts will return the same error until the request is no longer throttled
+ * or throttling conditions change.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED = 2;
+
+ /**
+ * Purchase premium capability failed because it is already purchased and available.
+ * Subsequent attempts will return the same error until the booster expires.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED = 3;
+
+ /**
+ * Purchase premium capability failed because a request was already made and is in progress.
+ * This may have been requested by either the same app or another app.
+ * Subsequent attempts will return the same error until the previous request completes.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS = 4;
+
+ /**
+ * Purchase premium capability failed because the user disabled the feature.
+ * Subsequent attempts will return the same error until the user re-enables the feature.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED = 5;
+
+ /**
+ * Purchase premium capability failed because the user canceled the operation.
+ * Subsequent attempts will be throttled for the amount of time specified by
+ * {@link CarrierConfigManager
+ * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
+ * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED = 6;
+
+ /**
+ * Purchase premium capability failed because the carrier disabled or does not support
+ * the capability, as specified in
+ * {@link CarrierConfigManager#KEY_SUPPORTED_PREMIUM_CAPABILITIES_INT_ARRAY}.
+ * Subsequent attempts will return the same error until the carrier enables the feature.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED = 7;
+
+ /**
+ * Purchase premium capability failed because the carrier app did not indicate success.
+ * Subsequent attempts will be throttled for the amount of time specified by
+ * {@link CarrierConfigManager
+ * #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
+ * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR = 8;
+
+ /**
+ * Purchase premium capability failed because we did not receive a response from the user
+ * for the booster notification within the time specified by
+ * {@link CarrierConfigManager#KEY_PREMIUM_CAPABILITY_NOTIFICATION_DISPLAY_TIMEOUT_MILLIS_LONG}.
+ * The booster notification will be automatically dismissed and subsequent attempts will be
+ * throttled for the amount of time specified by
+ * {@link CarrierConfigManager
+ * #KEY_PREMIUM_CAPABILITY_NOTIFICATION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
+ * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT = 9;
+
+ /**
+ * Purchase premium capability failed because the device does not support the feature.
+ * Subsequent attempts will return the same error.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED = 10;
+
+ /**
+ * Purchase premium capability failed because the telephony service is down or unavailable.
+ * Subsequent attempts will return the same error until request conditions are satisfied.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED = 11;
+
+ /**
+ * Purchase premium capability failed because the network is not available.
+ * Subsequent attempts will return the same error until network conditions change.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE = 12;
+
+ /**
+ * Purchase premium capability failed because the network is congested.
+ * Subsequent attempts will be throttled for the amount of time specified by
+ * {@link CarrierConfigManager
+ * #KEY_PREMIUM_CAPABILITY_PURCHASE_CONDITION_BACKOFF_HYSTERESIS_TIME_MILLIS_LONG}
+ * and return {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED}.
+ * Throttling will be reevaluated when the network is no longer congested.
+ */
+ public static final int PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED = 13;
+
+ /**
+ * Results of the purchase premium capability request.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "PURCHASE_PREMIUM_CAPABILITY_RESULT_" }, value = {
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE,
+ PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED})
+ public @interface PurchasePremiumCapabilityResult {}
+
+ /**
+ * Returns the purchase result {@link PurchasePremiumCapabilityResult} as a String.
+ *
+ * @param result The purchase premium capability result.
+ * @return The purchase result as a String.
+ * @hide
+ */
+ public static String convertPurchaseResultToString(
+ @PurchasePremiumCapabilityResult int result) {
+ switch (result) {
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS:
+ return "SUCCESS";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_THROTTLED:
+ return "THROTTLED";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED:
+ return "ALREADY_PURCHASED";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_IN_PROGRESS:
+ return "ALREADY_IN_PROGRESS";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_DISABLED:
+ return "USER_DISABLED";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_USER_CANCELED:
+ return "USER_CANCELED";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_DISABLED:
+ return "CARRIER_DISABLED";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_CARRIER_ERROR:
+ return "CARRIER_ERROR";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_TIMEOUT:
+ return "TIMEOUT";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_FEATURE_NOT_SUPPORTED:
+ return "FEATURE_NOT_SUPPORTED";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED:
+ return "REQUEST_FAILED";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_NOT_AVAILABLE:
+ return "NETWORK_NOT_AVAILABLE";
+ case PURCHASE_PREMIUM_CAPABILITY_RESULT_NETWORK_CONGESTED:
+ return "NETWORK_CONGESTED";
+ default:
+ return "UNKNOWN (" + result + ")";
+ }
+ }
+
+ /**
+ * Purchase the given premium capability from the carrier.
+ * This requires user action to purchase the boost from the carrier.
+ * If this returns {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_SUCCESS} or
+ * {@link #PURCHASE_PREMIUM_CAPABILITY_RESULT_ALREADY_PURCHASED}, applications can request
+ * the premium capability via {@link ConnectivityManager#requestNetwork}.
+ *
+ * @param capability The premium capability to purchase.
+ * @param executor The callback executor for the response.
+ * @param callback The result of the purchase request.
+ * One of {@link PurchasePremiumCapabilityResult}.
+ * @throws SecurityException if the caller does not hold permission READ_BASIC_PHONE_STATE.
+ * @see #isPremiumCapabilityAvailableForPurchase(int) to check whether the capability is valid
+ */
+ @RequiresPermission(android.Manifest.permission.READ_BASIC_PHONE_STATE)
+ public void purchasePremiumCapability(@PremiumCapability int capability,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull @PurchasePremiumCapabilityResult Consumer<Integer> callback) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
+ @Override
+ public void accept(int result) {
+ executor.execute(() -> callback.accept(result));
+ }
+ };
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null) {
+ callback.accept(PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED);
+ return;
+ }
+ telephony.purchasePremiumCapability(capability, internalCallback, getSubId());
+ } catch (RemoteException ex) {
+ callback.accept(PURCHASE_PREMIUM_CAPABILITY_RESULT_REQUEST_FAILED);
+ }
+ }
+
+ /**
* Get last known cell identity.
* Require {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
* com.android.phone.permission.ACCESS_LAST_KNOWN_CELL_ID, otherwise throws SecurityException.
diff --git a/telephony/java/android/telephony/data/DataService.java b/telephony/java/android/telephony/data/DataService.java
index 700d61597d00..d8b2cbebdf28 100644
--- a/telephony/java/android/telephony/data/DataService.java
+++ b/telephony/java/android/telephony/data/DataService.java
@@ -725,7 +725,7 @@ public abstract class DataService extends Service {
@Override
public void onDestroy() {
- mHandlerThread.quit();
+ mHandlerThread.quitSafely();
super.onDestroy();
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 3032babee07f..648866b6662d 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2529,6 +2529,16 @@ interface ITelephony {
void getSlicingConfig(in ResultReceiver callback);
/**
+ * Check whether the given premium capability is available for purchase from the carrier.
+ */
+ boolean isPremiumCapabilityAvailableForPurchase(int capability, int subId);
+
+ /**
+ * Purchase the given premium capability from the carrier.
+ */
+ void purchasePremiumCapability(int capability, IIntegerConsumer callback, int subId);
+
+ /**
* Register an IMS connection state callback
*/
void registerImsStateCallback(int subId, int feature, in IImsStateCallback cb,
@@ -2548,9 +2558,6 @@ interface ITelephony {
CellIdentity getLastKnownCellIdentity(int subId, String callingPackage,
String callingFeatureId);
- /** Check if telephony new data stack is enabled. */
- boolean isUsingNewDataStack();
-
/**
* @return true if the modem service is set successfully, false otherwise.
*/
diff --git a/tests/Codegen/Android.bp b/tests/Codegen/Android.bp
index ddbf16817b94..7fbe3b37f99e 100644
--- a/tests/Codegen/Android.bp
+++ b/tests/Codegen/Android.bp
@@ -24,6 +24,14 @@ android_test {
plugins: [
"staledataclass-annotation-processor",
],
+ // Exports needed for staledataclass-annotation-processor, see b/139342589.
+ javacflags: [
+ "-J--add-modules=jdk.compiler",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
static_libs: [
"junit",
"hamcrest",
diff --git a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java b/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java
index 2e515705a253..761efe4a8484 100644
--- a/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java
+++ b/tests/FixVibrateSetting/src/com/android/fixvibratesetting/FixVibrateSetting.java
@@ -110,7 +110,7 @@ public class FixVibrateSetting extends Activity implements View.OnClickListener
private void test() {
Intent intent = new Intent(this, FixVibrateSetting.class);
- PendingIntent pending = PendingIntent.getActivity(this, 0, intent, 0);
+ PendingIntent pending = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
Notification n = new Notification.Builder(this)
.setSmallIcon(R.drawable.stat_sys_warning)
diff --git a/tests/FlickerTests/AndroidManifest.xml b/tests/FlickerTests/AndroidManifest.xml
index e173eba0a393..487a0c3bd465 100644
--- a/tests/FlickerTests/AndroidManifest.xml
+++ b/tests/FlickerTests/AndroidManifest.xml
@@ -40,6 +40,8 @@
<uses-permission android:name="android.permission.READ_LOGS"/>
<!-- ATM.removeRootTasksWithActivityTypes() -->
<uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <!-- ActivityOptions.makeCustomTaskAnimation() -->
+ <uses-permission android:name="android.permission.START_TASKS_FROM_RECENTS" />
<!-- Allow the test to write directly to /sdcard/ -->
<application android:requestLegacyExternalStorage="true">
<uses-library android:name="android.test.runner"/>
diff --git a/tests/FlickerTests/res/anim/show_2000ms.xml b/tests/FlickerTests/res/anim/show_2000ms.xml
new file mode 100644
index 000000000000..76e375f12e31
--- /dev/null
+++ b/tests/FlickerTests/res/anim/show_2000ms.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<translate xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="2000"
+ android:fromXDelta="0"
+ android:toXDelta="0" /> \ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
index 1e798f3ed12e..f77ec8a4a853 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/BaseTest.kt
@@ -21,13 +21,14 @@ import android.platform.test.annotations.Presubmit
import androidx.test.platform.app.InstrumentationRegistry
import com.android.launcher3.tapl.LauncherInstrumentation
import com.android.server.wm.flicker.dsl.FlickerBuilder
+import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Assume
import org.junit.Test
/**
- * Base test class containing common assertions for [ComponentMatcher.NAV_BAR],
- * [ComponentMatcher.TASK_BAR], [ComponentMatcher.STATUS_BAR], and general assertions
+ * Base test class containing common assertions for [ComponentNameMatcher.NAV_BAR],
+ * [ComponentNameMatcher.TASK_BAR], [ComponentNameMatcher.STATUS_BAR], and general assertions
* (layers visible in consecutive states, entire screen covered, etc.)
*/
abstract class BaseTest @JvmOverloads constructor(
@@ -72,7 +73,7 @@ abstract class BaseTest @JvmOverloads constructor(
open fun entireScreenCovered() = testSpec.entireScreenCovered()
/**
- * Checks that the [ComponentMatcher.NAV_BAR] layer is visible during the whole transition
+ * Checks that the [ComponentNameMatcher.NAV_BAR] layer is visible during the whole transition
*
* Note: Phones only
*/
@@ -84,7 +85,8 @@ abstract class BaseTest @JvmOverloads constructor(
}
/**
- * Checks the position of the [ComponentMatcher.NAV_BAR] at the start and end of the transition
+ * Checks the position of the [ComponentNameMatcher.NAV_BAR] at the start and end of the
+ * transition
*
* Note: Phones only
*/
@@ -96,7 +98,7 @@ abstract class BaseTest @JvmOverloads constructor(
}
/**
- * Checks that the [ComponentMatcher.NAV_BAR] window is visible during the whole transition
+ * Checks that the [ComponentNameMatcher.NAV_BAR] window is visible during the whole transition
*
* Note: Phones only
*/
@@ -108,8 +110,8 @@ abstract class BaseTest @JvmOverloads constructor(
}
/**
- * Checks that the [ComponentMatcher.TASK_BAR] window is visible at the start and end of the
- * transition
+ * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible at the start and end of
+ * the transition
*
* Note: Large screen only
*/
@@ -121,7 +123,8 @@ abstract class BaseTest @JvmOverloads constructor(
}
/**
- * Checks that the [ComponentMatcher.TASK_BAR] window is visible during the whole transition
+ * Checks that the [ComponentNameMatcher.TASK_BAR] window is visible during the whole
+ * transition
*
* Note: Large screen only
*/
@@ -133,7 +136,7 @@ abstract class BaseTest @JvmOverloads constructor(
}
/**
- * Checks that the [ComponentMatcher.STATUS_BAR] layer is visible at the start and end
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is visible at the start and end
* of the transition
*/
@Presubmit
@@ -142,7 +145,7 @@ abstract class BaseTest @JvmOverloads constructor(
testSpec.statusBarLayerIsVisibleAtStartAndEnd()
/**
- * Checks the position of the [ComponentMatcher.STATUS_BAR] at the start and end of the
+ * Checks the position of the [ComponentNameMatcher.STATUS_BAR] at the start and end of the
* transition
*/
@Presubmit
@@ -150,7 +153,8 @@ abstract class BaseTest @JvmOverloads constructor(
open fun statusBarLayerPositionAtStartAndEnd() = testSpec.statusBarLayerPositionAtStartAndEnd()
/**
- * Checks that the [ComponentMatcher.STATUS_BAR] window is visible during the whole transition
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] window is visible during the whole
+ * transition
*/
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index b8fe9f9df882..ef5cec29f898 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -33,12 +33,12 @@ import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelpe
import org.junit.Assume.assumeNotNull
class ActivityEmbeddingAppHelper @JvmOverloads constructor(
- instr: Instrumentation,
- launcherName: String = ActivityOptions.ACTIVITY_EMBEDDING_LAUNCHER_NAME,
- component: ComponentNameMatcher = MAIN_ACTIVITY_COMPONENT,
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.ActivityEmbedding.MainActivity.LABEL,
+ component: ComponentNameMatcher = MAIN_ACTIVITY_COMPONENT,
+ launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+ .getInstance(instr)
+ .launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
/**
@@ -47,8 +47,8 @@ class ActivityEmbeddingAppHelper @JvmOverloads constructor(
*/
fun launchPlaceholderSplit(wmHelper: WindowManagerStateHelper) {
val launchButton = uiDevice.wait(
- Until.findObject(By.res(getPackage(), "launch_placeholder_split_button")),
- FIND_TIMEOUT)
+ Until.findObject(By.res(getPackage(), "launch_placeholder_split_button")),
+ FIND_TIMEOUT)
require(launchButton != null) {
"Can't find launch placeholder split button on screen."
}
@@ -62,14 +62,15 @@ class ActivityEmbeddingAppHelper @JvmOverloads constructor(
companion object {
private const val TAG = "ActivityEmbeddingAppHelper"
- val MAIN_ACTIVITY_COMPONENT = ActivityOptions
- .ACTIVITY_EMBEDDING_MAIN_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
+ val MAIN_ACTIVITY_COMPONENT =
+ ActivityOptions.ActivityEmbedding.MainActivity.COMPONENT.toFlickerComponent()
- val PLACEHOLDER_PRIMARY_COMPONENT = ActivityOptions
- .ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
+ val PLACEHOLDER_PRIMARY_COMPONENT =
+ ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT
+ .toFlickerComponent()
- val PLACEHOLDER_SECONDARY_COMPONENT = ActivityOptions
- .ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME
+ val PLACEHOLDER_SECONDARY_COMPONENT =
+ ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT
.toFlickerComponent()
@JvmStatic
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
index 826cc2ef16d6..4ff4e316476b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/AppPairsHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/AppPairsHelper.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.helpers
+package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
import com.android.server.wm.traces.common.ComponentNameMatcher
@@ -23,4 +23,4 @@ class AppPairsHelper(
instrumentation: Instrumentation,
activityLabel: String,
component: ComponentNameMatcher
-) : BaseAppHelper(instrumentation, activityLabel, component)
+) : StandardAppHelper(instrumentation, activityLabel, component)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
index b696fc30c19f..132e7b6fef6d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/FixedOrientationAppHelper.kt
@@ -24,11 +24,11 @@ import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.parser.toFlickerComponent
class FixedOrientationAppHelper @JvmOverloads constructor(
- instr: Instrumentation,
- launcherName: String = ActivityOptions.PORTRAIT_ONLY_ACTIVITY_LAUNCHER_NAME,
- component: ComponentNameMatcher =
- ActivityOptions.PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
- ) : StandardAppHelper(instr, launcherName, component, launcherStrategy)
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.PortraitOnlyActivity.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.PortraitOnlyActivity.COMPONENT.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+ .getInstance(instr)
+ .launcherStrategy
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.kt
new file mode 100644
index 000000000000..2d81e0d9e165
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/GameAppHelper.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.server.wm.flicker.helpers
+
+import android.app.Instrumentation
+import android.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Direction
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.parser.toFlickerComponent
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
+
+class GameAppHelper @JvmOverloads constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.Game.LABEL,
+ component: ComponentNameMatcher = ActivityOptions.Game.COMPONENT.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy =
+ LauncherStrategyFactory.getInstance(instr).launcherStrategy,
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+
+ /**
+ * Swipes down in the mock game app.
+ *
+ * @return true if the swipe operation is successful.
+ */
+ fun swipeDown(): Boolean {
+ val gameView = uiDevice.wait(
+ Until.findObject(By.res(getPackage(), GAME_APP_VIEW_RES)), WAIT_TIME_MS)
+ require(gameView != null) { "Mock game app view not found." }
+
+ val bound = gameView.getVisibleBounds()
+ return uiDevice.swipe(
+ bound.centerX(), bound.top, bound.centerX(), bound.centerY(), SWIPE_STEPS)
+ }
+
+ /**
+ * Switches to a recent app by quick switch gesture. This function can be used in both portrait
+ * and landscape mode.
+ *
+ * @param wmHelper Helper used to get window region.
+ * @param direction UiAutomator Direction enum to indicate the swipe direction.
+ *
+ * @return true if the swipe operation is successful.
+ */
+ fun switchToPreviousAppByQuickSwitchGesture(
+ wmHelper: WindowManagerStateHelper,
+ direction: Direction
+ ): Boolean {
+ val ratioForScreenBottom = 0.97
+ val fullView = wmHelper.getWindowRegion(componentMatcher)
+ require(!fullView.isEmpty) { "Target $componentMatcher view not found." }
+
+ val bound = fullView.bounds
+ val targetYPos = bound.bottom * ratioForScreenBottom
+ val endX = when (direction) {
+ Direction.LEFT -> bound.left
+ Direction.RIGHT -> bound.right
+ else -> {
+ throw IllegalStateException("Only left or right direction is allowed.")
+ }
+ }
+ return uiDevice.swipe(
+ bound.centerX(), targetYPos.toInt(), endX, targetYPos.toInt(), SWIPE_STEPS)
+ }
+
+ /**
+ * Waits for view idel with timeout, then checkes the target object whether visible on screen.
+ *
+ * @param packageName The targe application's package name.
+ * @param identifier The resource id of the target object.
+ * @param timeout The timeout duration in milliseconds.
+ *
+ * @return true if the target object exists.
+ */
+ @JvmOverloads
+ fun isTargetObjVisible(
+ packageName: String,
+ identifier: String,
+ timeout: Long = WAIT_TIME_MS
+ ): Boolean {
+ uiDevice.waitForIdle(timeout)
+ return uiDevice.hasObject(By.res(packageName, identifier))
+ }
+
+ companion object {
+ private const val GAME_APP_VIEW_RES = "container"
+ private const val WAIT_TIME_MS = 3_000L
+ private const val SWIPE_STEPS = 30
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
index e01cceb5d636..b5b0da934ad9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeAppAutoFocusHelper.kt
@@ -32,9 +32,9 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
instr: Instrumentation,
private val rotation: Int,
private val imePackageName: String = IME_PACKAGE,
- launcherName: String = ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.Ime.AutoFocusActivity.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
+ ActivityOptions.Ime.AutoFocusActivity.COMPONENT.toFlickerComponent()
) : ImeAppHelper(instr, launcherName, component) {
override fun openIME(wmHelper: WindowManagerStateHelper) {
// do nothing (the app is focused automatically)
@@ -62,21 +62,22 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
fun startDialogThemedActivity(wmHelper: WindowManagerStateHelper) {
val button = uiDevice.wait(Until.findObject(By.res(getPackage(),
- "start_dialog_themed_activity_btn")), FIND_TIMEOUT)
+ "start_dialog_themed_activity_btn")), FIND_TIMEOUT)
requireNotNull(button) {
"Button not found, this usually happens when the device " +
- "was left in an unknown state (e.g. Screen turned off)"
+ "was left in an unknown state (e.g. Screen turned off)"
}
button.click()
wmHelper.StateSyncBuilder()
.withFullScreenApp(
- ActivityOptions.DIALOG_THEMED_ACTIVITY_COMPONENT_NAME.toFlickerComponent())
+ ActivityOptions.DialogThemedActivity.COMPONENT.toFlickerComponent())
.waitForAndVerify()
}
+
fun dismissDialog(wmHelper: WindowManagerStateHelper) {
val dialog = uiDevice.wait(
- Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT)
+ Until.findObject(By.text("Dialog for test")), FIND_TIMEOUT)
// Pressing back key to dismiss the dialog
if (dialog != null) {
@@ -86,9 +87,10 @@ class ImeAppAutoFocusHelper @JvmOverloads constructor(
.waitForAndVerify()
}
}
+
fun getInsetsVisibleFromDialog(type: Int): Boolean {
val insetsVisibilityTextView = uiDevice.wait(
- Until.findObject(By.res("android:id/text1")), FIND_TIMEOUT)
+ Until.findObject(By.res("android:id/text1")), FIND_TIMEOUT)
if (insetsVisibilityTextView != null) {
val visibility = insetsVisibilityTextView.text.toString()
val matcher = when (type) {
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 b672b1bc0da5..56b6b92dd6ef 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
@@ -28,12 +28,11 @@ import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelpe
open class ImeAppHelper @JvmOverloads constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.IME_ACTIVITY_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.Ime.Default.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.IME_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
+ ActivityOptions.Ime.Default.COMPONENT.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy =
+ LauncherStrategyFactory.getInstance(instr).launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
/**
* Opens the IME and wait for it to be displayed
@@ -73,8 +72,8 @@ open class ImeAppHelper @JvmOverloads constructor(
open fun finishActivity(wmHelper: WindowManagerStateHelper) {
val finishButton = uiDevice.wait(
- Until.findObject(By.res(getPackage(), "finish_activity_btn")),
- FIND_TIMEOUT)
+ Until.findObject(By.res(getPackage(), "finish_activity_btn")),
+ FIND_TIMEOUT)
requireNotNull(finishButton) {
"Finish activity button not found, probably IME activity is not on the screen?"
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
index df47e9dfacd9..bfb68da20836 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeEditorPopupDialogAppHelper.kt
@@ -26,16 +26,16 @@ import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelpe
class ImeEditorPopupDialogAppHelper @JvmOverloads constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.Ime.EditorPopupDialogActivity.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
+ ActivityOptions.Ime.EditorPopupDialogActivity.COMPONENT.toFlickerComponent()
) : ImeAppHelper(instr, launcherName, component) {
override fun openIME(wmHelper: WindowManagerStateHelper) {
val editText = uiDevice.wait(Until.findObject(By.text("focused editText")), FIND_TIMEOUT)
- require(editText != null) {
+ requireNotNull(editText) {
"Text field not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)"
+ "was left in an unknown state (e.g. in split screen)"
}
editText.click()
waitIMEShown(wmHelper)
@@ -43,7 +43,7 @@ class ImeEditorPopupDialogAppHelper @JvmOverloads constructor(
fun dismissDialog(wmHelper: WindowManagerStateHelper) {
val dismissButton = uiDevice.wait(
- Until.findObject(By.text("Dismiss")), FIND_TIMEOUT)
+ Until.findObject(By.text("Dismiss")), FIND_TIMEOUT)
// Pressing back key to dismiss the dialog
if (dismissButton != null) {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
index d3945c1749e3..3b8d3c3440a8 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ImeStateInitializeHelper.kt
@@ -25,10 +25,9 @@ import com.android.server.wm.traces.parser.toFlickerComponent
class ImeStateInitializeHelper @JvmOverloads constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.Ime.StateInitializeActivity.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.IME_ACTIVITY_INITIALIZE_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
+ ActivityOptions.Ime.StateInitializeActivity.COMPONENT.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy =
+ LauncherStrategyFactory.getInstance(instr).launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
index 4d0fbc4a0e38..cb54b5732c8d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/SimpleAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/LaunchBubbleHelper.kt
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.helpers
+package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
+import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.traces.parser.toFlickerComponent
-import com.android.wm.shell.flicker.testapp.Components
-class SimpleAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
+class LaunchBubbleHelper(instrumentation: Instrumentation) : StandardAppHelper(
instrumentation,
- Components.SimpleActivity.LABEL,
- Components.SimpleActivity.COMPONENT.toFlickerComponent()
-) \ No newline at end of file
+ ActivityOptions.Bubbles.LaunchBubble.LABEL,
+ ActivityOptions.Bubbles.LaunchBubble.COMPONENT.toFlickerComponent()
+)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
new file mode 100644
index 000000000000..dde0b3e6873d
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MailAppHelper.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.support.test.launcherhelper.ILauncherStrategy
+import android.support.test.launcherhelper.LauncherStrategyFactory
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.Direction
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.parser.toFlickerComponent
+
+class MailAppHelper @JvmOverloads constructor(
+ instr: Instrumentation,
+ launcherName: String = ActivityOptions.Mail.LABEL,
+ component: ComponentNameMatcher =
+ ActivityOptions.Mail.COMPONENT.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
+ .getInstance(instr)
+ .launcherStrategy
+) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+
+ fun openMail(rowIdx: Int) {
+ val rowSel = By.res(getPackage(), "mail_row_item_text")
+ .textEndsWith(String.format("%04d", rowIdx))
+ var row: UiObject2? = null
+ for (i in 1..1000) {
+ row = uiDevice.wait(Until.findObject(rowSel), SHORT_WAIT_TIME_MS)
+ if (row != null) break
+ scrollDown()
+ }
+ require(row != null) { "" }
+ row.click()
+ uiDevice.wait(Until.gone(By.res(getPackage(), MAIL_LIST_RES_ID)), FIND_TIMEOUT)
+ }
+
+ fun scrollDown() {
+ val listView = waitForMailList()
+ listView.scroll(Direction.DOWN, 1.0f)
+ }
+
+ fun waitForMailList(): UiObject2 {
+ val sel = By.res(getPackage(), MAIL_LIST_RES_ID).scrollable(true)
+ val ret = uiDevice.wait(Until.findObject(sel), FIND_TIMEOUT)
+ requireNotNull(ret) { "Unable to find $MAIL_LIST_RES_ID object" }
+ return ret
+ }
+
+ companion object {
+ private const val SHORT_WAIT_TIME_MS = 5000L
+ private const val MAIL_LIST_RES_ID = "mail_recycle_view"
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
index 245a82f938b3..9bdfadb35ff3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/MultiWindowHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/MultiWindowUtils.kt
@@ -14,20 +14,31 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.helpers
+package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
import android.content.Context
import android.provider.Settings
+import android.util.Log
+import com.android.compatibility.common.util.SystemUtil
import com.android.server.wm.traces.common.ComponentNameMatcher
+import java.io.IOException
-class MultiWindowHelper(
+class MultiWindowUtils(
instrumentation: Instrumentation,
activityLabel: String,
componentsInfo: ComponentNameMatcher
-) : BaseAppHelper(instrumentation, activityLabel, componentsInfo) {
+) : StandardAppHelper(instrumentation, activityLabel, componentsInfo) {
companion object {
+ fun executeShellCommand(instrumentation: Instrumentation, cmd: String) {
+ try {
+ SystemUtil.runShellCommand(instrumentation, cmd)
+ } catch (e: IOException) {
+ Log.e(MultiWindowUtils::class.simpleName, "executeShellCommand error! $e")
+ }
+ }
+
fun getDevEnableNonResizableMultiWindow(context: Context): Int =
Settings.Global.getInt(context.contentResolver,
Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
index 9fb574cf1c04..f3386afa610c 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NewTasksAppHelper.kt
@@ -29,9 +29,9 @@ import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelpe
class NewTasksAppHelper @JvmOverloads constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.LaunchNewTask.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
+ ActivityOptions.LaunchNewTask.COMPONENT.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
.getInstance(instr)
.launcherStrategy
@@ -43,7 +43,7 @@ class NewTasksAppHelper @JvmOverloads constructor(
requireNotNull(button) {
"Button not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)"
+ "was left in an unknown state (e.g. in split screen)"
}
button.click()
wmHelper.StateSyncBuilder()
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
index a1dbeeaef5cc..19ce3ba21812 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NonResizeableAppHelper.kt
@@ -25,10 +25,9 @@ import com.android.server.wm.traces.parser.toFlickerComponent
class NonResizeableAppHelper @JvmOverloads constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.NON_RESIZEABLE_ACTIVITY_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.NonResizeableActivity.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.NON_RESIZEABLE_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
+ ActivityOptions.NonResizeableActivity.COMPONENT.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy =
+ LauncherStrategyFactory.getInstance(instr).launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
index b031a4523ab0..97642f1617af 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/NotificationAppHelper.kt
@@ -28,25 +28,28 @@ import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelpe
class NotificationAppHelper @JvmOverloads constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.NOTIFICATION_ACTIVITY_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.Notification.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.NOTIFICATION_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
+ ActivityOptions.Notification.COMPONENT.toFlickerComponent(),
launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
+ .getInstance(instr)
+ .launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
fun postNotification(wmHelper: WindowManagerStateHelper) {
val button = uiDevice.wait(
- Until.findObject(By.res(getPackage(), "post_notification")),
- FIND_TIMEOUT)
+ Until.findObject(By.res(getPackage(), "post_notification")),
+ FIND_TIMEOUT)
- require(button != null) {
+ requireNotNull(button) {
"Post notification button not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)"
+ "was left in an unknown state (e.g. in split screen)"
}
button.click()
uiDevice.wait(Until.findObject(By.text("Flicker Test Notification")), FIND_TIMEOUT)
- ?: error("Flicker Notification not found")
+ ?: error("Flicker Notification not found")
+ wmHelper.StateSyncBuilder()
+ .withAppTransitionIdle()
+ .waitForAndVerify()
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
index bdc05e7ecb52..8d4da8a013cf 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/helpers/PipAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.kt
@@ -14,28 +14,24 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.helpers
+package com.android.server.wm.flicker.helpers
import android.app.Instrumentation
import android.media.session.MediaController
import android.media.session.MediaSessionManager
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.flicker.testapp.ActivityOptions
import com.android.server.wm.traces.common.Rect
import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import com.android.server.wm.traces.common.region.Region
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
-import com.android.wm.shell.flicker.pip.tv.closeTvPipWindow
-import com.android.wm.shell.flicker.pip.tv.isFocusedOrHasFocusedChild
-import com.android.wm.shell.flicker.testapp.Components
-class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
+open class PipAppHelper(instrumentation: Instrumentation) : StandardAppHelper(
instrumentation,
- Components.PipActivity.LABEL,
- Components.PipActivity.COMPONENT.toFlickerComponent()
+ ActivityOptions.Pip.LABEL,
+ ActivityOptions.Pip.COMPONENT.toFlickerComponent()
) {
private val mediaSessionManager: MediaSessionManager
get() = context.getSystemService(MediaSessionManager::class.java)
@@ -46,16 +42,11 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
it.packageName == `package`
}
- fun clickObject(resId: String) {
+ open fun clickObject(resId: String) {
val selector = By.res(`package`, resId)
val obj = uiDevice.findObject(selector) ?: error("Could not find `$resId` object")
- if (!isTelevision) {
- obj.click()
- } else {
- focusOnObject(selector) || error("Could not focus on `$resId` object")
- uiDevice.pressDPadCenter()
- }
+ obj.click()
}
/**
@@ -85,20 +76,6 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) =
launchViaIntentAndWaitShown(wmHelper)
- private fun focusOnObject(selector: BySelector): Boolean {
- // We expect all the focusable UI elements to be arranged in a way so that it is possible
- // to "cycle" over all them by clicking the D-Pad DOWN button, going back up to "the top"
- // from "the bottom".
- repeat(FOCUS_ATTEMPTS) {
- uiDevice.findObject(selector)?.apply { if (isFocusedOrHasFocusedChild) return true }
- ?: error("The object we try to focus on is gone.")
-
- uiDevice.pressDPadDown()
- uiDevice.waitForIdle()
- }
- return false
- }
-
fun clickEnterPipButton(wmHelper: WindowManagerStateHelper) {
clickObject(ENTER_PIP_BUTTON_ID)
@@ -140,12 +117,8 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
"Use PipAppHelper.closePipWindow(wmHelper) instead",
ReplaceWith("closePipWindow(wmHelper)")
)
- fun closePipWindow() {
- if (isTelevision) {
- uiDevice.closeTvPipWindow()
- } else {
- closePipWindow(WindowManagerStateHelper(mInstrumentation))
- }
+ open fun closePipWindow() {
+ closePipWindow(WindowManagerStateHelper(mInstrumentation))
}
private fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect {
@@ -159,20 +132,16 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
/**
* Taps the pip window and dismisses it by clicking on the X button.
*/
- fun closePipWindow(wmHelper: WindowManagerStateHelper) {
- if (isTelevision) {
- uiDevice.closeTvPipWindow()
- } else {
- val windowRect = getWindowRect(wmHelper)
- uiDevice.click(windowRect.centerX(), windowRect.centerY())
- // search and interact with the dismiss button
- val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
- uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
- val dismissPipObject = uiDevice.findObject(dismissSelector)
- ?: error("PIP window dismiss button not found")
- val dismissButtonBounds = dismissPipObject.visibleBounds
- uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
- }
+ open fun closePipWindow(wmHelper: WindowManagerStateHelper) {
+ val windowRect = getWindowRect(wmHelper)
+ uiDevice.click(windowRect.centerX(), windowRect.centerY())
+ // search and interact with the dismiss button
+ val dismissSelector = By.res(SYSTEMUI_PACKAGE, "dismiss")
+ uiDevice.wait(Until.hasObject(dismissSelector), FIND_TIMEOUT)
+ val dismissPipObject = uiDevice.findObject(dismissSelector)
+ ?: error("PIP window dismiss button not found")
+ val dismissButtonBounds = dismissPipObject.visibleBounds
+ uiDevice.click(dismissButtonBounds.centerX(), dismissButtonBounds.centerY())
// Wait for animation to complete.
wmHelper.StateSyncBuilder()
@@ -210,10 +179,23 @@ class PipAppHelper(instrumentation: Instrumentation) : BaseAppHelper(
wmHelper.StateSyncBuilder()
.withAppTransitionIdle()
.waitForAndVerify()
+ waitForPipWindowToExpandFrom(wmHelper, Region.from(windowRect))
+ }
+
+ private fun waitForPipWindowToExpandFrom(
+ wmHelper: WindowManagerStateHelper,
+ windowRect: Region
+ ) {
+ wmHelper.StateSyncBuilder().add("pipWindowExpanded") {
+ val pipAppWindow = it.wmState.visibleWindows.firstOrNull { window ->
+ this.windowMatchesAnyOf(window)
+ } ?: return@add false
+ val pipRegion = pipAppWindow.frameRegion
+ return@add pipRegion.coversMoreThan(windowRect)
+ }.waitForAndVerify()
}
companion object {
- private const val FOCUS_ATTEMPTS = 20
private const val ENTER_PIP_BUTTON_ID = "enter_pip"
private const val WITH_CUSTOM_ACTIONS_BUTTON_ID = "with_custom_actions"
private const val MEDIA_SESSION_START_RADIO_BUTTON_ID = "media_session_start"
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
index 6d466d72d7dd..fc1ff7c84675 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SeamlessRotationAppHelper.kt
@@ -25,10 +25,9 @@ import com.android.server.wm.traces.parser.toFlickerComponent
class SeamlessRotationAppHelper @JvmOverloads constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.SEAMLESS_ACTIVITY_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.SeamlessRotation.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.SEAMLESS_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
+ ActivityOptions.SeamlessRotation.COMPONENT.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy =
+ LauncherStrategyFactory.getInstance(instr).launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
index 804ab3864591..0e1df411d394 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ShowWhenLockedAppHelper.kt
@@ -25,10 +25,9 @@ import com.android.server.wm.traces.parser.toFlickerComponent
class ShowWhenLockedAppHelper @JvmOverloads constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.SHOW_WHEN_LOCKED_ACTIVITY_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.ShowWhenLockedActivity.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.SHOW_WHEN_LOCKED_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
+ ActivityOptions.ShowWhenLockedActivity.COMPONENT.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy =
+ LauncherStrategyFactory.getInstance(instr).launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
index 5da273a7f1dc..e3461a74b4ce 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/SimpleAppHelper.kt
@@ -25,10 +25,9 @@ import com.android.server.wm.traces.parser.toFlickerComponent
class SimpleAppHelper @JvmOverloads constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.SIMPLE_ACTIVITY_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.SimpleActivity.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
+ ActivityOptions.SimpleActivity.COMPONENT.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy =
+ LauncherStrategyFactory.getInstance(instr).launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy)
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
index 060e9af4a51c..f4ea37f2127a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -29,16 +29,15 @@ import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelpe
class TwoActivitiesAppHelper @JvmOverloads constructor(
instr: Instrumentation,
- launcherName: String = ActivityOptions.BUTTON_ACTIVITY_LAUNCHER_NAME,
+ launcherName: String = ActivityOptions.LaunchNewActivity.LABEL,
component: ComponentNameMatcher =
- ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent(),
- launcherStrategy: ILauncherStrategy = LauncherStrategyFactory
- .getInstance(instr)
- .launcherStrategy
+ ActivityOptions.LaunchNewActivity.COMPONENT.toFlickerComponent(),
+ launcherStrategy: ILauncherStrategy =
+ LauncherStrategyFactory.getInstance(instr).launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
private val secondActivityComponent =
- ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
+ ActivityOptions.SimpleActivity.COMPONENT.toFlickerComponent()
fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) {
val launchActivityButton = By.res(getPackage(), LAUNCH_SECOND_ACTIVITY)
@@ -46,7 +45,7 @@ class TwoActivitiesAppHelper @JvmOverloads constructor(
requireNotNull(button) {
"Button not found, this usually happens when the device " +
- "was left in an unknown state (e.g. in split screen)"
+ "was left in an unknown state (e.g. in split screen)"
}
button.click()
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 f7e5b239ec29..31fcc6a2fd45 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
@@ -26,11 +26,9 @@ 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.helpers.isShellTransitionsEnabled
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.traces.common.ComponentNameMatcher
-import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -113,30 +111,11 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
@Presubmit
@Test
- fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible(!isShellTransitionsEnabled)
+ fun imeWindowIsAlwaysVisible() = testSpec.imeWindowIsAlwaysVisible()
@Presubmit
@Test
- fun imeAppWindowVisibilityLegacy() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- // the app starts visible in live tile, and stays visible for the duration of entering
- // and exiting overview. However, legacy transitions seem to have a bug which causes
- // everything to restart during the test, so expect the app to disappear and come back.
- // 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)
- .then()
- .isAppWindowInvisible(testApp)
- .then()
- .isAppWindowVisible(testApp)
- }
- }
-
- @Presubmit
- @Test
- fun imeAppWindowIsAlwaysVisibleShellTransit() {
- Assume.assumeTrue(isShellTransitionsEnabled)
+ fun imeAppWindowIsAlwaysVisible() {
// the app starts visible in live tile, and stays visible for the duration of entering
// and exiting overview. Since we log 1x per frame, sometimes the activity visibility
// and the app visibility are updated together, sometimes not, thus ignore activity
@@ -148,21 +127,7 @@ open class ReOpenImeWindowTest(testSpec: FlickerTestParameter) : BaseTest(testSp
@Presubmit
@Test
- fun imeLayerIsBecomesVisibleLegacy() {
- Assume.assumeFalse(isShellTransitionsEnabled)
- testSpec.assertLayers {
- this.isVisible(ComponentNameMatcher.IME)
- .then()
- .isInvisible(ComponentNameMatcher.IME)
- .then()
- .isVisible(ComponentNameMatcher.IME)
- }
- }
-
- @Presubmit
- @Test
- fun imeLayerBecomesVisibleShellTransit() {
- Assume.assumeTrue(isShellTransitionsEnabled)
+ fun imeLayerBecomesVisible() {
testSpec.assertLayers {
this.isVisible(ComponentNameMatcher.IME)
}
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
index 08d7be2ac662..f019acc29316 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -83,19 +83,17 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp
override fun navBarLayerPositionAtStartAndEnd() = super.navBarLayerPositionAtStartAndEnd()
/**
- * Checks that the [ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME] activity is visible at
- * the start of the transition, that
- * [ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME] becomes visible during the
- * transition, and that [ActivityOptions.BUTTON_ACTIVITY_COMPONENT_NAME] is again visible
- * at the end
+ * Checks that the [ActivityOptions.LaunchNewActivity] activity is visible at
+ * the start of the transition, that [ActivityOptions.SimpleActivity] becomes visible during
+ * the transition, and that [ActivityOptions.LaunchNewActivity] is again visible at the end
*/
@Presubmit
@Test
fun finishSubActivity() {
- val buttonActivityComponent = ActivityOptions
- .BUTTON_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
- val imeAutoFocusActivityComponent = ActivityOptions
- .SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
+ val buttonActivityComponent =
+ ActivityOptions.LaunchNewActivity.COMPONENT.toFlickerComponent()
+ val imeAutoFocusActivityComponent =
+ ActivityOptions.SimpleActivity.COMPONENT.toFlickerComponent()
testSpec.assertWm {
this.isAppWindowOnTop(buttonActivityComponent)
.then()
@@ -106,7 +104,7 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp
}
/**
- * Checks that the [ComponentMatcher.LAUNCHER] window is not on top. The launcher cannot be
+ * Checks that the [ComponentNameMatcher.LAUNCHER] window is not on top. The launcher cannot be
* asserted with `isAppWindowVisible` because it contains 2 windows with the exact same name,
* and both are never simultaneously visible
*/
@@ -119,7 +117,7 @@ class ActivitiesTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSp
}
/**
- * Checks that the [ComponentMatcher.LAUNCHER] layer is never visible during the transition
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer is never visible during the transition
*/
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
index 354964d33ece..9b1c541da94e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdFromIcon.kt
@@ -62,19 +62,23 @@ class OpenAppColdFromIcon(
get() = {
super.transition(this)
setup {
- tapl.setExpectedRotation(Surface.ROTATION_0)
+ if (testSpec.isTablet) {
+ tapl.setExpectedRotation(testSpec.startRotation)
+ } else {
+ tapl.setExpectedRotation(Surface.ROTATION_0)
+ }
RemoveAllTasksButHomeRule.removeAllTasksButHome()
this.setRotation(testSpec.startRotation)
}
- teardown {
- testApp.exit(wmHelper)
- }
transitions {
tapl.goHome()
.switchToAllApps()
.getAppIcon(testApp.launcherName)
.launch(testApp.`package`)
}
+ teardown {
+ testApp.exit(wmHelper)
+ }
}
/** {@inheritDoc} */
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
index 77f28f60d2cc..2babf1c8e982 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarm.kt
@@ -19,6 +19,7 @@ package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.RequiresDevice
+import android.view.Surface
import android.view.WindowInsets
import android.view.WindowManager
import androidx.test.uiautomator.By
@@ -74,6 +75,13 @@ open class OpenAppFromNotificationWarm(
.withFullScreenApp(testApp)
.waitForAndVerify()
testApp.postNotification(wmHelper)
+
+ if (testSpec.isTablet) {
+ tapl.setExpectedRotation(testSpec.startRotation)
+ } else {
+ tapl.setExpectedRotation(Surface.ROTATION_0)
+ }
+
tapl.goHome()
wmHelper.StateSyncBuilder()
.withHomeActivityVisible()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
new file mode 100644
index 000000000000..d362c7d5c7be
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OverrideTaskTransitionTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.os.Bundle
+import android.os.Handler
+import android.platform.test.annotations.Presubmit
+import androidx.test.filters.RequiresDevice
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.wm.flicker.FlickerBuilderProvider
+import com.android.server.wm.flicker.FlickerParametersRunnerFactory
+import com.android.server.wm.flicker.FlickerTestParameter
+import com.android.server.wm.flicker.FlickerTestParameterFactory
+import com.android.server.wm.flicker.R
+import com.android.server.wm.flicker.annotation.Group4
+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.isShellTransitionsEnabled
+import com.android.server.wm.flicker.helpers.setRotation
+import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import com.android.server.wm.flicker.rules.RemoveAllTasksButHomeRule
+import com.android.server.wm.traces.common.ComponentNameMatcher
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test the [android.app.ActivityOptions.makeCustomTaskAnimation].
+ *
+ * To run this test: `atest FlickerTests:OverrideTaskTransitionTest`
+ *
+ * Actions:
+ * Launches SimpleActivity with alpha_2000ms animation
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+@Group4
+class OverrideTaskTransitionTest(val testSpec: FlickerTestParameter) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val testApp: StandardAppHelper = SimpleAppHelper(instrumentation)
+
+ @FlickerBuilderProvider
+ fun buildFlicker(): FlickerBuilder {
+ return FlickerBuilder(instrumentation).apply {
+ setup {
+ device.wakeUpAndGoToHomeScreen()
+ RemoveAllTasksButHomeRule.removeAllTasksButHome()
+ setRotation(testSpec.startRotation)
+ }
+ transitions {
+ instrumentation.context.startActivity(
+ testApp.openAppIntent, createCustomTaskAnimation())
+ wmHelper.StateSyncBuilder()
+ .add(WindowManagerConditionsFactory.isWMStateComplete())
+ .withAppTransitionIdle()
+ .withWindowSurfaceAppeared(testApp)
+ .waitForAndVerify()
+ }
+ teardown {
+ testApp.exit()
+ }
+ }
+ }
+
+ @Presubmit
+ @Test
+ fun testSimpleActivityIsShownDirectly() {
+ Assume.assumeFalse(isShellTransitionsEnabled)
+ testSpec.assertLayers {
+ isVisible(ComponentNameMatcher.LAUNCHER)
+ .isInvisible(ComponentNameMatcher.SPLASH_SCREEN)
+ .isInvisible(testApp)
+ .then()
+ // The custom animation should block the entire launcher from the very beginning
+ .isInvisible(ComponentNameMatcher.LAUNCHER)
+ }
+ }
+
+ private fun createCustomTaskAnimation(): Bundle {
+ return android.app.ActivityOptions.makeCustomTaskAnimation(instrumentation.context,
+ R.anim.show_2000ms, 0, Handler.getMain(), null, null).toBundle()
+ }
+
+ companion object {
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTestParameter> {
+ return FlickerTestParameterFactory.getInstance()
+ .getConfigNonRotationTests()
+ }
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
index 26f46cd073f1..63b78b687ae2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/TaskTransitionTest.kt
@@ -28,8 +28,7 @@ import com.android.server.wm.flicker.annotation.Group4
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.NewTasksAppHelper
import com.android.server.wm.flicker.helpers.WindowUtils
-import com.android.server.wm.flicker.testapp.ActivityOptions.LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME
-import com.android.server.wm.flicker.testapp.ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME
+import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.traces.common.ComponentNameMatcher
import com.android.server.wm.traces.common.ComponentNameMatcher.Companion.SPLASH_SCREEN
import com.android.server.wm.traces.common.ComponentNameMatcher.Companion.WALLPAPER_BBQ_WRAPPER
@@ -105,7 +104,7 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
}
/**
- * Check that the [ComponentMatcher.LAUNCHER] window is never visible when performing task
+ * Check that the [ComponentNameMatcher.LAUNCHER] window is never visible when performing task
* transitions.
* A solid color background should be shown above it.
*/
@@ -118,7 +117,7 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
}
/**
- * Checks that the [ComponentMatcher.LAUNCHER] layer is never visible when performing task
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer is never visible when performing task
* transitions.
* A solid color background should be shown above it.
*/
@@ -246,8 +245,8 @@ class TaskTransitionTest(testSpec: FlickerTestParameter) : BaseTest(testSpec) {
companion object {
private val LAUNCH_NEW_TASK_ACTIVITY =
- LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME.toFlickerComponent()
- private val SIMPLE_ACTIVITY = SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
+ ActivityOptions.LaunchNewActivity.COMPONENT.toFlickerComponent()
+ private val SIMPLE_ACTIVITY = ActivityOptions.SimpleActivity.COMPONENT.toFlickerComponent()
private fun getWallpaperPackage(instrumentation: Instrumentation): IComponentMatcher? {
val wallpaperManager = WallpaperManager.getInstance(instrumentation.targetContext)
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 16ad6309af40..9d27079c6a86 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
@@ -90,7 +90,7 @@ open class SeamlessAppRotationTest(
testApp.launchViaIntent(
wmHelper,
stringExtras = mapOf(
- ActivityOptions.EXTRA_STARVE_UI_THREAD
+ ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD
to testSpec.starveUiThread.toString()
)
)
@@ -164,20 +164,23 @@ open class SeamlessAppRotationTest(
/** {@inheritDoc} */
@Test
@Ignore("Not applicable to this CUJ. App is full screen")
- override fun statusBarLayerPositionAtStartAndEnd() { }
+ override fun statusBarLayerPositionAtStartAndEnd() {
+ }
/** {@inheritDoc} */
@Test
@Ignore("Not applicable to this CUJ. App is full screen")
- override fun statusBarLayerIsVisibleAtStartAndEnd() { }
+ override fun statusBarLayerIsVisibleAtStartAndEnd() {
+ }
/** {@inheritDoc} */
@Test
@Ignore("Not applicable to this CUJ. App is full screen")
- override fun statusBarWindowIsAlwaysVisible() { }
+ override fun statusBarWindowIsAlwaysVisible() {
+ }
/**
- * Checks that the [ComponentMatcher.STATUS_BAR] window is invisible during the whole
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] window is invisible during the whole
* transition
*/
@Presubmit
@@ -189,7 +192,7 @@ open class SeamlessAppRotationTest(
}
/**
- * Checks that the [ComponentMatcher.STATUS_BAR] layer is invisible during the whole
+ * Checks that the [ComponentNameMatcher.STATUS_BAR] layer is invisible during the whole
* transition
*/
@Presubmit
@@ -218,14 +221,17 @@ open class SeamlessAppRotationTest(
companion object {
private val FlickerTestParameter.starveUiThread
- get() = config.getOrDefault(ActivityOptions.EXTRA_STARVE_UI_THREAD, false) as Boolean
+ get() = config.getOrDefault(
+ ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD, false) as Boolean
private fun createConfig(
sourceConfig: FlickerTestParameter,
starveUiThread: Boolean
): FlickerTestParameter {
val newConfig = sourceConfig.config.toMutableMap()
- .also { it[ActivityOptions.EXTRA_STARVE_UI_THREAD] = starveUiThread }
+ .also {
+ it[ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD] = starveUiThread
+ }
val nameExt = if (starveUiThread) "_BUSY_UI_THREAD" else ""
return FlickerTestParameter(newConfig, nameOverride = "$sourceConfig$nameExt")
}
@@ -233,8 +239,8 @@ open class SeamlessAppRotationTest(
/**
* Creates the test configurations for seamless rotation based on the default rotation
* tests from [FlickerTestParameterFactory.getConfigRotationTests], but adding an
- * additional flag ([ActivityOptions.EXTRA_STARVE_UI_THREAD]) to indicate if the app
- * should starve the UI thread of not
+ * additional flag ([ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD]) to indicate
+ * if the app should starve the UI thread of not
*/
@JvmStatic
private fun getConfigurations(): List<FlickerTestParameter> {
diff --git a/tests/FlickerTests/test-apps/Android.bp b/tests/FlickerTests/test-apps/Android.bp
deleted file mode 100644
index e69de29bb2d1..000000000000
--- a/tests/FlickerTests/test-apps/Android.bp
+++ /dev/null
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 54662ef2d0b5..6e935d11411e 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -15,41 +15,41 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.server.wm.flicker.testapp">
+ package="com.android.server.wm.flicker.testapp">
<uses-sdk android:minSdkVersion="29"
- android:targetSdkVersion="29"/>
+ android:targetSdkVersion="29"/>
<application android:allowBackup="false"
- android:supportsRtl="true">
+ android:supportsRtl="true">
<uses-library android:name="androidx.window.extensions" android:required="false"/>
<activity android:name=".SimpleActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="SimpleApp"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SimpleActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="SimpleActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ImeActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
- android:theme="@style/CutoutShortEdges"
- android:label="ImeApp"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="ImeActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ImeActivityAutoFocus"
- android:theme="@style/CutoutShortEdges"
- android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
- android:windowSoftInputMode="stateVisible"
- android:configChanges="orientation|screenSize"
- android:label="ImeAppAutoFocus"
- android:exported="true">
+ android:theme="@style/CutoutShortEdges"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ImeActivityAutoFocus"
+ android:windowSoftInputMode="stateVisible"
+ android:configChanges="orientation|screenSize"
+ android:label="ImeAppAutoFocus"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -66,34 +66,34 @@
</intent-filter>
</activity>
<activity android:name=".SeamlessRotationActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
- android:theme="@style/CutoutShortEdges"
- android:configChanges="orientation|screenSize"
- android:label="SeamlessApp"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SeamlessRotationActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize"
+ android:label="SeamlessActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".NonResizeableActivity"
- android:theme="@style/CutoutShortEdges"
- android:resizeableActivity="false"
- android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
- android:label="NonResizeableApp"
- android:exported="true"
- android:showOnLockScreen="true">
+ android:theme="@style/CutoutShortEdges"
+ android:resizeableActivity="false"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.NonResizeableActivity"
+ android:label="NonResizeableActivity"
+ 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:theme="@style/CutoutShortEdges"
- android:configChanges="orientation|screenSize"
- android:label="ButtonActivity"
- android:exported="true">
+ <activity android:name=".LaunchNewActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.LaunchNewActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize"
+ android:label="LaunchNewActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -111,55 +111,56 @@
</intent-filter>
</activity>
<activity android:name=".DialogThemedActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.DialogThemedActivity"
- android:configChanges="orientation|screenSize"
- android:theme="@style/DialogTheme"
- android:label="DialogThemedActivity"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.DialogThemedActivity"
+ android:configChanges="orientation|screenSize"
+ android:theme="@style/DialogTheme"
+ android:label="DialogThemedActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".PortraitOnlyActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitOnlyActivity"
- android:theme="@style/CutoutShortEdges"
- android:screenOrientation="portrait"
- android:configChanges="orientation|screenSize"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.PortraitOnlyActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:screenOrientation="portrait"
+ android:configChanges="orientation|screenSize"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ImeEditorPopupDialogActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity"
- android:configChanges="orientation|screenSize"
- android:theme="@style/CutoutShortEdges"
- android:label="ImeEditorPopupDialogActivity"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ImeEditorPopupDialogActivity"
+ android:configChanges="orientation|screenSize"
+ android:theme="@style/CutoutShortEdges"
+ android:label="ImeEditorPopupDialogActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".ShowWhenLockedActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.ShowWhenLockedActivity"
- android:theme="@style/CutoutShortEdges"
- android:configChanges="orientation|screenSize"
- android:label="ShowWhenLockedActivity"
- android:showWhenLocked="true"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ShowWhenLockedActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize"
+ android:label="ShowWhenLockedActivity"
+ android:showWhenLocked="true"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".NotificationActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.NotificationActivity"
- android:theme="@style/CutoutShortEdges"
- android:configChanges="orientation|screenSize"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.NotificationActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize"
+ android:label="NotificationActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
@@ -173,8 +174,8 @@
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="true">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
@@ -193,26 +194,123 @@
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false"/>
<activity android:name=".MailActivity"
- android:exported="true"
- android:label="MailApp"
- android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity"
- android:theme="@style/Theme.AppCompat.Light">
+ android:exported="true"
+ android:label="MailActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.MailActivity"
+ android:theme="@style/Theme.AppCompat.Light">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name=".GameActivity"
- android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
- android:immersive="true"
- android:theme="@android:style/Theme.NoTitleBar"
- android:configChanges="screenSize"
- android:label="GameApp"
- android:exported="true">
+ android:taskAffinity="com.android.server.wm.flicker.testapp.GameActivity"
+ android:immersive="true"
+ android:theme="@android:style/Theme.NoTitleBar"
+ android:configChanges="screenSize"
+ android:label="GameActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".PipActivity"
+ android:resizeableActivity="true"
+ android:supportsPictureInPicture="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.PipActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:launchMode="singleTop"
+ android:label="PipActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".SplitScreenActivity"
+ android:resizeableActivity="true"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="SplitScreenPrimaryActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity android:name=".SplitScreenSecondaryActivity"
+ android:resizeableActivity="true"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SplitScreenSecondaryActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="SplitScreenSecondaryActivity"
+ android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+ <activity android:name=".SendNotificationActivity"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.SendNotificationActivity"
+ android:theme="@style/CutoutShortEdges"
+ android:label="SendNotificationActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".LaunchBubbleActivity"
+ android:label="LaunchBubbleActivity"
+ android:exported="true"
+ android:theme="@style/CutoutShortEdges"
+ android:launchMode="singleTop">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <action android:name="android.intent.action.VIEW"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+ <activity
+ android:name=".BubbleActivity"
+ android:label="BubbleActivity"
+ android:exported="false"
+ android:theme="@style/CutoutShortEdges"
+ android:resizeableActivity="true"/>
+ <service
+ android:name=".AssistantInteractionSessionService"
+ android:exported="true"
+ android:permission="android.permission.BIND_VOICE_INTERACTION"/>
+ <service
+ android:name=".AssistantRecognitionService"
+ android:exported="true"
+ android:label="Test Voice Interaction Service">
+ <intent-filter>
+ <action android:name="android.speech.RecognitionService"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ <meta-data
+ android:name="android.speech"
+ android:resource="@xml/recognition_service"/>
+ </service>
+ <service
+ android:name=".AssistantInteractionService"
+ android:exported="true"
+ android:label="Test Voice Interaction Service"
+ android:permission="android.permission.BIND_VOICE_INTERACTION">
+ <intent-filter>
+ <action android:name="android.service.voice.VoiceInteractionService"/>
+ </intent-filter>
+ <meta-data
+ android:name="android.voice_interaction"
+ android:resource="@xml/interaction_service"/>
+ </service>
</application>
+ <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
</manifest>
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/bg.png b/tests/FlickerTests/test-apps/flickerapp/res/drawable/bg.png
index d424a17b4157..d424a17b4157 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/bg.png
+++ b/tests/FlickerTests/test-apps/flickerapp/res/drawable/bg.png
Binary files differ
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_bubble.xml b/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_bubble.xml
index b43f31da748d..4ea156de9f40 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_bubble.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_bubble.xml
@@ -1,19 +1,19 @@
<?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.
--->
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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"
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_message.xml b/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_message.xml
index 0e8c7a0fe64a..45ed98c6e503 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/drawable/ic_message.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/drawable/ic_message.xml
@@ -1,19 +1,19 @@
<?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.
--->
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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"
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_bubble.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml
index f8b0ca3da26e..7c7b2cacaefb 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_bubble.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bubble.xml
@@ -1,19 +1,19 @@
<?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.
--->
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml
index fe7bced690f9..fe7bced690f9 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_button.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_launch_new.xml
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_main.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_main.xml
index f23c46455c63..553c7fe08096 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_main.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_main.xml
@@ -1,19 +1,19 @@
<?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.
--->
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
index 229098313afa..f7ba45b25d48 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_pip.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_pip.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- 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.
--->
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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"
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml
index 642a08b5bbe0..79e88e49b99e 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/res/layout/activity_splitscreen.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- 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.
--->
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
new file mode 100644
index 000000000000..ed9feaf1eade
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_splitscreen_secondary.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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_blue_light">
+
+ <TextView
+ android:id="@+id/SplitScreenTest"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="center_vertical|center_horizontal"
+ android:text="SecondaryActivity"
+ android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/assistant_session.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/assistant_session.xml
new file mode 100644
index 000000000000..eb7f3074ebfb
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/assistant_session.xml
@@ -0,0 +1,26 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <FrameLayout
+ android:id="@+id/vis_frame"
+ android:layout_width="match_parent"
+ android:layout_height="300dp"
+ android:layout_gravity="bottom"
+ android:background="#37474F"/>
+</FrameLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/xml/interaction_service.xml b/tests/FlickerTests/test-apps/flickerapp/res/xml/interaction_service.xml
new file mode 100644
index 000000000000..2e661fbd3122
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/xml/interaction_service.xml
@@ -0,0 +1,20 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:recognitionService="com.android.server.wm.flicker.testapp.AssistantRecognitionService"
+ android:sessionService="com.android.server.wm.flicker.testapp.AssistantInteractionSessionService"
+ android:supportsAssist="true" />
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/xml/recognition_service.xml b/tests/FlickerTests/test-apps/flickerapp/res/xml/recognition_service.xml
new file mode 100644
index 000000000000..2e124982084f
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/xml/recognition_service.xml
@@ -0,0 +1,17 @@
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<recognition-service xmlns:android="http://schemas.android.com/apk/res/android" />
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java
index 166e3ca2a156..04a590daf32f 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingMainActivity.java
@@ -16,9 +16,6 @@
package com.android.server.wm.flicker.testapp;
-import static com.android.server.wm.flicker.testapp.ActivityOptions.ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME;
-import static com.android.server.wm.flicker.testapp.ActivityOptions.ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME;
-
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
@@ -39,8 +36,6 @@ public class ActivityEmbeddingMainActivity extends Activity {
private static final String TAG = "ActivityEmbeddingMainActivity";
private static final float DEFAULT_SPLIT_RATIO = 0.5f;
- private ActivityEmbeddingComponent mEmbeddingComponent;
-
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -51,20 +46,24 @@ public class ActivityEmbeddingMainActivity extends Activity {
/** R.id.launch_placeholder_split_button onClick */
public void launchPlaceholderSplit(View view) {
- startActivity(new Intent().setComponent(
- ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME));
+ startActivity(
+ new Intent().setComponent(
+ ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT
+ )
+ );
}
private void initializeSplitRules() {
- mEmbeddingComponent = ActivityEmbeddingAppHelper.getActivityEmbeddingComponent();
- if (mEmbeddingComponent == null) {
+ ActivityEmbeddingComponent embeddingComponent =
+ ActivityEmbeddingAppHelper.getActivityEmbeddingComponent();
+ if (embeddingComponent == null) {
// Embedding not supported
Log.d(TAG, "ActivityEmbedding is not supported on this device");
finish();
return;
}
- mEmbeddingComponent.setEmbeddingRules(getSplitRules());
+ embeddingComponent.setEmbeddingRules(getSplitRules());
}
private Set<EmbeddingRule> getSplitRules() {
@@ -72,10 +71,10 @@ public class ActivityEmbeddingMainActivity extends Activity {
final SplitPlaceholderRule placeholderRule = new SplitPlaceholderRule.Builder(
new Intent().setComponent(
- ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME),
+ ActivityOptions.ActivityEmbedding.PlaceholderSecondaryActivity.COMPONENT),
activity -> activity instanceof ActivityEmbeddingPlaceholderPrimaryActivity,
intent -> intent.getComponent().equals(
- ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME),
+ ActivityOptions.ActivityEmbedding.PlaceholderPrimaryActivity.COMPONENT),
windowMetrics -> true)
.setSplitRatio(DEFAULT_SPLIT_RATIO)
.build();
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 72a02f24629f..dcd85503ee14 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
@@ -19,90 +19,175 @@ package com.android.server.wm.flicker.testapp;
import android.content.ComponentName;
public class ActivityOptions {
- public static final String EXTRA_STARVE_UI_THREAD = "StarveUiThread";
public static final String FLICKER_APP_PACKAGE = "com.android.server.wm.flicker.testapp";
- public static final String SEAMLESS_ACTIVITY_LAUNCHER_NAME = "SeamlessApp";
- public static final ComponentName SEAMLESS_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".SeamlessRotationActivity");
+ public static class SimpleActivity {
+ public static final String LABEL = "SimpleActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".SimpleActivity");
+ }
- public static final String IME_ACTIVITY_AUTO_FOCUS_LAUNCHER_NAME = "ImeAppAutoFocus";
- public static final ComponentName IME_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".ImeActivityAutoFocus");
+ public static class SeamlessRotation {
+ public static final String LABEL = "SeamlessRotationActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".SeamlessRotationActivity");
+
+ public static final String EXTRA_STARVE_UI_THREAD = "StarveUiThread";
+ }
- public static final String IME_ACTIVITY_LAUNCHER_NAME = "ImeActivity";
- public static final ComponentName IME_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
+ public static class Ime {
+ public static class Default {
+ public static final String LABEL = "ImeActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeActivity");
+ }
+
+ public static class AutoFocusActivity {
+ public static final String LABEL = "ImeAppAutoFocus";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ImeActivityAutoFocus");
+ }
- public static final String IME_ACTIVITY_INITIALIZE_LAUNCHER_NAME = "ImeStateInitializeActivity";
- public static final ComponentName IME_ACTIVITY_INITIALIZE_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
+ public static class StateInitializeActivity {
+ public static final String LABEL = "ImeStateInitializeActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeStateInitializeActivity");
+ }
- public static final String SIMPLE_ACTIVITY_LAUNCHER_NAME = "SimpleApp";
- 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");
-
- public static final String LAUNCH_NEW_TASK_ACTIVITY_LAUNCHER_NAME = "LaunchNewTaskApp";
- public static final ComponentName LAUNCH_NEW_TASK_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
-
- public static final String DIALOG_THEMED_ACTIVITY = "DialogThemedActivity";
- public static final ComponentName DIALOG_THEMED_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".DialogThemedActivity");
-
- public static final String PORTRAIT_ONLY_ACTIVITY_LAUNCHER_NAME = "PortraitOnlyActivity";
- public static final ComponentName PORTRAIT_ONLY_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".PortraitOnlyActivity");
-
- public static final String EDITOR_POPUP_DIALOG_ACTIVITY_LAUNCHER_NAME =
- "ImeEditorPopupDialogActivity";
- public static final ComponentName EDITOR_POPUP_DIALOG_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
+ public static class EditorPopupDialogActivity {
+ public static final String LABEL = "ImeEditorPopupDialogActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ImeEditorPopupDialogActivity");
-
- public static final String SHOW_WHEN_LOCKED_ACTIVITY_LAUNCHER_NAME = "ShowWhenLockedApp";
- public static final ComponentName SHOW_WHEN_LOCKED_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".ShowWhenLockedActivity");
-
- public static final String NOTIFICATION_ACTIVITY_LAUNCHER_NAME = "NotificationApp";
- public static final ComponentName NOTIFICATION_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".NotificationActivity");
-
- public static final String ACTIVITY_EMBEDDING_LAUNCHER_NAME = "ActivityEmbeddingMainActivity";
- public static final ComponentName ACTIVITY_EMBEDDING_MAIN_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
+ }
+ }
+
+ public static class NonResizeableActivity {
+ public static final String LABEL = "NonResizeableActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".NonResizeableActivity");
+ }
+
+ public static class DialogThemedActivity {
+ public static final String LABEL = "DialogThemedActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".DialogThemedActivity");
+ }
+
+ public static class PortraitOnlyActivity {
+ public static final String LABEL = "PortraitOnlyActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".PortraitOnlyActivity");
+ public static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
+ }
+
+ public static class ActivityEmbedding {
+ public static class MainActivity {
+ public static final String LABEL = "ActivityEmbeddingMainActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
FLICKER_APP_PACKAGE + ".ActivityEmbeddingMainActivity");
- public static final ComponentName
- ACTIVITY_EMBEDDING_PLACEHOLDER_PRIMARY_ACTIVITY_COMPONENT_NAME = new ComponentName(
- FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderPrimaryActivity");
- public static final ComponentName
- ACTIVITY_EMBEDDING_PLACEHOLDER_SECONDARY_ACTIVITY_COMPONENT_NAME = new ComponentName(
- FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity");
-
- public static final String GAME_ACTIVITY_LAUNCHER_NAME = "GameApp";
- public static final ComponentName GAME_ACTIVITY_COMPONENT_NAME =
- new ComponentName(FLICKER_APP_PACKAGE,
- FLICKER_APP_PACKAGE + ".GameActivity");
+ }
+
+ public static class PlaceholderPrimaryActivity {
+ public static final String LABEL = "ActivityEmbeddingPlaceholderPrimaryActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderPrimaryActivity");
+ }
+
+ public static class PlaceholderSecondaryActivity {
+ public static final String LABEL = "ActivityEmbeddingPlaceholderSecondaryActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ActivityEmbeddingPlaceholderSecondaryActivity");
+ }
+ }
+
+ public static class Notification {
+ public static final String LABEL = "NotificationActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".NotificationActivity");
+ }
+
+ public static class Mail {
+ public static final String LABEL = "MailActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".MailActivity");
+ }
+
+ public static class ShowWhenLockedActivity {
+ public static final String LABEL = "ShowWhenLockedActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ShowWhenLockedActivity");
+ }
+
+ public static class LaunchNewTask {
+ public static final String LABEL = "LaunchNewTaskActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".LaunchNewTaskActivity");
+ }
+
+ public static class Game {
+ public static final String LABEL = "GameActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".GameActivity");
+ }
+
+ public static class LaunchNewActivity {
+ public static final String LABEL = "LaunchNewActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".LaunchNewActivity");
+ }
+
+ public static class Pip {
+ // Test App > Pip Activity
+ public static final String LABEL = "PipActivity";
+ public static final String MENU_ACTION_NO_OP = "No-Op";
+ public static final String MENU_ACTION_ON = "On";
+ public static final String MENU_ACTION_OFF = "Off";
+ public static final String MENU_ACTION_CLEAR = "Clear";
+
+ // Intent action that this activity dynamically registers to enter picture-in-picture
+ public static final String ACTION_ENTER_PIP =
+ FLICKER_APP_PACKAGE + ".PipActivity.ENTER_PIP";
+ // Intent action that this activity dynamically registers to set requested orientation.
+ // Will apply the oriention to the value set in the EXTRA_FIXED_ORIENTATION extra.
+ public static final String ACTION_SET_REQUESTED_ORIENTATION =
+ FLICKER_APP_PACKAGE + ".PipActivity.SET_REQUESTED_ORIENTATION";
+
+ // Calls enterPictureInPicture() on creation
+ public static final String EXTRA_ENTER_PIP = "enter_pip";
+ // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
+ public static final String EXTRA_PIP_ORIENTATION = "fixed_orientation";
+ // Adds a click listener to finish this activity when it is clicked
+ public static final String EXTRA_TAP_TO_FINISH = "tap_to_finish";
+
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".PipActivity");
+ }
+
+ public static class SplitScreen {
+ public static class Primary {
+ public static final String LABEL = "SplitScreenPrimaryActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".SplitScreenActivity");
+ }
+
+ public static class Secondary {
+ public static final String LABEL = "SplitScreenSecondaryActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".SplitScreenSecondaryActivity");
+ }
+ }
+
+ public static class Bubbles {
+ public static class LaunchBubble {
+ public static final String LABEL = "LaunchBubbleActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".LaunchBubbleActivity");
+ }
+
+ public static class BubbleActivity {
+ public static final String LABEL = "BubbleActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".BubbleActivity");
+ }
+ }
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionService.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionService.java
new file mode 100644
index 000000000000..d60143cdf40a
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionService.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.service.voice.VoiceInteractionService;
+
+public class AssistantInteractionService extends VoiceInteractionService {
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSession.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSession.java
new file mode 100644
index 000000000000..d2c9b37704b8
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSession.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.content.Context;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.view.View;
+import android.view.Window;
+
+public class AssistantInteractionSession extends VoiceInteractionSession {
+
+ public AssistantInteractionSession(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onCreate() {
+ Window window = getWindow().getWindow();
+ if (window != null) {
+ window.getDecorView().setBackgroundColor(Color.TRANSPARENT);
+
+ }
+ View rootView = getLayoutInflater().inflate(R.layout.assistant_session, null);
+ setContentView(rootView);
+ setUiEnabled(false);
+ }
+
+ @Override
+ public void onShow(Bundle args, int showFlags) {
+ setUiEnabled(true);
+ }
+
+ @Override
+ public void onHide() {
+ setUiEnabled(false);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSessionService.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSessionService.java
new file mode 100644
index 000000000000..4d6125c9a5d8
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantInteractionSessionService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.service.voice.VoiceInteractionSessionService;
+
+public class AssistantInteractionSessionService extends VoiceInteractionSessionService {
+ @Override
+ public VoiceInteractionSession onNewSession(Bundle args) {
+ return new AssistantInteractionSession(this);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantRecognitionService.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantRecognitionService.java
new file mode 100644
index 000000000000..68aae4520fe9
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/AssistantRecognitionService.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.content.Intent;
+import android.speech.RecognitionService;
+
+public class AssistantRecognitionService extends RecognitionService {
+ @Override
+ protected void onStartListening(Intent recognizerIntent, Callback listener) {
+
+ }
+
+ @Override
+ protected void onCancel(Callback listener) {
+
+ }
+
+ @Override
+ protected void onStopListening(Callback listener) {
+
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleActivity.java
index bc3bc75ab903..58d7e670e908 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.testapp;
+package com.android.server.wm.flicker.testapp;
import android.app.Activity;
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
index 6cd93eff2803..c92b82b896f2 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/BubbleHelper.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BubbleHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.testapp;
+package com.android.server.wm.flicker.testapp;
import android.app.Notification;
@@ -86,7 +86,7 @@ public class BubbleHelper {
}
- private int getNextNotifyId() {
+ private int getNextNotifyId() {
int id = mNextNotifyId;
mNextNotifyId++;
return id;
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/FixedActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/FixedActivity.java
index d4ae6c1313bf..722929fb81e5 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/FixedActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/FixedActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open 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,9 +14,9 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.testapp;
+package com.android.server.wm.flicker.testapp;
-import static com.android.wm.shell.flicker.testapp.Components.FixedActivity.EXTRA_FIXED_ORIENTATION;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.PortraitOnlyActivity.EXTRA_FIXED_ORIENTATION;
import android.os.Bundle;
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/LaunchBubbleActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
index 71fa66d8a61c..dea34442464d 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/LaunchBubbleActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/LaunchBubbleActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.testapp;
+package com.android.server.wm.flicker.testapp;
import android.app.Activity;
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/LaunchNewActivity.java
index b42ac2a6fd97..e5710c850f07 100644
--- 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/LaunchNewActivity.java
@@ -22,7 +22,7 @@ import android.os.Bundle;
import android.view.WindowManager;
import android.widget.Button;
-public class ButtonActivity extends Activity {
+public class LaunchNewActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -30,11 +30,11 @@ public class ButtonActivity extends Activity {
p.layoutInDisplayCutoutMode = WindowManager.LayoutParams
.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
getWindow().setAttributes(p);
- setContentView(R.layout.activity_button);
+ setContentView(R.layout.activity_launch_new);
Button button = findViewById(R.id.launch_second_activity);
button.setOnClickListener(v -> {
- Intent intent = new Intent(ButtonActivity.this, SimpleActivity.class);
+ Intent intent = new Intent(LaunchNewActivity.this, SimpleActivity.class);
startActivity(intent);
});
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
index b31af385d363..a4dd5753539d 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/NotificationActivity.java
@@ -50,12 +50,12 @@ public class NotificationActivity extends Activity {
Intent resultIntent = new Intent(this, NotificationActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addNextIntentWithParentStack(resultIntent);
- PendingIntent resultPendingIntent =
- stackBuilder.getPendingIntent(0,
- PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
-
+ PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0,
+ new Intent(this, NotificationActivity.class),
+ PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
Notification.Builder builder = new Notification.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.ic_notification)
+ .setWhen(System.currentTimeMillis())
.setContentTitle("Flicker Test Notification")
.setContentText("Flicker Test Notification")
// Set the intent that will fire when the user taps the notification
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index 615b1730579c..cdb1d42bd4f2 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.testapp;
+package com.android.server.wm.flicker.testapp;
import static android.media.MediaMetadata.METADATA_KEY_TITLE;
import static android.media.session.PlaybackState.ACTION_PAUSE;
@@ -24,10 +24,10 @@ import static android.media.session.PlaybackState.STATE_PAUSED;
import static android.media.session.PlaybackState.STATE_PLAYING;
import static android.media.session.PlaybackState.STATE_STOPPED;
-import static com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_ENTER_PIP;
-import static com.android.wm.shell.flicker.testapp.Components.PipActivity.ACTION_SET_REQUESTED_ORIENTATION;
-import static com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_ENTER_PIP;
-import static com.android.wm.shell.flicker.testapp.Components.PipActivity.EXTRA_PIP_ORIENTATION;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_ENTER_PIP;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.ACTION_SET_REQUESTED_ORIENTATION;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.EXTRA_ENTER_PIP;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.Pip.EXTRA_PIP_ORIENTATION;
import android.app.Activity;
import android.app.PendingIntent;
@@ -127,7 +127,6 @@ public class PipActivity extends Activity {
break;
default:
Log.w(TAG, "Unhandled action=" + intent.getAction());
- return;
}
}
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
index 5cf81cb90fbc..ce7a0059fa2d 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SeamlessRotationActivity.java
@@ -18,7 +18,7 @@ package com.android.server.wm.flicker.testapp;
import static android.os.SystemClock.sleep;
-import static com.android.server.wm.flicker.testapp.ActivityOptions.EXTRA_STARVE_UI_THREAD;
+import static com.android.server.wm.flicker.testapp.ActivityOptions.SeamlessRotation.EXTRA_STARVE_UI_THREAD;
import android.app.Activity;
import android.os.Bundle;
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java
index 9c82eea1e8b8..70196aee9ff1 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.testapp;
+package com.android.server.wm.flicker.testapp;
import android.app.Activity;
import android.os.Bundle;
diff --git a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenSecondaryActivity.java
index baa1e6fdd1e9..a8ce8ff8f589 100644
--- a/libs/WindowManager/Shell/tests/flicker/test-apps/flickerapp/src/com/android/wm/shell/flicker/testapp/SplitScreenSecondaryActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/SplitScreenSecondaryActivity.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.testapp;
+package com.android.server.wm.flicker.testapp;
import android.app.Activity;
import android.os.Bundle;
diff --git a/tests/HandwritingIme/OWNERS b/tests/HandwritingIme/OWNERS
new file mode 100644
index 000000000000..6bb4b17ed4eb
--- /dev/null
+++ b/tests/HandwritingIme/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 34867
+
+include /services/core/java/com/android/server/inputmethod/OWNERS
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
index 94b1f863f197..e94c79ecca00 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
@@ -19,13 +19,11 @@ package com.google.android.test.handwritingime;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
-import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Path;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
-import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.WindowMetrics;
@@ -33,9 +31,8 @@ class InkView extends View {
private static final long FINISH_TIMEOUT = 1500;
private final HandwritingIme.HandwritingFinisher mHwCanceller;
private final HandwritingIme.StylusConsumer mConsumer;
- private final int mTopInset;
- private Paint mPaint;
- private Path mPath;
+ private final Paint mPaint;
+ private final Path mPath;
private float mX, mY;
private static final float STYLUS_MOVE_TOLERANCE = 1;
private Runnable mFinishRunnable;
@@ -59,12 +56,8 @@ class InkView extends View {
WindowManager wm = context.getSystemService(WindowManager.class);
WindowMetrics metrics = wm.getCurrentWindowMetrics();
- Insets insets = metrics.getWindowInsets()
- .getInsetsIgnoringVisibility(WindowInsets.Type.systemBars());
setLayoutParams(new ViewGroup.LayoutParams(
- metrics.getBounds().width() - insets.left - insets.right,
- metrics.getBounds().height() - insets.top - insets.bottom));
- mTopInset = insets.top;
+ metrics.getBounds().width(), metrics.getBounds().height()));
}
@Override
@@ -76,14 +69,12 @@ class InkView extends View {
}
private void stylusStart(float x, float y) {
- y = y - mTopInset;
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void stylusMove(float x, float y) {
- y = y - mTopInset;
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (mPath.isEmpty()) {
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index b0ccbd1cf22f..939c7de22356 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -436,6 +436,15 @@
</intent-filter>
</activity>
+ <activity android:name="SurfaceViewAlphaActivity"
+ android:label="SurfaceView/SurfaceView with Alpha"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="com.android.test.hwui.TEST"/>
+ </intent-filter>
+ </activity>
+
<activity android:name=".PenStylusActivity"
android:label="Pen/Draw"
android:exported="true">
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java
new file mode 100644
index 000000000000..01fe6ae0518b
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/SurfaceViewAlphaActivity.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.view.SurfaceHolder;
+import android.view.SurfaceHolder.Callback;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+public class SurfaceViewAlphaActivity extends Activity implements Callback {
+ SurfaceView mSurfaceView;
+
+ private enum ZOrder {
+ ABOVE,
+ BELOW
+ }
+
+ private float mAlpha = 127f / 255f;
+ private ZOrder mZOrder = ZOrder.BELOW;
+
+
+ private String getAlphaText() {
+ return "Alpha: " + mAlpha;
+ }
+
+ private void toggleZOrder() {
+ if (ZOrder.ABOVE.equals(mZOrder)) {
+ mZOrder = ZOrder.BELOW;
+ } else {
+ mZOrder = ZOrder.ABOVE;
+ }
+ }
+
+ // Overlaps a blue view on the left, then the SurfaceView in the center, then a blue view on the
+ // right.
+ private void overlapViews(SurfaceView view, LinearLayout parent) {
+ float density = getResources().getDisplayMetrics().density;
+ int surfaceViewSize = (int) (200 * density);
+ int blueViewSize = (int) (surfaceViewSize * 2 / 3f);
+ int totalSize = (int) (surfaceViewSize * 5 / 3f);
+
+ RelativeLayout overlapLayout = new RelativeLayout(this);
+
+ RelativeLayout.LayoutParams leftViewLayoutParams = new RelativeLayout.LayoutParams(
+ blueViewSize, surfaceViewSize);
+ leftViewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
+
+ View leftBlueView = new View(this);
+ leftBlueView.setBackgroundColor(Color.BLUE);
+ overlapLayout.addView(leftBlueView, leftViewLayoutParams);
+
+ RelativeLayout.LayoutParams sVLayoutParams = new RelativeLayout.LayoutParams(
+ surfaceViewSize, surfaceViewSize);
+ sVLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
+ overlapLayout.addView(view, sVLayoutParams);
+
+ RelativeLayout.LayoutParams rightViewLayoutParams = new RelativeLayout.LayoutParams(
+ blueViewSize, surfaceViewSize);
+ rightViewLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
+
+ View rightBlueView = new View(this);
+ rightBlueView.setBackgroundColor(Color.BLUE);
+ overlapLayout.addView(rightBlueView, rightViewLayoutParams);
+
+ parent.addView(overlapLayout, new LinearLayout.LayoutParams(
+ totalSize, surfaceViewSize));
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.getHolder().addCallback(this);
+ mSurfaceView.setAlpha(mAlpha);
+
+ LinearLayout content = new LinearLayout(this);
+ content.setOrientation(LinearLayout.VERTICAL);
+
+ TextView alphaText = new TextView(this);
+ alphaText.setText(getAlphaText());
+
+ SeekBar alphaToggle = new SeekBar(this);
+ alphaToggle.setMin(0);
+ alphaToggle.setMax(255);
+ alphaToggle.setProgress(Math.round(mAlpha * 255));
+ alphaToggle.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ mAlpha = progress / 255f;
+ alphaText.setText(getAlphaText());
+ mSurfaceView.setAlpha(mAlpha);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+
+ }
+ });
+
+ content.addView(alphaText, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ content.addView(alphaToggle, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ Button button = new Button(this);
+ button.setText("Z " + mZOrder.toString());
+ button.setOnClickListener(v -> {
+ toggleZOrder();
+ mSurfaceView.setZOrderOnTop(ZOrder.ABOVE.equals(mZOrder));
+ button.setText("Z " + mZOrder.toString());
+ });
+
+ content.addView(button, new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT));
+
+ overlapViews(mSurfaceView, content);
+
+ setContentView(content);
+ }
+
+ @Override
+ public void surfaceCreated(SurfaceHolder holder) {
+ }
+
+ @Override
+ public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+ Canvas canvas = holder.lockCanvas();
+ canvas.drawColor(Color.RED);
+ holder.unlockCanvasAndPost(canvas);
+ }
+
+ @Override
+ public void surfaceDestroyed(SurfaceHolder holder) {
+ }
+}
diff --git a/tests/TouchLatency/OWNERS b/tests/TouchLatency/OWNERS
new file mode 100644
index 000000000000..2b7de2555587
--- /dev/null
+++ b/tests/TouchLatency/OWNERS
@@ -0,0 +1,2 @@
+include platform/frameworks/base:/graphics/java/android/graphics/OWNERS
+include platform/frameworks/native:/services/surfaceflinger/OWNERS \ No newline at end of file
diff --git a/tests/TouchLatency/app/build.gradle b/tests/TouchLatency/app/build.gradle
index 04a878896f47..f5ae6f4b4ffc 100644
--- a/tests/TouchLatency/app/build.gradle
+++ b/tests/TouchLatency/app/build.gradle
@@ -1,13 +1,13 @@
apply plugin: 'com.android.application'
android {
- compileSdkVersion 28
+ compileSdkVersion 33
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "com.prefabulated.touchlatency"
minSdkVersion 28
- targetSdkVersion 28
+ targetSdkVersion 33
versionCode 1
versionName "1.0"
}
diff --git a/tests/TouchLatency/app/src/main/AndroidManifest.xml b/tests/TouchLatency/app/src/main/AndroidManifest.xml
index 98947367bd7b..25bb5d92f846 100644
--- a/tests/TouchLatency/app/src/main/AndroidManifest.xml
+++ b/tests/TouchLatency/app/src/main/AndroidManifest.xml
@@ -20,16 +20,22 @@
<application android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
- android:theme="@style/AppTheme">
+ android:theme="@style/AppTheme"
+ android:resizeableActivity="true" >
<activity android:name=".TouchLatencyActivity"
android:label="@string/app_name"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
-
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
+
+ <activity android:name=".TouchLatencyActivityPresentation"
+ android:label="@string/app_name"
+ android:parentActivityName=".TouchLatencyActivity"
+ android:exported="true">
+ </activity>
</application>
</manifest>
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
index a2842b6e214d..6ab3b3e6c037 100644
--- a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivity.java
@@ -17,218 +17,40 @@
package com.prefabulated.touchlatency;
import android.app.Activity;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Paint.Align;
+import android.app.ActivityOptions;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Trace;
-import android.util.AttributeSet;
-import android.util.Log;
import android.view.Display;
import android.view.Display.Mode;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.MotionEvent;
-import android.view.View;
import android.view.Window;
import android.view.WindowManager;
-import java.math.RoundingMode;
-import java.text.DecimalFormat;
-
-class TouchLatencyView extends View implements View.OnTouchListener {
- private static final String LOG_TAG = "TouchLatency";
- private static final int BACKGROUND_COLOR = 0xFF400080;
- private static final int INNER_RADIUS = 70;
- private static final int BALL_DIAMETER = 200;
- private static final int SEC_TO_NANOS = 1000000000;
- private static final float FPS_UPDATE_THRESHOLD = 20;
- private static final long BALL_VELOCITY = 420;
-
- public TouchLatencyView(Context context, AttributeSet attrs) {
- super(context, attrs);
- Trace.beginSection("TouchLatencyView constructor");
- setOnTouchListener(this);
- setWillNotDraw(false);
- mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mBluePaint.setColor(0xFF0000FF);
- mBluePaint.setStyle(Paint.Style.FILL);
- mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mGreenPaint.setColor(0xFF00FF00);
- mGreenPaint.setStyle(Paint.Style.FILL);
- mYellowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mYellowPaint.setColor(0xFFFFFF00);
- mYellowPaint.setStyle(Paint.Style.FILL);
- mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mRedPaint.setColor(0xFFFF0000);
- mRedPaint.setStyle(Paint.Style.FILL);
- mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- mTextPaint.setColor(0xFFFFFFFF);
- mTextPaint.setTextSize(100);
- mTextPaint.setTextAlign(Align.RIGHT);
-
- mTouching = false;
-
- mLastDrawNano = 0;
- mFps = 0;
- mLastFpsUpdate = 0;
- mFrameCount = 0;
-
- mDf = new DecimalFormat("fps: #.##");
- mDf.setRoundingMode(RoundingMode.HALF_UP);
-
- Trace.endSection();
- }
-
- @Override
- public boolean onTouch(View view, MotionEvent event) {
- Trace.beginSection("TouchLatencyView onTouch");
- int action = event.getActionMasked();
- if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
- mTouching = true;
- invalidate();
-
- mTouchX = event.getX();
- mTouchY = event.getY();
- } else if (action == MotionEvent.ACTION_UP) {
- mTouching = false;
- invalidate();
- }
- Trace.endSection();
- return true;
- }
-
- private void drawTouch(Canvas canvas) {
- Trace.beginSection("TouchLatencyView drawTouch");
-
- try {
- if (!mTouching) {
- Log.d(LOG_TAG, "Filling background");
- canvas.drawColor(BACKGROUND_COLOR);
- return;
- }
-
- float deltaX = (mTouchX - mLastDrawnX);
- float deltaY = (mTouchY - mLastDrawnY);
- float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f;
-
- mLastDrawnX = mTouchX;
- mLastDrawnY = mTouchY;
-
- canvas.drawColor(BACKGROUND_COLOR);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint);
- canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint);
- } finally {
- Trace.endSection();
- }
- }
-
- private Paint getBallColor() {
- if (mFps > 75)
- return mGreenPaint;
- else if (mFps > 45)
- return mYellowPaint;
- else
- return mRedPaint;
- }
-
- private void drawBall(Canvas canvas) {
- Trace.beginSection("TouchLatencyView drawBall");
- int width = canvas.getWidth();
- int height = canvas.getHeight();
- float fps = 0f;
-
- long t = System.nanoTime();
- long tDiff = t - mLastDrawNano;
- mLastDrawNano = t;
- mFrameCount++;
-
- if (tDiff < SEC_TO_NANOS) {
- fps = 1f * SEC_TO_NANOS / tDiff;
- }
-
- long fDiff = t - mLastFpsUpdate;
- if (Math.abs(mFps - fps) > FPS_UPDATE_THRESHOLD) {
- mFps = fps;
- mLastFpsUpdate = t;
- mFrameCount = 0;
- } else if (fDiff > SEC_TO_NANOS) {
- mFps = 1f * mFrameCount * SEC_TO_NANOS / fDiff;
- mLastFpsUpdate = t;
- mFrameCount = 0;
- }
-
- final long pos = t * BALL_VELOCITY / SEC_TO_NANOS;
- final long xMax = width - BALL_DIAMETER;
- final long yMax = height - BALL_DIAMETER;
- long xOffset = pos % xMax;
- long yOffset = pos % yMax;
-
- float left, right, top, bottom;
-
- if (((pos / xMax) & 1) == 0) {
- left = xMax - xOffset;
- } else {
- left = xOffset;
+public class TouchLatencyActivity extends Activity {
+ private Mode mDisplayModes[];
+ private int mCurrentModeIndex;
+ private DisplayManager mDisplayManager;
+ private final DisplayManager.DisplayListener mDisplayListener =
+ new DisplayManager.DisplayListener() {
+ @Override
+ public void onDisplayAdded(int i) {
+ invalidateOptionsMenu();
}
- right = left + BALL_DIAMETER;
- if (((pos / yMax) & 1) == 0) {
- top = yMax - yOffset;
- } else {
- top = yOffset;
+ @Override
+ public void onDisplayRemoved(int i) {
+ invalidateOptionsMenu();
}
- bottom = top + BALL_DIAMETER;
-
- // Draw the ball
- canvas.drawColor(BACKGROUND_COLOR);
- canvas.drawOval(left, top, right, bottom, getBallColor());
- canvas.drawText(mDf.format(mFps), width, 100, mTextPaint);
- invalidate();
- Trace.endSection();
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- Trace.beginSection("TouchLatencyView onDraw");
- if (mMode == 0) {
- drawTouch(canvas);
- } else {
- drawBall(canvas);
+ @Override
+ public void onDisplayChanged(int i) {
+ invalidateOptionsMenu();
}
- Trace.endSection();
- }
-
- public void changeMode(MenuItem item) {
- Trace.beginSection("TouchLatencyView changeMode");
- final int NUM_MODES = 2;
- final String modes[] = {"Touch", "Ball"};
- mMode = (mMode + 1) % NUM_MODES;
- invalidate();
- item.setTitle(modes[mMode]);
- Trace.endSection();
- }
-
- private final Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint, mTextPaint;
- private int mMode;
-
- private boolean mTouching;
- private float mTouchX, mTouchY;
- private float mLastDrawnX, mLastDrawnY;
-
- private long mLastDrawNano, mLastFpsUpdate, mFrameCount;
- private float mFps;
- private DecimalFormat mDf;
-}
-
-public class TouchLatencyActivity extends Activity {
- private Mode mDisplayModes[];
- private int mCurrentModeIndex;
+ };
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -236,9 +58,9 @@ public class TouchLatencyActivity extends Activity {
Trace.beginSection("TouchLatencyActivity onCreate");
setContentView(R.layout.activity_touch_latency);
-
mTouchView = findViewById(R.id.canvasView);
+ configureDisplayListener();
WindowManager wm = getWindowManager();
Display display = wm.getDefaultDisplay();
mDisplayModes = display.getSupportedModes();
@@ -250,11 +72,9 @@ public class TouchLatencyActivity extends Activity {
break;
}
}
-
Trace.endSection();
}
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
Trace.beginSection("TouchLatencyActivity onCreateOptionsMenu");
@@ -265,17 +85,26 @@ public class TouchLatencyActivity extends Activity {
Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
updateDisplayMode(menuItem, currentMode);
}
+ updateMultiDisplayMenu(menu.findItem(R.id.multi_display));
Trace.endSection();
return true;
}
-
private void updateDisplayMode(MenuItem menuItem, Mode displayMode) {
int fps = (int) displayMode.getRefreshRate();
menuItem.setTitle(fps + "hz");
menuItem.setVisible(true);
}
+ private void updateMultiDisplayMenu(MenuItem item) {
+ item.setVisible(mDisplayManager.getDisplays().length > 1);
+ }
+
+ private void configureDisplayListener() {
+ mDisplayManager = getSystemService(DisplayManager.class);
+ mDisplayManager.registerDisplayListener(mDisplayListener, new Handler());
+ }
+
public void changeDisplayMode(MenuItem item) {
Window w = getWindow();
WindowManager.LayoutParams params = w.getAttributes();
@@ -299,6 +128,19 @@ public class TouchLatencyActivity extends Activity {
mCurrentModeIndex = modeIndex;
}
+ private void changeMultipleDisplays() {
+ Intent intent = new Intent(this, TouchLatencyActivityPresentation.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT | Intent.FLAG_ACTIVITY_NEW_TASK);
+ ActivityOptions options = ActivityOptions.makeBasic();
+ for (int i = 1; i < mDisplayManager.getDisplays().length; ++i) {
+ // We assume the first display is already displaying the TouchLatencyActivity
+ int displayId = mDisplayManager.getDisplays()[i].getDisplayId();
+ options.setLaunchDisplayId(displayId);
+ intent.putExtra(TouchLatencyActivityPresentation.DISPLAY_ID, displayId);
+ startActivity(intent, options.toBundle());
+ }
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
@@ -309,15 +151,32 @@ public class TouchLatencyActivity extends Activity {
int id = item.getItemId();
//noinspection SimplifiableIfStatement
- if (id == R.id.action_settings) {
- mTouchView.changeMode(item);
- } else if (id == R.id.display_mode) {
- changeDisplayMode(item);
+ switch (id) {
+ case R.id.action_settings: {
+ mTouchView.changeMode(item);
+ break;
+ }
+ case R.id.display_mode: {
+ changeDisplayMode(item);
+ break;
+ }
+ case R.id.multi_display: {
+ changeMultipleDisplays();
+ break;
+ }
}
Trace.endSection();
return super.onOptionsItemSelected(item);
}
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mDisplayManager != null) {
+ mDisplayManager.unregisterDisplayListener(mDisplayListener);
+ }
+ }
+
private TouchLatencyView mTouchView;
}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivityPresentation.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivityPresentation.java
new file mode 100644
index 000000000000..2602e6b34395
--- /dev/null
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyActivityPresentation.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.prefabulated.touchlatency;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Trace;
+import android.view.Display;
+import android.view.Display.Mode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.Window;
+import android.view.WindowManager;
+
+public class TouchLatencyActivityPresentation extends Activity {
+ public static final String DISPLAY_ID = "DISPLAY_ID";
+ private Mode[] mDisplayModes;
+ private int mCurrentModeIndex;
+ private int mDisplayId;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getIntent().hasExtra(DISPLAY_ID)) {
+ mDisplayId = (int) getIntent().getExtras().get(DISPLAY_ID);
+ }
+ Trace.beginSection(
+ "TouchLatencyActivityPresentation::DisplayId::" + mDisplayId + " onCreate");
+ setContentView(R.layout.activity_touch_latency);
+
+ mTouchView = findViewById(R.id.canvasView);
+
+ WindowManager wm = getWindowManager();
+ Display display = wm.getDefaultDisplay();
+ mDisplayModes = display.getSupportedModes();
+ Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
+
+ for (int i = 0; i < mDisplayModes.length; i++) {
+ if (currentMode.getModeId() == mDisplayModes[i].getModeId()) {
+ mCurrentModeIndex = i;
+ break;
+ }
+ }
+ Trace.endSection();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ Trace.beginSection(
+ "TouchLatencyActivityPresentation::DisplayId:: "
+ + mDisplayId + " onCreateOptionsMenu");
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.menu_touch_latency, menu);
+ if (mDisplayModes.length > 1) {
+ MenuItem menuItem = menu.findItem(R.id.display_mode);
+ Mode currentMode = getWindowManager().getDefaultDisplay().getMode();
+ updateDisplayMode(menuItem, currentMode);
+ }
+ Trace.endSection();
+ return true;
+ }
+
+ private void updateDisplayMode(MenuItem menuItem, Mode displayMode) {
+ int fps = (int) displayMode.getRefreshRate();
+ menuItem.setTitle(fps + "hz");
+ menuItem.setVisible(true);
+ }
+
+ public void changeDisplayMode(MenuItem item) {
+ Window w = getWindow();
+ WindowManager.LayoutParams params = w.getAttributes();
+
+ int modeIndex = (mCurrentModeIndex + 1) % mDisplayModes.length;
+ params.preferredDisplayModeId = mDisplayModes[modeIndex].getModeId();
+ w.setAttributes(params);
+
+ updateDisplayMode(item, mDisplayModes[modeIndex]);
+ mCurrentModeIndex = modeIndex;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ Trace.beginSection(
+ "TouchLatencyActivityPresentation::DisplayId::"
+ + mDisplayId + " onOptionsItemSelected");
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+
+ //noinspection SimplifiableIfStatement
+ switch (id) {
+ case R.id.action_settings: {
+ mTouchView.changeMode(item);
+ break;
+ }
+ case R.id.display_mode: {
+ changeDisplayMode(item);
+ break;
+ }
+ }
+
+ Trace.endSection();
+ return super.onOptionsItemSelected(item);
+ }
+
+ private TouchLatencyView mTouchView;
+}
diff --git a/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java
new file mode 100644
index 000000000000..0803e8e8510f
--- /dev/null
+++ b/tests/TouchLatency/app/src/main/java/com/prefabulated/touchlatency/TouchLatencyView.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.prefabulated.touchlatency;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.os.Trace;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.math.RoundingMode;
+import java.text.DecimalFormat;
+
+class TouchLatencyView extends View implements View.OnTouchListener {
+ private static final String LOG_TAG = "TouchLatency";
+ private static final int BACKGROUND_COLOR = 0xFF400080;
+ private static final int INNER_RADIUS = 70;
+ private static final int BALL_DIAMETER = 200;
+ private static final int SEC_TO_NANOS = 1000000000;
+ private static final float FPS_UPDATE_THRESHOLD = 20;
+ private static final long BALL_VELOCITY = 420;
+
+ public TouchLatencyView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ Trace.beginSection("TouchLatencyView constructor");
+ setOnTouchListener(this);
+ setWillNotDraw(false);
+ mBluePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mBluePaint.setColor(0xFF0000FF);
+ mBluePaint.setStyle(Paint.Style.FILL);
+ mGreenPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mGreenPaint.setColor(0xFF00FF00);
+ mGreenPaint.setStyle(Paint.Style.FILL);
+ mYellowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mYellowPaint.setColor(0xFFFFFF00);
+ mYellowPaint.setStyle(Paint.Style.FILL);
+ mRedPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mRedPaint.setColor(0xFFFF0000);
+ mRedPaint.setStyle(Paint.Style.FILL);
+ mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mTextPaint.setColor(0xFFFFFFFF);
+ mTextPaint.setTextSize(100);
+ mTextPaint.setTextAlign(Paint.Align.RIGHT);
+
+ mTouching = false;
+
+ mLastDrawNano = 0;
+ mFps = 0;
+ mLastFpsUpdate = 0;
+ mFrameCount = 0;
+
+ mDf = new DecimalFormat("fps: #.##");
+ mDf.setRoundingMode(RoundingMode.HALF_UP);
+
+ Trace.endSection();
+ }
+
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ Trace.beginSection("TouchLatencyView onTouch");
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) {
+ mTouching = true;
+ invalidate();
+
+ mTouchX = event.getX();
+ mTouchY = event.getY();
+ } else if (action == MotionEvent.ACTION_UP) {
+ mTouching = false;
+ invalidate();
+ }
+ Trace.endSection();
+ return true;
+ }
+
+ private void drawTouch(Canvas canvas) {
+ Trace.beginSection("TouchLatencyView drawTouch");
+
+ try {
+ if (!mTouching) {
+ Log.d(LOG_TAG, "Filling background");
+ canvas.drawColor(BACKGROUND_COLOR);
+ return;
+ }
+
+ float deltaX = (mTouchX - mLastDrawnX);
+ float deltaY = (mTouchY - mLastDrawnY);
+ float scaleFactor = (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY) * 1.5f;
+
+ mLastDrawnX = mTouchX;
+ mLastDrawnY = mTouchY;
+
+ canvas.drawColor(BACKGROUND_COLOR);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 3 * scaleFactor, mRedPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + 2 * scaleFactor, mYellowPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS + scaleFactor, mGreenPaint);
+ canvas.drawCircle(mTouchX, mTouchY, INNER_RADIUS, mBluePaint);
+ } finally {
+ Trace.endSection();
+ }
+ }
+
+ private Paint getBallColor() {
+ if (mFps > 75) {
+ return mGreenPaint;
+ } else if (mFps > 45) {
+ return mYellowPaint;
+ } else
+ return mRedPaint;
+ }
+
+ private void drawBall(Canvas canvas) {
+ Trace.beginSection("TouchLatencyView drawBall");
+ int width = canvas.getWidth();
+ int height = canvas.getHeight();
+ float fps = 0f;
+
+ long t = System.nanoTime();
+ long tDiff = t - mLastDrawNano;
+ mLastDrawNano = t;
+ mFrameCount++;
+
+ if (tDiff < SEC_TO_NANOS) {
+ fps = 1f * SEC_TO_NANOS / tDiff;
+ }
+
+ long fDiff = t - mLastFpsUpdate;
+ if (Math.abs(mFps - fps) > FPS_UPDATE_THRESHOLD) {
+ mFps = fps;
+ mLastFpsUpdate = t;
+ mFrameCount = 0;
+ } else if (fDiff > SEC_TO_NANOS) {
+ mFps = 1f * mFrameCount * SEC_TO_NANOS / fDiff;
+ mLastFpsUpdate = t;
+ mFrameCount = 0;
+ }
+
+ final long pos = t * BALL_VELOCITY / SEC_TO_NANOS;
+ final long xMax = width - BALL_DIAMETER;
+ final long yMax = height - BALL_DIAMETER;
+ long xOffset = pos % xMax;
+ long yOffset = pos % yMax;
+
+ float left, right, top, bottom;
+
+ if (((pos / xMax) & 1) == 0) {
+ left = xMax - xOffset;
+ } else {
+ left = xOffset;
+ }
+ right = left + BALL_DIAMETER;
+
+ if (((pos / yMax) & 1) == 0) {
+ top = yMax - yOffset;
+ } else {
+ top = yOffset;
+ }
+ bottom = top + BALL_DIAMETER;
+
+ // Draw the ball
+ canvas.drawColor(BACKGROUND_COLOR);
+ canvas.drawOval(left, top, right, bottom, getBallColor());
+ canvas.drawText(mDf.format(mFps), width, 100, mTextPaint);
+
+ invalidate();
+ Trace.endSection();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ Trace.beginSection("TouchLatencyView onDraw");
+ if (mMode == 0) {
+ drawTouch(canvas);
+ } else {
+ drawBall(canvas);
+ }
+ Trace.endSection();
+ }
+
+ public void changeMode(MenuItem item) {
+ Trace.beginSection("TouchLatencyView changeMode");
+ final int NUM_MODES = 2;
+ final String modes[] = {"Touch", "Ball"};
+ mMode = (mMode + 1) % NUM_MODES;
+ invalidate();
+ item.setTitle(modes[mMode]);
+ Trace.endSection();
+ }
+
+ private final Paint mBluePaint, mGreenPaint, mYellowPaint, mRedPaint, mTextPaint;
+ private int mMode;
+
+ private boolean mTouching;
+ private float mTouchX, mTouchY;
+ private float mLastDrawnX, mLastDrawnY;
+
+ private long mLastDrawNano, mLastFpsUpdate, mFrameCount;
+ private float mFps;
+ private DecimalFormat mDf;
+}
diff --git a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
index 52be91900ae8..abc7fd5d6bb2 100644
--- a/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
+++ b/tests/TouchLatency/app/src/main/res/menu/menu_touch_latency.xml
@@ -25,4 +25,10 @@
android:showAsAction="ifRoom"
android:title="@string/display_mode"
android:visible="false"/>
+
+ <item
+ android:id="@+id/multi_display"
+ android:showAsAction="ifRoom"
+ android:title="@string/multi_display"
+ android:visible="false"/>
</menu>
diff --git a/tests/TouchLatency/app/src/main/res/values/strings.xml b/tests/TouchLatency/app/src/main/res/values/strings.xml
index 771992c8e5d3..5ee86d8bd8bf 100644
--- a/tests/TouchLatency/app/src/main/res/values/strings.xml
+++ b/tests/TouchLatency/app/src/main/res/values/strings.xml
@@ -18,4 +18,5 @@
<string name="mode">Touch</string>
<string name="display_mode">Mode</string>
+ <string name="multi_display">multi-display</string>
</resources>
diff --git a/tests/TouchLatency/build.gradle b/tests/TouchLatency/build.gradle
index 03abe82a4359..381e55e92c7d 100644
--- a/tests/TouchLatency/build.gradle
+++ b/tests/TouchLatency/build.gradle
@@ -6,7 +6,7 @@ buildscript {
google()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.2.1'
+ classpath 'com.android.tools.build:gradle:4.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
index 2d80b69a7665..4d9ca1649142 100644
--- a/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
+++ b/tests/TouchLatency/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.8.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java b/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java
index 388548691b77..2001c04bd645 100644
--- a/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java
+++ b/tests/benchmarks/internal/src/com/android/internal/LambdaPerfTest.java
@@ -1,454 +1,458 @@
-/*
- * 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;
-
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
-import android.app.Activity;
-import android.graphics.Rect;
-import android.os.Bundle;
-import android.os.Message;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.SystemClock;
-import android.util.Log;
-
-import androidx.test.filters.LargeTest;
-
-import com.android.internal.util.function.pooled.PooledConsumer;
-import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.internal.util.function.pooled.PooledPredicate;
-
-import org.junit.Assume;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runners.model.Statement;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/** Compares the performance of regular lambda and pooled lambda. */
-@LargeTest
-public class LambdaPerfTest {
- private static final boolean DEBUG = false;
- private static final String TAG = LambdaPerfTest.class.getSimpleName();
-
- private static final String LAMBDA_FORM_REGULAR = "regular";
- private static final String LAMBDA_FORM_POOLED = "pooled";
-
- private static final int WARMUP_ITERATIONS = 1000;
- private static final int TEST_ITERATIONS = 3000000;
- private static final int TASK_COUNT = 10;
- private static final long DELAY_AFTER_BENCH_MS = 1000;
-
- private String mMethodName;
-
- private final Bundle mTestResults = new Bundle();
- private final ArrayList<Task> mTasks = new ArrayList<>();
-
- // The member fields are used to ensure lambda capturing. They don't have the actual meaning.
- private final Task mTask = new Task();
- private final Rect mBounds = new Rect();
- private int mTaskId;
- private long mTime;
- private boolean mTop;
-
- @Rule
- public final TestRule mRule = (base, description) -> new Statement() {
- @Override
- public void evaluate() throws Throwable {
- mMethodName = description.getMethodName();
- mTasks.clear();
- for (int i = 0; i < TASK_COUNT; i++) {
- final Task t = new Task();
- mTasks.add(t);
- }
- base.evaluate();
-
- getInstrumentation().sendStatus(Activity.RESULT_OK, mTestResults);
- }
- };
-
- @Test
- public void test1ParamConsumer() {
- evaluate(LAMBDA_FORM_REGULAR, () -> forAllTask(t -> t.doSomething(mTask)));
- evaluate(LAMBDA_FORM_POOLED, () -> {
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
- PooledLambda.__(Task.class), mTask);
- forAllTask(c);
- c.recycle();
- });
- }
-
- @Test
- public void test2PrimitiveParamsConsumer() {
- // Not in Integer#IntegerCache (-128~127) for autoboxing, that will create new object.
- mTaskId = 12345;
- mTime = 54321;
-
- evaluate(LAMBDA_FORM_REGULAR, () -> forAllTask(t -> t.doSomething(mTaskId, mTime)));
- evaluate(LAMBDA_FORM_POOLED, () -> {
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
- PooledLambda.__(Task.class), mTaskId, mTime);
- forAllTask(c);
- c.recycle();
- });
- }
-
- @Test
- public void test3ParamsPredicate() {
- mTop = true;
- // In Integer#IntegerCache.
- mTaskId = 10;
-
- evaluate(LAMBDA_FORM_REGULAR, () -> handleTask(t -> t.doSomething(mBounds, mTop, mTaskId)));
- evaluate(LAMBDA_FORM_POOLED, () -> {
- final PooledPredicate c = PooledLambda.obtainPredicate(Task::doSomething,
- PooledLambda.__(Task.class), mBounds, mTop, mTaskId);
- handleTask(c);
- c.recycle();
- });
- }
-
- @Test
- public void testMessage() {
- evaluate(LAMBDA_FORM_REGULAR, () -> {
- final Message m = Message.obtain().setCallback(() -> mTask.doSomething(mTaskId, mTime));
- m.getCallback().run();
- m.recycle();
- });
- evaluate(LAMBDA_FORM_POOLED, () -> {
- final Message m = PooledLambda.obtainMessage(Task::doSomething, mTask, mTaskId, mTime);
- m.getCallback().run();
- m.recycle();
- });
- }
-
- @Test
- public void testRunnable() {
- evaluate(LAMBDA_FORM_REGULAR, () -> {
- final Runnable r = mTask::doSomething;
- r.run();
- });
- evaluate(LAMBDA_FORM_POOLED, () -> {
- final Runnable r = PooledLambda.obtainRunnable(Task::doSomething, mTask).recycleOnUse();
- r.run();
- });
- }
-
- @Test
- public void testMultiThread() {
- final int numThread = 3;
-
- final Runnable regularAction = () -> forAllTask(t -> t.doSomething(mTask));
- final Runnable[] regularActions = new Runnable[numThread];
- Arrays.fill(regularActions, regularAction);
- evaluateMultiThread(LAMBDA_FORM_REGULAR, regularActions);
-
- final Runnable pooledAction = () -> {
- final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
- PooledLambda.__(Task.class), mTask);
- forAllTask(c);
- c.recycle();
- };
- final Runnable[] pooledActions = new Runnable[numThread];
- Arrays.fill(pooledActions, pooledAction);
- evaluateMultiThread(LAMBDA_FORM_POOLED, pooledActions);
- }
-
- private void forAllTask(Consumer<Task> callback) {
- for (int i = mTasks.size() - 1; i >= 0; i--) {
- callback.accept(mTasks.get(i));
- }
- }
-
- private void handleTask(Predicate<Task> callback) {
- for (int i = mTasks.size() - 1; i >= 0; i--) {
- final Task task = mTasks.get(i);
- if (callback.test(task)) {
- return;
- }
- }
- }
-
- private void evaluate(String title, Runnable action) {
- for (int i = 0; i < WARMUP_ITERATIONS; i++) {
- action.run();
- }
- performGc();
-
- final GcStatus startGcStatus = getGcStatus();
- final long startTime = SystemClock.elapsedRealtime();
- for (int i = 0; i < TEST_ITERATIONS; i++) {
- action.run();
- }
- evaluateResult(title, startGcStatus, startTime);
- }
-
- private void evaluateMultiThread(String title, Runnable[] actions) {
- performGc();
-
- final CountDownLatch latch = new CountDownLatch(actions.length);
- final GcStatus startGcStatus = getGcStatus();
- final long startTime = SystemClock.elapsedRealtime();
- for (Runnable action : actions) {
- new Thread() {
- @Override
- public void run() {
- for (int i = 0; i < TEST_ITERATIONS; i++) {
- action.run();
- }
- latch.countDown();
- };
- }.start();
- }
- try {
- latch.await();
- } catch (InterruptedException ignored) {
- }
- evaluateResult(title, startGcStatus, startTime);
- }
-
- private void evaluateResult(String title, GcStatus startStatus, long startTime) {
- final float elapsed = SystemClock.elapsedRealtime() - startTime;
- // Sleep a while to see if GC may happen.
- SystemClock.sleep(DELAY_AFTER_BENCH_MS);
- final GcStatus endStatus = getGcStatus();
- final GcInfo info = startStatus.calculateGcTime(endStatus, title, mTestResults);
- Log.i(TAG, mMethodName + "_" + title + " execution time: "
- + elapsed + "ms (avg=" + String.format("%.5f", elapsed / TEST_ITERATIONS) + "ms)"
- + " GC time: " + String.format("%.3f", info.mTotalGcTime) + "ms"
- + " GC paused time: " + String.format("%.3f", info.mTotalGcPausedTime) + "ms");
- }
-
- /** Cleans the test environment. */
- private static void performGc() {
- System.gc();
- System.runFinalization();
- System.gc();
- }
-
- private static GcStatus getGcStatus() {
- if (DEBUG) {
- Log.i(TAG, "===== Read GC dump =====");
- }
- final GcStatus status = new GcStatus();
- final List<String> vmDump = getVmDump();
- Assume.assumeFalse("VM dump is empty", vmDump.isEmpty());
- for (String line : vmDump) {
- status.visit(line);
- if (line.startsWith("DALVIK THREADS")) {
- break;
- }
- }
- return status;
- }
-
- private static List<String> getVmDump() {
- final int myPid = Process.myPid();
- // Another approach Debug#dumpJavaBacktraceToFileTimeout requires setenforce 0.
- Process.sendSignal(myPid, Process.SIGNAL_QUIT);
- // Give a chance to handle the signal.
- SystemClock.sleep(100);
-
- String dump = null;
- final String pattern = myPid + " written to: ";
- final List<String> logs = shell("logcat -v brief -d tombstoned:I *:S");
- for (int i = logs.size() - 1; i >= 0; i--) {
- final String log = logs.get(i);
- // Log pattern: Traces for pid 9717 written to: /data/anr/trace_07
- final int pos = log.indexOf(pattern);
- if (pos > 0) {
- dump = log.substring(pattern.length() + pos);
- break;
- }
- }
-
- Assume.assumeNotNull("Unable to find VM dump", dump);
- // It requires system or root uid to read the trace.
- return shell("cat " + dump);
- }
-
- private static List<String> shell(String command) {
- final ParcelFileDescriptor.AutoCloseInputStream stream =
- new ParcelFileDescriptor.AutoCloseInputStream(
- getInstrumentation().getUiAutomation().executeShellCommand(command));
- final ArrayList<String> lines = new ArrayList<>();
- try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
- String line;
- while ((line = br.readLine()) != null) {
- lines.add(line);
- }
- } catch (IOException e) {
- throw new RuntimeException(e);
- }
- return lines;
- }
-
- /** An empty class which provides some methods with different type arguments. */
- static class Task {
- void doSomething() {
- }
-
- void doSomething(Task t) {
- }
-
- void doSomething(int taskId, long time) {
- }
-
- boolean doSomething(Rect bounds, boolean top, int taskId) {
- return false;
- }
- }
-
- static class ValPattern {
- static final int TYPE_COUNT = 0;
- static final int TYPE_TIME = 1;
- static final String PATTERN_COUNT = "(\\d+)";
- static final String PATTERN_TIME = "(\\d+\\.?\\d+)(\\w+)";
- final String mRawPattern;
- final Pattern mPattern;
- final int mType;
-
- int mIntValue;
- float mFloatValue;
-
- ValPattern(String p, int type) {
- mRawPattern = p;
- mPattern = Pattern.compile(
- p + (type == TYPE_TIME ? PATTERN_TIME : PATTERN_COUNT) + ".*");
- mType = type;
- }
-
- boolean visit(String line) {
- final Matcher matcher = mPattern.matcher(line);
- if (!matcher.matches()) {
- return false;
- }
- final String value = matcher.group(1);
- if (value == null) {
- return false;
- }
- if (mType == TYPE_COUNT) {
- mIntValue = Integer.parseInt(value);
- return true;
- }
- final float time = Float.parseFloat(value);
- final String unit = matcher.group(2);
- if (unit == null) {
- return false;
- }
- // Refer to art/libartbase/base/time_utils.cc
- switch (unit) {
- case "s":
- mFloatValue = time * 1000;
- break;
- case "ms":
- mFloatValue = time;
- break;
- case "us":
- mFloatValue = time / 1000;
- break;
- case "ns":
- mFloatValue = time / 1000 / 1000;
- break;
- default:
- throw new IllegalArgumentException();
- }
-
- return true;
- }
-
- @Override
- public String toString() {
- return mRawPattern + (mType == TYPE_TIME ? (mFloatValue + "ms") : mIntValue);
- }
- }
-
- /** Parses the dump pattern of Heap::DumpGcPerformanceInfo. */
- private static class GcStatus {
- private static final int TOTAL_GC_TIME_INDEX = 1;
- private static final int TOTAL_GC_PAUSED_TIME_INDEX = 5;
-
- // Refer to art/runtime/gc/heap.cc
- final ValPattern[] mPatterns = {
- new ValPattern("Total GC count: ", ValPattern.TYPE_COUNT),
- new ValPattern("Total GC time: ", ValPattern.TYPE_TIME),
- new ValPattern("Total time waiting for GC to complete: ", ValPattern.TYPE_TIME),
- new ValPattern("Total blocking GC count: ", ValPattern.TYPE_COUNT),
- new ValPattern("Total blocking GC time: ", ValPattern.TYPE_TIME),
- new ValPattern("Total mutator paused time: ", ValPattern.TYPE_TIME),
- new ValPattern("Total number of allocations ", ValPattern.TYPE_COUNT),
- new ValPattern("concurrent copying paused: Sum: ", ValPattern.TYPE_TIME),
- new ValPattern("concurrent copying total time: ", ValPattern.TYPE_TIME),
- new ValPattern("concurrent copying freed: ", ValPattern.TYPE_COUNT),
- new ValPattern("Peak regions allocated ", ValPattern.TYPE_COUNT),
- };
-
- void visit(String dumpLine) {
- for (ValPattern p : mPatterns) {
- if (p.visit(dumpLine)) {
- if (DEBUG) {
- Log.i(TAG, " " + p);
- }
- }
- }
- }
-
- GcInfo calculateGcTime(GcStatus newStatus, String title, Bundle result) {
- Log.i(TAG, "===== GC status of " + title + " =====");
- final GcInfo info = new GcInfo();
- for (int i = 0; i < mPatterns.length; i++) {
- final ValPattern p = mPatterns[i];
- if (p.mType == ValPattern.TYPE_COUNT) {
- final int diff = newStatus.mPatterns[i].mIntValue - p.mIntValue;
- Log.i(TAG, " " + p.mRawPattern + diff);
- if (diff > 0) {
- result.putInt("[" + title + "] " + p.mRawPattern, diff);
- }
- continue;
- }
- final float diff = newStatus.mPatterns[i].mFloatValue - p.mFloatValue;
- Log.i(TAG, " " + p.mRawPattern + diff + "ms");
- if (diff > 0) {
- result.putFloat("[" + title + "] " + p.mRawPattern + "(ms)", diff);
- }
- if (i == TOTAL_GC_TIME_INDEX) {
- info.mTotalGcTime = diff;
- } else if (i == TOTAL_GC_PAUSED_TIME_INDEX) {
- info.mTotalGcPausedTime = diff;
- }
- }
- return info;
- }
- }
-
- private static class GcInfo {
- float mTotalGcTime;
- float mTotalGcPausedTime;
- }
-}
+/*
+ * 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;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import android.app.Activity;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Log;
+
+import androidx.test.filters.LargeTest;
+
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
+import com.android.internal.util.function.pooled.PooledPredicate;
+
+import org.junit.Assume;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runners.model.Statement;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Compares the performance of regular lambda and pooled lambda. */
+@LargeTest
+public class LambdaPerfTest {
+ private static final boolean DEBUG = false;
+ private static final String TAG = LambdaPerfTest.class.getSimpleName();
+
+ private static final String LAMBDA_FORM_REGULAR = "regular";
+ private static final String LAMBDA_FORM_POOLED = "pooled";
+
+ private static final int WARMUP_ITERATIONS = 1000;
+ private static final int TEST_ITERATIONS = 3000000;
+ private static final int TASK_COUNT = 10;
+ private static final long DELAY_AFTER_BENCH_MS = 1000;
+
+ private String mMethodName;
+
+ private final Bundle mTestResults = new Bundle();
+ private final ArrayList<Task> mTasks = new ArrayList<>();
+
+ // The member fields are used to ensure lambda capturing. They don't have the actual meaning.
+ private final Task mTask = new Task();
+ private final Rect mBounds = new Rect();
+ private int mTaskId;
+ private long mTime;
+ private boolean mTop;
+
+ @Rule
+ public final TestRule mRule = (base, description) -> new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ mMethodName = description.getMethodName();
+ mTasks.clear();
+ for (int i = 0; i < TASK_COUNT; i++) {
+ final Task t = new Task();
+ mTasks.add(t);
+ }
+ base.evaluate();
+
+ getInstrumentation().sendStatus(Activity.RESULT_OK, mTestResults);
+ }
+ };
+
+ @Test
+ public void test1ParamConsumer() {
+ evaluate(LAMBDA_FORM_REGULAR, () -> forAllTask(t -> t.doSomething(mTask)));
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
+ PooledLambda.__(Task.class), mTask);
+ forAllTask(c);
+ c.recycle();
+ });
+ }
+
+ @Test
+ public void test2PrimitiveParamsConsumer() {
+ // Not in Integer#IntegerCache (-128~127) for autoboxing, that may create new object.
+ mTaskId = 12345;
+ mTime = 54321;
+
+ evaluate(LAMBDA_FORM_REGULAR, () -> forAllTask(t -> t.doSomething(mTaskId, mTime)));
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
+ PooledLambda.__(Task.class), mTaskId, mTime);
+ forAllTask(c);
+ c.recycle();
+ });
+ }
+
+ @Test
+ public void test3ParamsPredicate() {
+ mTop = true;
+ // In Integer#IntegerCache.
+ mTaskId = 10;
+
+ evaluate(LAMBDA_FORM_REGULAR, () -> handleTask(t -> t.doSomething(mBounds, mTop, mTaskId)));
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final PooledPredicate c = PooledLambda.obtainPredicate(Task::doSomething,
+ PooledLambda.__(Task.class), mBounds, mTop, mTaskId);
+ handleTask(c);
+ c.recycle();
+ });
+ }
+
+ @Test
+ public void testMessage() {
+ evaluate(LAMBDA_FORM_REGULAR, () -> {
+ final Message m = Message.obtain().setCallback(() -> mTask.doSomething(mTaskId, mTime));
+ m.getCallback().run();
+ m.recycle();
+ });
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final Message m = PooledLambda.obtainMessage(Task::doSomething, mTask, mTaskId, mTime);
+ m.getCallback().run();
+ m.recycle();
+ });
+ }
+
+ @Test
+ public void testRunnable() {
+ evaluate(LAMBDA_FORM_REGULAR, () -> {
+ final Runnable r = mTask::doSomething;
+ r.run();
+ });
+ evaluate(LAMBDA_FORM_POOLED, () -> {
+ final Runnable r = PooledLambda.obtainRunnable(Task::doSomething, mTask).recycleOnUse();
+ r.run();
+ });
+ }
+
+ @Test
+ public void testMultiThread() {
+ final int numThread = 3;
+
+ final Runnable regularAction = () -> forAllTask(t -> t.doSomething(mTask));
+ final Runnable[] regularActions = new Runnable[numThread];
+ Arrays.fill(regularActions, regularAction);
+ evaluateMultiThread(LAMBDA_FORM_REGULAR, regularActions);
+
+ final Runnable pooledAction = () -> {
+ final PooledConsumer c = PooledLambda.obtainConsumer(Task::doSomething,
+ PooledLambda.__(Task.class), mTask);
+ forAllTask(c);
+ c.recycle();
+ };
+ final Runnable[] pooledActions = new Runnable[numThread];
+ Arrays.fill(pooledActions, pooledAction);
+ evaluateMultiThread(LAMBDA_FORM_POOLED, pooledActions);
+ }
+
+ private void forAllTask(Consumer<Task> callback) {
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ callback.accept(mTasks.get(i));
+ }
+ }
+
+ private void handleTask(Predicate<Task> callback) {
+ for (int i = mTasks.size() - 1; i >= 0; i--) {
+ final Task task = mTasks.get(i);
+ if (callback.test(task)) {
+ return;
+ }
+ }
+ }
+
+ private void evaluate(String title, Runnable action) {
+ for (int i = 0; i < WARMUP_ITERATIONS; i++) {
+ action.run();
+ }
+ performGc();
+
+ final GcStatus startGcStatus = getGcStatus();
+ final long startTime = SystemClock.elapsedRealtime();
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ action.run();
+ }
+ evaluateResult(title, startGcStatus, startTime);
+ }
+
+ private void evaluateMultiThread(String title, Runnable[] actions) {
+ performGc();
+
+ final CountDownLatch latch = new CountDownLatch(actions.length);
+ final GcStatus startGcStatus = getGcStatus();
+ final long startTime = SystemClock.elapsedRealtime();
+ for (Runnable action : actions) {
+ new Thread() {
+ @Override
+ public void run() {
+ for (int i = 0; i < TEST_ITERATIONS; i++) {
+ action.run();
+ }
+ latch.countDown();
+ };
+ }.start();
+ }
+ try {
+ latch.await();
+ } catch (InterruptedException ignored) {
+ }
+ evaluateResult(title, startGcStatus, startTime);
+ }
+
+ private void evaluateResult(String title, GcStatus startStatus, long startTime) {
+ final float elapsed = SystemClock.elapsedRealtime() - startTime;
+ // Sleep a while to see if GC may happen.
+ SystemClock.sleep(DELAY_AFTER_BENCH_MS);
+ final GcStatus endStatus = getGcStatus();
+ final GcInfo info = startStatus.calculateGcTime(endStatus, title, mTestResults);
+ mTestResults.putFloat("[" + title + "-execution-time]", elapsed);
+ Log.i(TAG, mMethodName + "_" + title + " execution time: "
+ + elapsed + "ms (avg=" + String.format("%.5f", elapsed / TEST_ITERATIONS) + "ms)"
+ + " GC time: " + String.format("%.3f", info.mTotalGcTime) + "ms"
+ + " GC paused time: " + String.format("%.3f", info.mTotalGcPausedTime) + "ms");
+ }
+
+ /** Cleans the test environment. */
+ private static void performGc() {
+ System.gc();
+ System.runFinalization();
+ System.gc();
+ }
+
+ private static GcStatus getGcStatus() {
+ if (DEBUG) {
+ Log.i(TAG, "===== Read GC dump =====");
+ }
+ final GcStatus status = new GcStatus();
+ final List<String> vmDump = getVmDump();
+ Assume.assumeFalse("VM dump is empty", vmDump.isEmpty());
+ for (String line : vmDump) {
+ status.visit(line);
+ if (line.startsWith("DALVIK THREADS")) {
+ break;
+ }
+ }
+ return status;
+ }
+
+ private static List<String> getVmDump() {
+ final int myPid = Process.myPid();
+ // Another approach Debug#dumpJavaBacktraceToFileTimeout requires setenforce 0.
+ Process.sendSignal(myPid, Process.SIGNAL_QUIT);
+ // Give a chance to handle the signal.
+ SystemClock.sleep(100);
+
+ String dump = null;
+ final String pattern = myPid + " written to: ";
+ final List<String> logs = shell("logcat -v brief -d tombstoned:I *:S");
+ for (int i = logs.size() - 1; i >= 0; i--) {
+ final String log = logs.get(i);
+ // Log pattern: Traces for pid 9717 written to: /data/anr/trace_07
+ final int pos = log.indexOf(pattern);
+ if (pos > 0) {
+ dump = log.substring(pattern.length() + pos);
+ if (!dump.startsWith("/data/anr/")) {
+ dump = "/data/anr/" + dump;
+ }
+ break;
+ }
+ }
+
+ Assume.assumeNotNull("Unable to find VM dump", dump);
+ // It requires system or root uid to read the trace.
+ return shell("cat " + dump);
+ }
+
+ private static List<String> shell(String command) {
+ final ParcelFileDescriptor.AutoCloseInputStream stream =
+ new ParcelFileDescriptor.AutoCloseInputStream(
+ getInstrumentation().getUiAutomation().executeShellCommand(command));
+ final ArrayList<String> lines = new ArrayList<>();
+ try (BufferedReader br = new BufferedReader(new InputStreamReader(stream))) {
+ String line;
+ while ((line = br.readLine()) != null) {
+ lines.add(line);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ return lines;
+ }
+
+ /** An empty class which provides some methods with different type arguments. */
+ static class Task {
+ void doSomething() {
+ }
+
+ void doSomething(Task t) {
+ }
+
+ void doSomething(int taskId, long time) {
+ }
+
+ boolean doSomething(Rect bounds, boolean top, int taskId) {
+ return false;
+ }
+ }
+
+ static class ValPattern {
+ static final int TYPE_COUNT = 0;
+ static final int TYPE_TIME = 1;
+ static final String PATTERN_COUNT = "(\\d+)";
+ static final String PATTERN_TIME = "(\\d+\\.?\\d+)(\\w+)";
+ final String mRawPattern;
+ final Pattern mPattern;
+ final int mType;
+
+ int mIntValue;
+ float mFloatValue;
+
+ ValPattern(String p, int type) {
+ mRawPattern = p;
+ mPattern = Pattern.compile(
+ p + (type == TYPE_TIME ? PATTERN_TIME : PATTERN_COUNT) + ".*");
+ mType = type;
+ }
+
+ boolean visit(String line) {
+ final Matcher matcher = mPattern.matcher(line);
+ if (!matcher.matches()) {
+ return false;
+ }
+ final String value = matcher.group(1);
+ if (value == null) {
+ return false;
+ }
+ if (mType == TYPE_COUNT) {
+ mIntValue = Integer.parseInt(value);
+ return true;
+ }
+ final float time = Float.parseFloat(value);
+ final String unit = matcher.group(2);
+ if (unit == null) {
+ return false;
+ }
+ // Refer to art/libartbase/base/time_utils.cc
+ switch (unit) {
+ case "s":
+ mFloatValue = time * 1000;
+ break;
+ case "ms":
+ mFloatValue = time;
+ break;
+ case "us":
+ mFloatValue = time / 1000;
+ break;
+ case "ns":
+ mFloatValue = time / 1000 / 1000;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return mRawPattern + (mType == TYPE_TIME ? (mFloatValue + "ms") : mIntValue);
+ }
+ }
+
+ /** Parses the dump pattern of Heap::DumpGcPerformanceInfo. */
+ private static class GcStatus {
+ private static final int TOTAL_GC_TIME_INDEX = 1;
+ private static final int TOTAL_GC_PAUSED_TIME_INDEX = 5;
+
+ // Refer to art/runtime/gc/heap.cc
+ final ValPattern[] mPatterns = {
+ new ValPattern("Total GC count: ", ValPattern.TYPE_COUNT),
+ new ValPattern("Total GC time: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total time waiting for GC to complete: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total blocking GC count: ", ValPattern.TYPE_COUNT),
+ new ValPattern("Total blocking GC time: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total mutator paused time: ", ValPattern.TYPE_TIME),
+ new ValPattern("Total number of allocations ", ValPattern.TYPE_COUNT),
+ new ValPattern("concurrent copying paused: Sum: ", ValPattern.TYPE_TIME),
+ new ValPattern("concurrent copying total time: ", ValPattern.TYPE_TIME),
+ new ValPattern("concurrent copying freed: ", ValPattern.TYPE_COUNT),
+ new ValPattern("Peak regions allocated ", ValPattern.TYPE_COUNT),
+ };
+
+ void visit(String dumpLine) {
+ for (ValPattern p : mPatterns) {
+ if (p.visit(dumpLine)) {
+ if (DEBUG) {
+ Log.i(TAG, " " + p);
+ }
+ }
+ }
+ }
+
+ GcInfo calculateGcTime(GcStatus newStatus, String title, Bundle result) {
+ Log.i(TAG, "===== GC status of " + title + " =====");
+ final GcInfo info = new GcInfo();
+ for (int i = 0; i < mPatterns.length; i++) {
+ final ValPattern p = mPatterns[i];
+ if (p.mType == ValPattern.TYPE_COUNT) {
+ final int diff = newStatus.mPatterns[i].mIntValue - p.mIntValue;
+ Log.i(TAG, " " + p.mRawPattern + diff);
+ if (diff > 0) {
+ result.putInt("[" + title + "] " + p.mRawPattern, diff);
+ }
+ continue;
+ }
+ final float diff = newStatus.mPatterns[i].mFloatValue - p.mFloatValue;
+ Log.i(TAG, " " + p.mRawPattern + diff + "ms");
+ if (diff > 0) {
+ result.putFloat("[" + title + "] " + p.mRawPattern + "(ms)", diff);
+ }
+ if (i == TOTAL_GC_TIME_INDEX) {
+ info.mTotalGcTime = diff;
+ } else if (i == TOTAL_GC_PAUSED_TIME_INDEX) {
+ info.mTotalGcPausedTime = diff;
+ }
+ }
+ return info;
+ }
+ }
+
+ private static class GcInfo {
+ float mTotalGcTime;
+ float mTotalGcPausedTime;
+ }
+}
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 54b3c400af4f..f924b2e9b932 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -23,6 +23,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
@@ -276,7 +277,6 @@ public class VcnManagementServiceTest {
@Test
public void testSystemReady() throws Exception {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
verify(mConnMgr).registerNetworkProvider(any(VcnNetworkProvider.class));
verify(mSubscriptionTracker).register();
@@ -494,8 +494,10 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
- mTestLooper.dispatchAll();
+ // Verify teardown after delay
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
verify(vcn).teardownAsynchronously();
verify(mMockPolicyListener).onPolicyChanged();
}
@@ -521,6 +523,92 @@ public class VcnManagementServiceTest {
assertEquals(0, mVcnMgmtSvc.getAllVcns().size());
}
+ /**
+ * Tests an intermediate state where carrier privileges are marked as lost before active data
+ * subId changes during a SIM ejection.
+ *
+ * <p>The expected outcome is that the VCN is torn down after a delay, as opposed to
+ * immediately.
+ */
+ @Test
+ public void testTelephonyNetworkTrackerCallbackLostCarrierPrivilegesBeforeActiveDataSubChanges()
+ throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate privileges lost
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ TEST_SUBSCRIPTION_ID,
+ TEST_UUID_2,
+ Collections.emptySet(),
+ Collections.emptyMap(),
+ false /* hasCarrierPrivileges */);
+
+ // Verify teardown after delay
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(vcn).teardownAsynchronously();
+ }
+
+ @Test
+ public void testTelephonyNetworkTrackerCallbackSimSwitchesDoNotKillVcnInstances()
+ throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate SIM unloaded
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ INVALID_SUBSCRIPTION_ID,
+ null /* activeDataSubscriptionGroup */,
+ Collections.emptySet(),
+ Collections.emptyMap(),
+ false /* hasCarrierPrivileges */);
+
+ // Simulate new SIM loaded right during teardown delay.
+ mTestLooper.moveTimeForward(
+ VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
+ mTestLooper.dispatchAll();
+ triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2));
+
+ // Verify that even after the full timeout duration, the VCN instance is not torn down
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(vcn, never()).teardownAsynchronously();
+ }
+
+ @Test
+ public void testTelephonyNetworkTrackerCallbackDoesNotKillNewVcnInstances() throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate SIM unloaded
+ triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
+
+ // Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
+ // vcnInstance.
+ mTestLooper.moveTimeForward(
+ VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
+ mTestLooper.dispatchAll();
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME);
+ triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2));
+ final Vcn newInstance = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Verify that new instance was different, and the old one was torn down
+ assertTrue(oldInstance != newInstance);
+ verify(oldInstance).teardownAsynchronously();
+
+ // Verify that even after the full timeout duration, the new VCN instance is not torn down
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(newInstance, never()).teardownAsynchronously();
+ }
+
@Test
public void testPackageChangeListenerRegistered() throws Exception {
verify(mMockContext).registerReceiver(any(BroadcastReceiver.class), argThat(filter -> {
@@ -910,8 +998,6 @@ public class VcnManagementServiceTest {
private void setupSubscriptionAndStartVcn(
int subId, ParcelUuid subGrp, boolean isVcnActive, boolean hasCarrierPrivileges) {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
-
triggerSubscriptionTrackerCbAndGetSnapshot(
subGrp,
Collections.singleton(subGrp),
@@ -1007,7 +1093,6 @@ public class VcnManagementServiceTest {
private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
final ArgumentCaptor<NetworkCallback> captor =
ArgumentCaptor.forClass(NetworkCallback.class);
@@ -1252,14 +1337,15 @@ public class VcnManagementServiceTest {
true /* isActive */,
true /* hasCarrierPrivileges */);
- // VCN is currently active. Lose carrier privileges for TEST_PACKAGE so the VCN goes
- // inactive.
+ // VCN is currently active. Lose carrier privileges for TEST_PACKAGE and hit teardown
+ // timeout so the VCN goes inactive.
final TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(
TEST_UUID_1,
Collections.singleton(TEST_UUID_1),
Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_1),
false /* hasCarrierPrivileges */);
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
mTestLooper.dispatchAll();
// Giving TEST_PACKAGE privileges again will restart the VCN (which will indicate ACTIVE
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index 7efe3c3472fa..7ddbe95aa79b 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -130,7 +130,7 @@ cc_library_host_static {
"optimize/MultiApkGenerator.cpp",
"optimize/ResourceDeduper.cpp",
"optimize/ResourceFilter.cpp",
- "optimize/ResourcePathShortener.cpp",
+ "optimize/Obfuscator.cpp",
"optimize/VersionCollapser.cpp",
"process/SymbolTable.cpp",
"split/TableSplitter.cpp",
@@ -161,6 +161,7 @@ cc_library_host_static {
"ApkInfo.proto",
"Configuration.proto",
"Resources.proto",
+ "ResourceMetadata.proto",
"ResourcesInternal.proto",
"ValueTransformer.cpp",
],
@@ -218,6 +219,7 @@ genrule {
srcs: [
"Configuration.proto",
"ResourcesInternal.proto",
+ "ResourceMetadata.proto",
"Resources.proto",
],
out: ["aapt2-protos.zip"],
diff --git a/tools/aapt2/ResourceMetadata.proto b/tools/aapt2/ResourceMetadata.proto
new file mode 100644
index 000000000000..8eca54c4da5e
--- /dev/null
+++ b/tools/aapt2/ResourceMetadata.proto
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto3";
+
+package aapt.pb;
+
+option java_package = "com.android.aapt";
+option java_multiple_files = true;
+
+message ResourceMappings {
+ ShortenedPathsMap shortened_paths = 1;
+ CollapsedNamesMap collapsed_names = 2;
+}
+
+// Metadata relating to "aapt2 optimize --shorten-resource-paths"
+message ShortenedPathsMap {
+ // Maps shorted paths (e.g. "res/foo.xml") to their original names (e.g.
+ // "res/xml/file_with_long_name.xml").
+ message ResourcePathMapping {
+ string shortened_path = 1;
+ string original_path = 2;
+ }
+ repeated ResourcePathMapping resource_paths = 1;
+}
+
+// Metadata relating to "aapt2 optimize --collapse-resource-names"
+message CollapsedNamesMap {
+ // Maps resource IDs (e.g. 0x7f123456) to their original names (e.g.
+ // "package:type/entry").
+ message ResourceNameMapping {
+ uint32 id = 1;
+ string name = 2;
+ }
+ repeated ResourceNameMapping resource_names = 1;
+}
diff --git a/tools/aapt2/cmd/Convert.cpp b/tools/aapt2/cmd/Convert.cpp
index cf58f989a7d4..aeedf8b5805e 100644
--- a/tools/aapt2/cmd/Convert.cpp
+++ b/tools/aapt2/cmd/Convert.cpp
@@ -395,6 +395,12 @@ int ConvertCommand::Action(const std::vector<std::string>& args) {
<< output_format_.value());
return 1;
}
+ if (enable_sparse_encoding_) {
+ table_flattener_options_.sparse_entries = SparseEntriesMode::Enabled;
+ }
+ if (force_sparse_encoding_) {
+ table_flattener_options_.sparse_entries = SparseEntriesMode::Forced;
+ }
return Convert(&context, apk.get(), writer.get(), format, table_flattener_options_,
xml_flattener_options_);
diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h
index 2cdb0c87310f..6c096496a1ea 100644
--- a/tools/aapt2/cmd/Convert.h
+++ b/tools/aapt2/cmd/Convert.h
@@ -34,10 +34,18 @@ class ConvertCommand : public Command {
AddOptionalFlag("--output-format", android::base::StringPrintf("Format of the output. "
"Accepted values are '%s' and '%s'. When not set, defaults to '%s'.",
kOutputFormatProto, kOutputFormatBinary, kOutputFormatBinary), &output_format_);
- AddOptionalSwitch("--enable-sparse-encoding",
+ AddOptionalSwitch(
+ "--enable-sparse-encoding",
"Enables encoding sparse entries using a binary search tree.\n"
- "This decreases APK size at the cost of resource retrieval performance.",
- &table_flattener_options_.use_sparse_entries);
+ "This decreases APK size at the cost of resource retrieval performance.\n"
+ "Only applies sparse encoding to Android O+ resources or all resources if minSdk of "
+ "the APK is O+",
+ &enable_sparse_encoding_);
+ AddOptionalSwitch("--force-sparse-encoding",
+ "Enables encoding sparse entries using a binary search tree.\n"
+ "This decreases APK size at the cost of resource retrieval performance.\n"
+ "Applies sparse encoding to all resources regardless of minSdk.",
+ &force_sparse_encoding_);
AddOptionalSwitch("--keep-raw-values",
android::base::StringPrintf("Preserve raw attribute values in xml files when using the"
" '%s' output format", kOutputFormatBinary),
@@ -56,6 +64,8 @@ class ConvertCommand : public Command {
std::string output_path_;
std::optional<std::string> output_format_;
bool verbose_ = false;
+ bool enable_sparse_encoding_ = false;
+ bool force_sparse_encoding_ = false;
};
int Convert(IAaptContext* context, LoadedApk* input, IArchiveWriter* output_writer,
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 74a8bbd4fd0d..116dcd641bc1 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -2419,6 +2419,9 @@ int LinkCommand::Action(const std::vector<std::string>& args) {
<< "the --merge-only flag can be only used when building a static library");
return 1;
}
+ if (options_.use_sparse_encoding) {
+ options_.table_flattener_options.sparse_entries = SparseEntriesMode::Enabled;
+ }
// The default build type.
context.SetPackageType(PackageType::kApp);
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index a5623cb1f1e1..6c8d1434fb88 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -69,6 +69,7 @@ struct LinkOptions {
bool no_resource_removal = false;
bool no_xml_namespaces = false;
bool do_not_compress_anything = false;
+ bool use_sparse_encoding = false;
std::unordered_set<std::string> extensions_to_not_compress;
std::optional<std::regex> regex_to_not_compress;
@@ -156,8 +157,8 @@ class LinkCommand : public Command {
"defaults. Use this only when building runtime resource overlay packages.",
&options_.no_resource_removal);
AddOptionalSwitch("--enable-sparse-encoding",
- "This decreases APK size at the cost of resource retrieval performance.",
- &options_.table_flattener_options.use_sparse_entries);
+ "This decreases APK size at the cost of resource retrieval performance.",
+ &options_.use_sparse_encoding);
AddOptionalSwitch("-x", "Legacy flag that specifies to use the package identifier 0x01.",
&legacy_x_flag_);
AddOptionalSwitch("-z", "Require localization of strings marked 'suggested'.",
diff --git a/tools/aapt2/cmd/Optimize.cpp b/tools/aapt2/cmd/Optimize.cpp
index 4033983f4c0f..9feaf524eaf1 100644
--- a/tools/aapt2/cmd/Optimize.cpp
+++ b/tools/aapt2/cmd/Optimize.cpp
@@ -16,7 +16,11 @@
#include "Optimize.h"
+#include <map>
#include <memory>
+#include <set>
+#include <string>
+#include <utility>
#include <vector>
#include "Diagnostics.h"
@@ -38,9 +42,9 @@
#include "io/BigBufferStream.h"
#include "io/Util.h"
#include "optimize/MultiApkGenerator.h"
+#include "optimize/Obfuscator.h"
#include "optimize/ResourceDeduper.h"
#include "optimize/ResourceFilter.h"
-#include "optimize/ResourcePathShortener.h"
#include "optimize/VersionCollapser.h"
#include "split/TableSplitter.h"
#include "util/Files.h"
@@ -114,11 +118,11 @@ class OptimizeContext : public IAaptContext {
}
private:
- DISALLOW_COPY_AND_ASSIGN(OptimizeContext);
-
StdErrDiagnostics diagnostics_;
bool verbose_ = false;
int sdk_version_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(OptimizeContext);
};
class Optimizer {
@@ -151,8 +155,8 @@ class Optimizer {
}
if (options_.shorten_resource_paths) {
- ResourcePathShortener shortener(options_.table_flattener_options.shortened_path_map);
- if (!shortener.Consume(context_, apk->GetResourceTable())) {
+ Obfuscator obfuscator(options_.table_flattener_options.shortened_path_map);
+ if (!obfuscator.Consume(context_, apk->GetResourceTable())) {
context_->GetDiagnostics()->Error(android::DiagMessage()
<< "failed shortening resource paths");
return 1;
@@ -427,6 +431,13 @@ int OptimizeCommand::Action(const std::vector<std::string>& args) {
return 1;
}
+ if (options_.enable_sparse_encoding) {
+ options_.table_flattener_options.sparse_entries = SparseEntriesMode::Enabled;
+ }
+ if (options_.force_sparse_encoding) {
+ options_.table_flattener_options.sparse_entries = SparseEntriesMode::Forced;
+ }
+
if (target_densities_) {
// Parse the target screen densities.
for (const StringPiece& config_str : util::Tokenize(target_densities_.value(), ',')) {
diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h
index ff63e8dd76d4..10b84b0c6bda 100644
--- a/tools/aapt2/cmd/Optimize.h
+++ b/tools/aapt2/cmd/Optimize.h
@@ -61,6 +61,12 @@ struct OptimizeOptions {
// Path to the output map of original resource paths to shortened paths.
std::optional<std::string> shortened_paths_map_path;
+
+ // Whether sparse encoding should be used for O+ resources.
+ bool enable_sparse_encoding = false;
+
+ // Whether sparse encoding should be used for all resources.
+ bool force_sparse_encoding = false;
};
class OptimizeCommand : public Command {
@@ -96,10 +102,18 @@ class OptimizeCommand : public Command {
"Comma separated list of artifacts to keep. If none are specified,\n"
"all artifacts will be kept.",
&kept_artifacts_);
- AddOptionalSwitch("--enable-sparse-encoding",
+ AddOptionalSwitch(
+ "--enable-sparse-encoding",
"Enables encoding sparse entries using a binary search tree.\n"
- "This decreases APK size at the cost of resource retrieval performance.",
- &options_.table_flattener_options.use_sparse_entries);
+ "This decreases APK size at the cost of resource retrieval performance.\n"
+ "Only applies sparse encoding to Android O+ resources or all resources if minSdk of "
+ "the APK is O+",
+ &options_.enable_sparse_encoding);
+ AddOptionalSwitch("--force-sparse-encoding",
+ "Enables encoding sparse entries using a binary search tree.\n"
+ "This decreases APK size at the cost of resource retrieval performance.\n"
+ "Applies sparse encoding to all resources regardless of minSdk.",
+ &options_.force_sparse_encoding);
AddOptionalSwitch("--collapse-resource-names",
"Collapses resource names to a single value in the key string pool. Resources can \n"
"be exempted using the \"no_collapse\" directive in a file specified by "
diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp
index 4fb7ed19ed20..22f278cc0b22 100644
--- a/tools/aapt2/format/binary/TableFlattener.cpp
+++ b/tools/aapt2/format/binary/TableFlattener.cpp
@@ -231,14 +231,14 @@ struct OverlayableChunk {
class PackageFlattener {
public:
PackageFlattener(IAaptContext* context, const ResourceTablePackageView& package,
- const std::map<size_t, std::string>* shared_libs, bool use_sparse_entries,
- bool collapse_key_stringpool,
+ const std::map<size_t, std::string>* shared_libs,
+ SparseEntriesMode sparse_entries, bool collapse_key_stringpool,
const std::set<ResourceName>& name_collapse_exemptions)
: context_(context),
diag_(context->GetDiagnostics()),
package_(package),
shared_libs_(shared_libs),
- use_sparse_entries_(use_sparse_entries),
+ sparse_entries_(sparse_entries),
collapse_key_stringpool_(collapse_key_stringpool),
name_collapse_exemptions_(name_collapse_exemptions) {
}
@@ -367,10 +367,12 @@ class PackageFlattener {
}
}
- bool sparse_encode = use_sparse_entries_;
+ bool sparse_encode = sparse_entries_ == SparseEntriesMode::Enabled ||
+ sparse_entries_ == SparseEntriesMode::Forced;
- if (context_->GetMinSdkVersion() == 0 && config.sdkVersion == 0) {
- // Sparse encode if sdk version is not set in context and config.
+ if (sparse_entries_ == SparseEntriesMode::Forced ||
+ (context_->GetMinSdkVersion() == 0 && config.sdkVersion == 0)) {
+ // Sparse encode if forced or sdk version is not set in context and config.
} else {
// Otherwise, only sparse encode if the entries will be read on platforms S_V2+.
sparse_encode = sparse_encode &&
@@ -712,7 +714,7 @@ class PackageFlattener {
android::IDiagnostics* diag_;
const ResourceTablePackageView package_;
const std::map<size_t, std::string>* shared_libs_;
- bool use_sparse_entries_;
+ SparseEntriesMode sparse_entries_;
android::StringPool type_pool_;
android::StringPool key_pool_;
bool collapse_key_stringpool_;
@@ -768,7 +770,7 @@ bool TableFlattener::Consume(IAaptContext* context, ResourceTable* table) {
}
PackageFlattener flattener(context, package, &table->included_packages_,
- options_.use_sparse_entries, options_.collapse_key_stringpool,
+ options_.sparse_entries, options_.collapse_key_stringpool,
options_.name_collapse_exemptions);
if (!flattener.FlattenPackage(&package_buffer)) {
return false;
diff --git a/tools/aapt2/format/binary/TableFlattener.h b/tools/aapt2/format/binary/TableFlattener.h
index 1eec0e4d8c51..c6d30335ed53 100644
--- a/tools/aapt2/format/binary/TableFlattener.h
+++ b/tools/aapt2/format/binary/TableFlattener.h
@@ -29,12 +29,20 @@ namespace aapt {
// preferred.
constexpr const size_t kSparseEncodingThreshold = 60;
+enum class SparseEntriesMode {
+ // Disables sparse encoding for entries.
+ Disabled,
+ // Enables sparse encoding for all entries for APKs with O+ minSdk. For APKs with minSdk less
+ // than O only applies sparse encoding for resource configuration available on O+.
+ Enabled,
+ // Enables sparse encoding for all entries regardless of minSdk.
+ Forced,
+};
+
struct TableFlattenerOptions {
- // When true, types for configurations with a sparse set of entries are encoded
+ // When enabled, types for configurations with a sparse set of entries are encoded
// as a sparse map of entry ID and offset to actual data.
- // This is only available on platforms O+ and will only be respected when
- // minSdk is O+.
- bool use_sparse_entries = false;
+ SparseEntriesMode sparse_entries = SparseEntriesMode::Disabled;
// When true, the key string pool in the final ResTable
// is collapsed to a single entry. All resource entries
diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp
index f551bf61dc06..b69daddf1c7a 100644
--- a/tools/aapt2/format/binary/TableFlattener_test.cpp
+++ b/tools/aapt2/format/binary/TableFlattener_test.cpp
@@ -337,7 +337,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkO) {
auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
TableFlattenerOptions options;
- options.use_sparse_entries = true;
+ options.sparse_entries = SparseEntriesMode::Enabled;
std::string no_sparse_contents;
ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
@@ -380,7 +380,29 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithConfigSdkVersionO) {
auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
TableFlattenerOptions options;
- options.use_sparse_entries = true;
+ options.sparse_entries = SparseEntriesMode::Enabled;
+
+ std::string no_sparse_contents;
+ ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
+
+ std::string sparse_contents;
+ ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents));
+
+ EXPECT_GT(no_sparse_contents.size(), sparse_contents.size());
+}
+
+TEST_F(TableFlattenerTest, FlattenSparseEntryRegardlessOfMinSdkWhenForced) {
+ std::unique_ptr<IAaptContext> context = test::ContextBuilder()
+ .SetCompilationPackage("android")
+ .SetPackageId(0x01)
+ .SetMinSdkVersion(SDK_LOLLIPOP)
+ .Build();
+
+ const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB");
+ auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
+
+ TableFlattenerOptions options;
+ options.sparse_entries = SparseEntriesMode::Forced;
std::string no_sparse_contents;
ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
@@ -399,7 +421,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSet) {
auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f);
TableFlattenerOptions options;
- options.use_sparse_entries = true;
+ options.sparse_entries = SparseEntriesMode::Enabled;
std::string no_sparse_contents;
ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
@@ -442,7 +464,7 @@ TEST_F(TableFlattenerTest, DoNotUseSparseEntryForDenseConfig) {
auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.80f);
TableFlattenerOptions options;
- options.use_sparse_entries = true;
+ options.sparse_entries = SparseEntriesMode::Enabled;
std::string no_sparse_contents;
ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents));
diff --git a/tools/aapt2/optimize/ResourcePathShortener.cpp b/tools/aapt2/optimize/Obfuscator.cpp
index 7ff9bf5aa8df..f704f26bfd29 100644
--- a/tools/aapt2/optimize/ResourcePathShortener.cpp
+++ b/tools/aapt2/optimize/Obfuscator.cpp
@@ -14,28 +14,25 @@
* limitations under the License.
*/
-#include "optimize/ResourcePathShortener.h"
+#include "optimize/Obfuscator.h"
#include <set>
+#include <string>
#include <unordered_set>
-#include "androidfw/StringPiece.h"
-
#include "ResourceTable.h"
#include "ValueVisitor.h"
+#include "androidfw/StringPiece.h"
#include "util/Util.h"
-
-static const std::string base64_chars =
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "abcdefghijklmnopqrstuvwxyz"
- "0123456789-_";
+static const char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789-_";
namespace aapt {
-ResourcePathShortener::ResourcePathShortener(
- std::map<std::string, std::string>& path_map_out)
- : path_map_(path_map_out) {
+Obfuscator::Obfuscator(std::map<std::string, std::string>& path_map_out) : path_map_(path_map_out) {
}
std::string ShortenFileName(const android::StringPiece& file_path, int output_length) {
@@ -50,7 +47,6 @@ std::string ShortenFileName(const android::StringPiece& file_path, int output_le
return result;
}
-
// Return the optimal hash length such that at most 10% of resources collide in
// their shortened path.
// Reference: http://matt.might.net/articles/counting-hash-collisions/
@@ -63,7 +59,7 @@ int OptimalShortenedLength(int num_resources) {
}
std::string GetShortenedPath(const android::StringPiece& shortened_filename,
- const android::StringPiece& extension, int collision_count) {
+ const android::StringPiece& extension, int collision_count) {
std::string shortened_path = "res/" + shortened_filename.to_string();
if (collision_count > 0) {
shortened_path += std::to_string(collision_count);
@@ -76,12 +72,12 @@ std::string GetShortenedPath(const android::StringPiece& shortened_filename,
// underlying filepath as key rather than the integer address. This is to ensure
// determinism of output for colliding files.
struct PathComparator {
- bool operator() (const FileReference* lhs, const FileReference* rhs) const {
- return lhs->path->compare(*rhs->path);
- }
+ bool operator()(const FileReference* lhs, const FileReference* rhs) const {
+ return lhs->path->compare(*rhs->path);
+ }
};
-bool ResourcePathShortener::Consume(IAaptContext* context, ResourceTable* table) {
+bool Obfuscator::Consume(IAaptContext* context, ResourceTable* table) {
// used to detect collisions
std::unordered_set<std::string> shortened_paths;
std::set<FileReference*, PathComparator> file_refs;
@@ -103,8 +99,7 @@ bool ResourcePathShortener::Consume(IAaptContext* context, ResourceTable* table)
util::ExtractResFilePathParts(*file_ref->path, &res_subdir, &actual_filename, &extension);
// Android detects ColorStateLists via pathname, skip res/color*
- if (util::StartsWith(res_subdir, "res/color"))
- continue;
+ if (util::StartsWith(res_subdir, "res/color")) continue;
std::string shortened_filename = ShortenFileName(*file_ref->path, num_chars);
int collision_count = 0;
diff --git a/tools/aapt2/optimize/ResourcePathShortener.h b/tools/aapt2/optimize/Obfuscator.h
index f1074ef083bd..1ea32db12815 100644
--- a/tools/aapt2/optimize/ResourcePathShortener.h
+++ b/tools/aapt2/optimize/Obfuscator.h
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-#ifndef AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H
-#define AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H
+#ifndef TOOLS_AAPT2_OPTIMIZE_OBFUSCATOR_H_
+#define TOOLS_AAPT2_OPTIMIZE_OBFUSCATOR_H_
#include <map>
+#include <string>
#include "android-base/macros.h"
-
#include "process/IResourceTableConsumer.h"
namespace aapt {
@@ -28,17 +28,17 @@ namespace aapt {
class ResourceTable;
// Maps resources in the apk to shortened paths.
-class ResourcePathShortener : public IResourceTableConsumer {
+class Obfuscator : public IResourceTableConsumer {
public:
- explicit ResourcePathShortener(std::map<std::string, std::string>& path_map_out);
+ explicit Obfuscator(std::map<std::string, std::string>& path_map_out);
bool Consume(IAaptContext* context, ResourceTable* table) override;
private:
- DISALLOW_COPY_AND_ASSIGN(ResourcePathShortener);
std::map<std::string, std::string>& path_map_;
+ DISALLOW_COPY_AND_ASSIGN(Obfuscator);
};
-} // namespace aapt
+} // namespace aapt
-#endif // AAPT_OPTIMIZE_RESOURCEPATHSHORTENER_H
+#endif // TOOLS_AAPT2_OPTIMIZE_OBFUSCATOR_H_
diff --git a/tools/aapt2/optimize/ResourcePathShortener_test.cpp b/tools/aapt2/optimize/Obfuscator_test.cpp
index f5a02be0ea5e..a3339d486d4a 100644
--- a/tools/aapt2/optimize/ResourcePathShortener_test.cpp
+++ b/tools/aapt2/optimize/Obfuscator_test.cpp
@@ -14,15 +14,18 @@
* limitations under the License.
*/
-#include "optimize/ResourcePathShortener.h"
+#include "optimize/Obfuscator.h"
+
+#include <memory>
+#include <string>
#include "ResourceTable.h"
#include "test/Test.h"
using ::aapt::test::GetValue;
+using ::testing::Eq;
using ::testing::Not;
using ::testing::NotNull;
-using ::testing::Eq;
android::StringPiece GetExtension(android::StringPiece path) {
auto iter = std::find(path.begin(), path.end(), '.');
@@ -30,16 +33,15 @@ android::StringPiece GetExtension(android::StringPiece path) {
}
void FillTable(aapt::test::ResourceTableBuilder& builder, int start, int end) {
- for (int i=start; i<end; i++) {
- builder.AddFileReference(
- "android:drawable/xmlfile" + std::to_string(i),
- "res/drawable/xmlfile" + std::to_string(i) + ".xml");
+ for (int i = start; i < end; i++) {
+ builder.AddFileReference("android:drawable/xmlfile" + std::to_string(i),
+ "res/drawable/xmlfile" + std::to_string(i) + ".xml");
}
}
namespace aapt {
-TEST(ResourcePathShortenerTest, FileRefPathsChangedInResourceTable) {
+TEST(ObfuscatorTest, FileRefPathsChangedInResourceTable) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
@@ -50,7 +52,7 @@ TEST(ResourcePathShortenerTest, FileRefPathsChangedInResourceTable) {
.Build();
std::map<std::string, std::string> path_map;
- ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get()));
+ ASSERT_TRUE(Obfuscator(path_map).Consume(context.get(), table.get()));
// Expect that the path map is populated
ASSERT_THAT(path_map.find("res/drawables/xmlfile.xml"), Not(Eq(path_map.end())));
@@ -64,39 +66,36 @@ TEST(ResourcePathShortenerTest, FileRefPathsChangedInResourceTable) {
EXPECT_THAT(path_map["res/drawables/xmlfile.xml"],
Not(Eq(path_map["res/drawables/xmlfile2.xml"])));
- FileReference* ref =
- GetValue<FileReference>(table.get(), "android:drawable/xmlfile");
+ FileReference* ref = GetValue<FileReference>(table.get(), "android:drawable/xmlfile");
ASSERT_THAT(ref, NotNull());
// The map correctly points to the new location of the file
EXPECT_THAT(path_map["res/drawables/xmlfile.xml"], Eq(*ref->path));
// Strings should not be affected, only file paths
- EXPECT_THAT(
- *GetValue<String>(table.get(), "android:string/string")->value,
+ EXPECT_THAT(*GetValue<String>(table.get(), "android:string/string")->value,
Eq("res/should/still/be/the/same.png"));
EXPECT_THAT(path_map.find("res/should/still/be/the/same.png"), Eq(path_map.end()));
}
-TEST(ResourcePathShortenerTest, SkipColorFileRefPaths) {
+TEST(ObfuscatorTest, SkipColorFileRefPaths) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::unique_ptr<ResourceTable> table =
test::ResourceTableBuilder()
.AddFileReference("android:color/colorlist", "res/color/colorlist.xml")
- .AddFileReference("android:color/colorlist",
- "res/color-mdp-v21/colorlist.xml",
+ .AddFileReference("android:color/colorlist", "res/color-mdp-v21/colorlist.xml",
test::ParseConfigOrDie("mdp-v21"))
.Build();
std::map<std::string, std::string> path_map;
- ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get()));
+ ASSERT_TRUE(Obfuscator(path_map).Consume(context.get(), table.get()));
// Expect that the path map to not contain the ColorStateList
ASSERT_THAT(path_map.find("res/color/colorlist.xml"), Eq(path_map.end()));
ASSERT_THAT(path_map.find("res/color-mdp-v21/colorlist.xml"), Eq(path_map.end()));
}
-TEST(ResourcePathShortenerTest, KeepExtensions) {
+TEST(ObfuscatorTest, KeepExtensions) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
std::string original_xml_path = "res/drawable/xmlfile.xml";
@@ -109,7 +108,7 @@ TEST(ResourcePathShortenerTest, KeepExtensions) {
.Build();
std::map<std::string, std::string> path_map;
- ASSERT_TRUE(ResourcePathShortener(path_map).Consume(context.get(), table.get()));
+ ASSERT_TRUE(Obfuscator(path_map).Consume(context.get(), table.get()));
// Expect that the path map is populated
ASSERT_THAT(path_map.find("res/drawable/xmlfile.xml"), Not(Eq(path_map.end())));
@@ -122,7 +121,7 @@ TEST(ResourcePathShortenerTest, KeepExtensions) {
EXPECT_THAT(GetExtension(path_map[original_png_path]), Eq(android::StringPiece(".png")));
}
-TEST(ResourcePathShortenerTest, DeterministicallyHandleCollisions) {
+TEST(ObfuscatorTest, DeterministicallyHandleCollisions) {
std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
// 4000 resources is the limit at which the hash space is expanded to 3
@@ -135,27 +134,27 @@ TEST(ResourcePathShortenerTest, DeterministicallyHandleCollisions) {
FillTable(builder1, 0, kNumResources);
std::unique_ptr<ResourceTable> table1 = builder1.Build();
std::map<std::string, std::string> expected_mapping;
- ASSERT_TRUE(ResourcePathShortener(expected_mapping).Consume(context.get(), table1.get()));
+ ASSERT_TRUE(Obfuscator(expected_mapping).Consume(context.get(), table1.get()));
// We are trying to ensure lack of non-determinism, it is not simple to prove
// a negative, thus we must try the test a few times so that the test itself
// is non-flaky. Basically create the pathmap 5 times from the same set of
// resources but a different order of addition and then ensure they are always
// mapped to the same short path.
- for (int i=0; i<kNumTries; i++) {
+ for (int i = 0; i < kNumTries; i++) {
test::ResourceTableBuilder builder2;
// This loop adds resources to the resource table in the range of
// [0:kNumResources). Adding the file references in different order makes
// non-determinism more likely to surface. Thus we add resources
// [start_index:kNumResources) first then [0:start_index). We also use a
// different start_index each run.
- int start_index = (kNumResources/kNumTries)*i;
+ int start_index = (kNumResources / kNumTries) * i;
FillTable(builder2, start_index, kNumResources);
FillTable(builder2, 0, start_index);
std::unique_ptr<ResourceTable> table2 = builder2.Build();
std::map<std::string, std::string> actual_mapping;
- ASSERT_TRUE(ResourcePathShortener(actual_mapping).Consume(context.get(), table2.get()));
+ ASSERT_TRUE(Obfuscator(actual_mapping).Consume(context.get(), table2.get()));
for (auto& item : actual_mapping) {
ASSERT_THAT(expected_mapping[item.first], Eq(item.second));
@@ -163,4 +162,4 @@ TEST(ResourcePathShortenerTest, DeterministicallyHandleCollisions) {
}
}
-} // namespace aapt
+} // namespace aapt
diff --git a/tools/fonts/Android.bp b/tools/fonts/Android.bp
index eeb9e3ceda1e..f8629f9bd0b8 100644
--- a/tools/fonts/Android.bp
+++ b/tools/fonts/Android.bp
@@ -24,12 +24,7 @@ package {
python_defaults {
name: "fonts_python_defaults",
version: {
- py2: {
- enabled: false,
- embedded_launcher: false,
- },
py3: {
- enabled: true,
embedded_launcher: true,
},
},
diff --git a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
index a415217105a8..bba819cd9096 100644
--- a/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
+++ b/tools/lint/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionDetector.kt
@@ -82,8 +82,16 @@ class EnforcePermissionDetector : Detector(), SourceCodeScanner {
if (attr1[i].name != attr2[i].name) {
return false
}
- val v1 = ConstantEvaluator.evaluate(context, attr1[i].value)
- val v2 = ConstantEvaluator.evaluate(context, attr2[i].value)
+ val value1 = attr1[i].value
+ val value2 = attr2[i].value
+ if (value1 == null && value2 == null) {
+ continue
+ }
+ if (value1 == null || value2 == null) {
+ return false
+ }
+ val v1 = ConstantEvaluator.evaluate(context, value1)
+ val v2 = ConstantEvaluator.evaluate(context, value2)
if (v1 != v2) {
return false
}
diff --git a/tools/processors/immutability/Android.bp b/tools/processors/immutability/Android.bp
new file mode 100644
index 000000000000..fe97a903bff8
--- /dev/null
+++ b/tools/processors/immutability/Android.bp
@@ -0,0 +1,76 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the '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: "ImmutabilityAnnotationProcessorHostLibrary",
+ srcs: [
+ "src/**/*.kt",
+ "src/**/*.java",
+ ],
+ use_tools_jar: true,
+ javacflags: [
+ "--add-modules=jdk.compiler",
+ "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
+}
+
+java_plugin {
+ name: "ImmutabilityAnnotationProcessor",
+ processor_class: "android.processor.immutability.ImmutabilityProcessor",
+ static_libs: ["ImmutabilityAnnotationProcessorHostLibrary"],
+ javacflags: [
+ "--add-modules=jdk.compiler",
+ "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
+}
+
+java_library {
+ name: "ImmutabilityAnnotation",
+ srcs: ["src/**/Immutable.java"],
+ sdk_version: "core_current",
+ host_supported: true,
+}
+
+java_test_host {
+ name: "ImmutabilityAnnotationProcessorUnitTests",
+
+ srcs: ["test/**/*.kt"],
+
+ static_libs: [
+ "compile-testing-prebuilt",
+ "truth-prebuilt",
+ "junit",
+ "kotlin-reflect",
+ "ImmutabilityAnnotationProcessorHostLibrary",
+ ],
+
+ // Bundle the source file so it can be loaded into the test compiler
+ java_resources: [":ImmutabilityAnnotationJavaSource"],
+
+ test_suites: ["general-tests"],
+ javacflags: [
+ "--add-modules=jdk.compiler",
+ "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
+}
+
+filegroup {
+ name: "ImmutabilityAnnotationJavaSource",
+ srcs: ["src/android/processor/immutability/Immutable.java"],
+ path: "src/android/processor/immutability/",
+}
diff --git a/tools/processors/immutability/OWNERS b/tools/processors/immutability/OWNERS
new file mode 100644
index 000000000000..86ae5818e91c
--- /dev/null
+++ b/tools/processors/immutability/OWNERS
@@ -0,0 +1 @@
+include /PACKAGE_MANAGER_OWNERS
diff --git a/tools/processors/immutability/TEST_MAPPING b/tools/processors/immutability/TEST_MAPPING
new file mode 100644
index 000000000000..4e8e2386ad8d
--- /dev/null
+++ b/tools/processors/immutability/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "ImmutabilityAnnotationProcessorUnitTests"
+ }
+ ]
+}
diff --git a/tools/processors/immutability/src/android/processor/immutability/ImmutabilityProcessor.kt b/tools/processors/immutability/src/android/processor/immutability/ImmutabilityProcessor.kt
new file mode 100644
index 000000000000..3ab09a8366be
--- /dev/null
+++ b/tools/processors/immutability/src/android/processor/immutability/ImmutabilityProcessor.kt
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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:Suppress("JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE")
+
+package android.processor.immutability
+
+import com.sun.tools.javac.code.Symbol
+import com.sun.tools.javac.code.Type
+import javax.annotation.processing.AbstractProcessor
+import javax.annotation.processing.ProcessingEnvironment
+import javax.annotation.processing.RoundEnvironment
+import javax.lang.model.SourceVersion
+import javax.lang.model.element.Element
+import javax.lang.model.element.ElementKind
+import javax.lang.model.element.Modifier
+import javax.lang.model.element.TypeElement
+import javax.lang.model.type.TypeKind
+import javax.lang.model.type.TypeMirror
+import javax.tools.Diagnostic
+
+val IMMUTABLE_ANNOTATION_NAME = Immutable::class.qualifiedName
+
+class ImmutabilityProcessor : AbstractProcessor() {
+
+ companion object {
+ /**
+ * Types that are already immutable.
+ */
+ private val IGNORED_TYPES = listOf(
+ "java.io.File",
+ "java.lang.Boolean",
+ "java.lang.Byte",
+ "java.lang.CharSequence",
+ "java.lang.Character",
+ "java.lang.Double",
+ "java.lang.Float",
+ "java.lang.Integer",
+ "java.lang.Long",
+ "java.lang.Short",
+ "java.lang.String",
+ "java.lang.Void",
+ "android.os.Parcelable.Creator",
+ )
+
+ private val IGNORED_METHODS = listOf(
+ "writeToParcel",
+ )
+ }
+
+ private lateinit var collectionType: TypeMirror
+ private lateinit var mapType: TypeMirror
+
+ private lateinit var ignoredTypes: List<TypeMirror>
+
+ private val seenTypesByPolicy = mutableMapOf<Set<Immutable.Policy.Exception>, Set<Type>>()
+
+ override fun getSupportedSourceVersion() = SourceVersion.latest()!!
+
+ override fun getSupportedAnnotationTypes() = setOf(Immutable::class.qualifiedName)
+
+ override fun init(processingEnv: ProcessingEnvironment) {
+ super.init(processingEnv)
+ collectionType = processingEnv.erasedType("java.util.Collection")!!
+ mapType = processingEnv.erasedType("java.util.Map")!!
+ ignoredTypes = IGNORED_TYPES.mapNotNull { processingEnv.erasedType(it) }
+ }
+
+ override fun process(
+ annotations: MutableSet<out TypeElement>,
+ roundEnvironment: RoundEnvironment
+ ): Boolean {
+ annotations.find {
+ it.qualifiedName.toString() == IMMUTABLE_ANNOTATION_NAME
+ } ?: return false
+ roundEnvironment.getElementsAnnotatedWith(Immutable::class.java)
+ .forEach {
+ visitClass(
+ parentChain = emptyList(),
+ seenTypesByPolicy = seenTypesByPolicy,
+ elementToPrint = it,
+ classType = it as Symbol.TypeSymbol,
+ parentPolicyExceptions = emptySet()
+ )
+ }
+ return true
+ }
+
+ /**
+ * @return true if any error was encountered at this level or any child level
+ */
+ private fun visitClass(
+ parentChain: List<String>,
+ seenTypesByPolicy: MutableMap<Set<Immutable.Policy.Exception>, Set<Type>>,
+ elementToPrint: Element,
+ classType: Symbol.TypeSymbol,
+ parentPolicyExceptions: Set<Immutable.Policy.Exception>,
+ ): Boolean {
+ if (classType.getAnnotation(Immutable.Ignore::class.java) != null) return false
+
+ val policyAnnotation = classType.getAnnotation(Immutable.Policy::class.java)
+ val newPolicyExceptions = parentPolicyExceptions + policyAnnotation?.exceptions.orEmpty()
+
+ // If already seen this type with the same policies applied, skip it
+ val seenTypes = seenTypesByPolicy[newPolicyExceptions]
+ val type = classType.asType()
+ if (seenTypes?.contains(type) == true) return false
+ seenTypesByPolicy[newPolicyExceptions] = seenTypes.orEmpty() + type
+
+ val allowFinalClassesFinalFields =
+ newPolicyExceptions.contains(Immutable.Policy.Exception.FINAL_CLASSES_WITH_FINAL_FIELDS)
+
+ val filteredElements = classType.enclosedElements
+ .filterNot(::isIgnored)
+
+ val hasFieldError = filteredElements
+ .filter { it.getKind() == ElementKind.FIELD }
+ .fold(false) { anyError, field ->
+ if (field.isStatic) {
+ if (!field.isPrivate) {
+ var finalityError = !field.modifiers.contains(Modifier.FINAL)
+ if (finalityError) {
+ printError(parentChain, field, MessageUtils.staticNonFinalFailure())
+ }
+
+ // Must call visitType first so it doesn't get short circuited by the ||
+ visitType(
+ parentChain = parentChain,
+ seenTypesByPolicy = seenTypesByPolicy,
+ symbol = field,
+ type = field.type,
+ parentPolicyExceptions = parentPolicyExceptions
+ ) || anyError || finalityError
+ }
+ return@fold anyError
+ } else {
+ val isFinal = field.modifiers.contains(Modifier.FINAL)
+ if (!isFinal || !allowFinalClassesFinalFields) {
+ printError(parentChain, field, MessageUtils.memberNotMethodFailure())
+ return@fold true
+ }
+
+ return@fold anyError
+ }
+ }
+
+ // Scan inner classes before methods so that any violations isolated to the file prints
+ // the error on the class declaration rather than on the method that returns the type.
+ // Although it doesn't matter too much either way.
+ val hasClassError = filteredElements
+ .filter { it.getKind() == ElementKind.CLASS }
+ .map { it as Symbol.ClassSymbol }
+ .fold(false) { anyError, innerClass ->
+ // Must call visitClass first so it doesn't get short circuited by the ||
+ visitClass(
+ parentChain,
+ seenTypesByPolicy,
+ innerClass,
+ innerClass,
+ newPolicyExceptions
+ ) || anyError
+ }
+
+ val newChain = parentChain + "$classType"
+
+ val hasMethodError = filteredElements
+ .filter { it.getKind() == ElementKind.METHOD }
+ .map { it as Symbol.MethodSymbol }
+ .filterNot { IGNORED_METHODS.contains(it.name.toString()) }
+ .fold(false) { anyError, method ->
+ // Must call visitMethod first so it doesn't get short circuited by the ||
+ visitMethod(newChain, seenTypesByPolicy, method, newPolicyExceptions) || anyError
+ }
+
+ val className = classType.simpleName.toString()
+ val isRegularClass = classType.getKind() == ElementKind.CLASS
+
+ var anyError = hasFieldError || hasClassError || hasMethodError
+
+ // If final classes are not considered OR there's a non-field failure, also check for
+ // interface/@Immutable, assuming the class is malformed
+ if ((isRegularClass && !allowFinalClassesFinalFields) || hasMethodError || hasClassError) {
+ if (classType.getAnnotation(Immutable::class.java) == null) {
+ printError(
+ parentChain,
+ elementToPrint,
+ MessageUtils.classNotImmutableFailure(className)
+ )
+ anyError = true
+ }
+
+ if (classType.getKind() != ElementKind.INTERFACE) {
+ printError(parentChain, elementToPrint, MessageUtils.nonInterfaceClassFailure())
+ anyError = true
+ }
+ }
+
+ if (isRegularClass && !anyError && allowFinalClassesFinalFields &&
+ !classType.modifiers.contains(Modifier.FINAL)
+ ) {
+ printError(parentChain, elementToPrint, MessageUtils.classNotFinalFailure(className))
+ return true
+ }
+
+ return anyError
+ }
+
+ /**
+ * @return true if any error was encountered at this level or any child level
+ */
+ private fun visitMethod(
+ parentChain: List<String>,
+ seenTypesByPolicy: MutableMap<Set<Immutable.Policy.Exception>, Set<Type>>,
+ method: Symbol.MethodSymbol,
+ parentPolicyExceptions: Set<Immutable.Policy.Exception>,
+ ): Boolean {
+ val returnType = method.returnType
+ val typeName = returnType.toString()
+ when (returnType.kind) {
+ TypeKind.BOOLEAN,
+ TypeKind.BYTE,
+ TypeKind.SHORT,
+ TypeKind.INT,
+ TypeKind.LONG,
+ TypeKind.CHAR,
+ TypeKind.FLOAT,
+ TypeKind.DOUBLE,
+ TypeKind.NONE,
+ TypeKind.NULL -> {
+ // Do nothing
+ }
+ TypeKind.VOID -> {
+ if (!method.isConstructor) {
+ printError(parentChain, method, MessageUtils.voidReturnFailure())
+ return true
+ }
+ }
+ TypeKind.ARRAY -> {
+ printError(parentChain, method, MessageUtils.arrayFailure())
+ return true
+ }
+ TypeKind.DECLARED -> {
+ return visitType(
+ parentChain,
+ seenTypesByPolicy,
+ method,
+ method.returnType,
+ parentPolicyExceptions
+ )
+ }
+ TypeKind.ERROR,
+ TypeKind.TYPEVAR,
+ TypeKind.WILDCARD,
+ TypeKind.PACKAGE,
+ TypeKind.EXECUTABLE,
+ TypeKind.OTHER,
+ TypeKind.UNION,
+ TypeKind.INTERSECTION,
+ // Java 9+
+ // TypeKind.MODULE,
+ null -> {
+ printError(
+ parentChain, method,
+ MessageUtils.genericTypeKindFailure(typeName = typeName)
+ )
+ return true
+ }
+ else -> {
+ printError(
+ parentChain, method,
+ MessageUtils.genericTypeKindFailure(typeName = typeName)
+ )
+ return true
+ }
+ }
+
+ return false
+ }
+
+ /**
+ * @return true if any error was encountered at this level or any child level
+ */
+ private fun visitType(
+ parentChain: List<String>,
+ seenTypesByPolicy: MutableMap<Set<Immutable.Policy.Exception>, Set<Type>>,
+ symbol: Symbol,
+ type: Type,
+ parentPolicyExceptions: Set<Immutable.Policy.Exception>,
+ nonInterfaceClassFailure: () -> String = { MessageUtils.nonInterfaceReturnFailure() },
+ ): Boolean {
+ if (type.isPrimitive) return false
+ if (type.isPrimitiveOrVoid) {
+ printError(parentChain, symbol, MessageUtils.voidReturnFailure())
+ return true
+ }
+
+ if (ignoredTypes.any { processingEnv.typeUtils.isAssignable(type, it) }) {
+ return false
+ }
+
+ val policyAnnotation = symbol.getAnnotation(Immutable.Policy::class.java)
+ val newPolicyExceptions = parentPolicyExceptions + policyAnnotation?.exceptions.orEmpty()
+
+ // Collection (and Map) types are ignored for the interface check as they have immutability
+ // enforced through a runtime exception which must be verified in a separate runtime test
+ val isMap = processingEnv.typeUtils.isAssignable(type, mapType)
+ if (!processingEnv.typeUtils.isAssignable(type, collectionType) && !isMap) {
+ if (!type.isInterface && !newPolicyExceptions
+ .contains(Immutable.Policy.Exception.FINAL_CLASSES_WITH_FINAL_FIELDS)
+ ) {
+ printError(parentChain, symbol, nonInterfaceClassFailure())
+ return true
+ } else {
+ return visitClass(
+ parentChain, seenTypesByPolicy, symbol,
+ processingEnv.typeUtils.asElement(type) as Symbol.TypeSymbol,
+ newPolicyExceptions,
+ )
+ }
+ }
+
+ var anyError = false
+
+ type.typeArguments.forEachIndexed { index, typeArg ->
+ val argError =
+ visitType(parentChain, seenTypesByPolicy, symbol, typeArg, newPolicyExceptions) {
+ MessageUtils.nonInterfaceReturnFailure(
+ prefix = when {
+ !isMap -> ""
+ index == 0 -> "Key " + typeArg.asElement().simpleName
+ else -> "Value " + typeArg.asElement().simpleName
+ }, index = index
+ )
+ }
+ anyError = anyError || argError
+ }
+
+ return anyError
+ }
+
+ private fun printError(
+ parentChain: List<String>,
+ element: Element,
+ message: String,
+ ) = processingEnv.messager.printMessage(
+ Diagnostic.Kind.ERROR,
+ // Drop one from the parent chain so that the directly enclosing class isn't logged.
+ // It exists in the list at this point in the traversal so that further children can
+ // include the right reference.
+ parentChain.dropLast(1).joinToString() + "\n\t" + message,
+ element,
+ )
+
+ private fun ProcessingEnvironment.erasedType(typeName: String) =
+ elementUtils.getTypeElement(typeName)?.asType()?.let(typeUtils::erasure)
+
+ private fun isIgnored(symbol: Symbol) =
+ symbol.getAnnotation(Immutable.Ignore::class.java) != null
+}
diff --git a/tools/processors/immutability/src/android/processor/immutability/Immutable.java b/tools/processors/immutability/src/android/processor/immutability/Immutable.java
new file mode 100644
index 000000000000..ba822ba562ee
--- /dev/null
+++ b/tools/processors/immutability/src/android/processor/immutability/Immutable.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.immutability;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Marks a class as immutable. When used with the Immutability processor, verifies at compile that
+ * the class is truly immutable. Immutable is defined as:
+ * <ul>
+ * <li>Only exposes methods and/or static final constants</li>
+ * <li>Every exposed type is an @Immutable interface or otherwise immutable class</li>
+ * <ul>
+ * <li>Implicitly immutable types like {@link String} are ignored</li>
+ * <li>{@link Collection} and {@link Map} and their subclasses where immutability is
+ * enforced at runtime are ignored</li>
+ * </ul>
+ * <li>Every method must return a type (no void methods allowed)</li>
+ * <li>All inner classes must be @Immutable interfaces</li>
+ * </ul>
+ */
+public @interface Immutable {
+
+ /**
+ * Marks a specific class, field, or method as ignored for immutability validation.
+ */
+ @Retention(RetentionPolicy.CLASS) // Not SOURCE as that isn't retained for some reason
+ @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+ @interface Ignore {
+ String reason() default "";
+ }
+
+ /**
+ * Marks an element and its reachable children with a specific policy.
+ */
+ @Retention(RetentionPolicy.CLASS)
+ @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
+ @interface Policy {
+ Exception[] exceptions() default {};
+
+ enum Exception {
+ /**
+ * Allow final classes with only final fields. By default these are not allowed because
+ * direct field access disallows hard removal of APIs (by having their getters return
+ * mocks/stubs) and also prevents field compaction, which can occur with booleans
+ * stuffed into a number as flags.
+ *
+ * This exception is allowed though because several framework classes are built around
+ * the final field access model and it would be unnecessarily difficult to migrate or
+ * wrap each type.
+ */
+ FINAL_CLASSES_WITH_FINAL_FIELDS,
+ }
+ }
+}
diff --git a/tools/processors/immutability/src/android/processor/immutability/MessageUtils.kt b/tools/processors/immutability/src/android/processor/immutability/MessageUtils.kt
new file mode 100644
index 000000000000..7fa2fb56deb1
--- /dev/null
+++ b/tools/processors/immutability/src/android/processor/immutability/MessageUtils.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor.immutability
+
+object MessageUtils {
+
+ fun classNotImmutableFailure(className: String) = "$className should be marked @Immutable"
+
+ fun classNotFinalFailure(className: String) = "$className should be marked final"
+
+ fun memberNotMethodFailure() = "Member must be a method"
+
+ fun nonInterfaceClassFailure() = "Class was not an interface"
+
+ fun nonInterfaceReturnFailure(prefix: String, index: Int = -1) =
+ if (prefix.isEmpty()) {
+ "Type at index $index was not an interface"
+ } else {
+ "$prefix was not an interface"
+ }
+
+ fun genericTypeKindFailure(typeName: CharSequence) = "TypeKind $typeName unsupported"
+
+ fun arrayFailure() = "Array types are not supported as they can be mutated by callers"
+
+ fun nonInterfaceReturnFailure() = "Must return an interface"
+
+ fun voidReturnFailure() = "Cannot return void"
+
+ fun staticNonFinalFailure() = "Static member must be final"
+} \ No newline at end of file
diff --git a/tools/processors/immutability/test/android/processor/ImmutabilityProcessorTest.kt b/tools/processors/immutability/test/android/processor/ImmutabilityProcessorTest.kt
new file mode 100644
index 000000000000..f26357ff8e4b
--- /dev/null
+++ b/tools/processors/immutability/test/android/processor/ImmutabilityProcessorTest.kt
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.processor
+
+import android.processor.immutability.IMMUTABLE_ANNOTATION_NAME
+import android.processor.immutability.ImmutabilityProcessor
+import android.processor.immutability.MessageUtils
+import com.google.common.truth.Expect
+import com.google.testing.compile.CompilationSubject.assertThat
+import com.google.testing.compile.Compiler.javac
+import com.google.testing.compile.JavaFileObjects
+import org.junit.Rule
+import org.junit.Test
+import java.util.*
+import javax.tools.JavaFileObject
+
+class ImmutabilityProcessorTest {
+
+ companion object {
+ private const val PACKAGE_PREFIX = "android.processor.immutability"
+ private const val DATA_CLASS_NAME = "DataClass"
+ private val ANNOTATION = JavaFileObjects.forResource("Immutable.java")
+
+ private val FINAL_CLASSES = listOf(
+ JavaFileObjects.forSourceString(
+ "$PACKAGE_PREFIX.NonFinalClassFinalFields",
+ /* language=JAVA */ """
+ package $PACKAGE_PREFIX;
+
+ public class NonFinalClassFinalFields {
+ private final String finalField;
+ public NonFinalClassFinalFields(String value) {
+ this.finalField = value;
+ }
+ }
+ """.trimIndent()
+ ),
+ JavaFileObjects.forSourceString(
+ "$PACKAGE_PREFIX.NonFinalClassNonFinalFields",
+ /* language=JAVA */ """
+ package $PACKAGE_PREFIX;
+
+ public class NonFinalClassNonFinalFields {
+ private String nonFinalField;
+ }
+ """.trimIndent()
+ ),
+ JavaFileObjects.forSourceString(
+ "$PACKAGE_PREFIX.FinalClassFinalFields",
+ /* language=JAVA */ """
+ package $PACKAGE_PREFIX;
+
+ public final class FinalClassFinalFields {
+ private final String finalField;
+ public FinalClassFinalFields(String value) {
+ this.finalField = value;
+ }
+ }
+ """.trimIndent()
+ ),
+ JavaFileObjects.forSourceString(
+ "$PACKAGE_PREFIX.FinalClassNonFinalFields",
+ /* language=JAVA */ """
+ package $PACKAGE_PREFIX;
+
+ public final class FinalClassNonFinalFields {
+ private String nonFinalField;
+ }
+ """.trimIndent()
+ )
+ )
+ }
+
+ @get:Rule
+ val expect = Expect.create()
+
+ @Test
+ fun validInterface() = test(
+ JavaFileObjects.forSourceString(
+ "$PACKAGE_PREFIX.$DATA_CLASS_NAME",
+ /* language=JAVA */ """
+ package $PACKAGE_PREFIX;
+
+ import $IMMUTABLE_ANNOTATION_NAME;
+ import java.util.ArrayList;
+ import java.util.Collections;
+ import java.util.List;
+
+ @Immutable
+ public interface $DATA_CLASS_NAME {
+ InnerInterface DEFAULT = new InnerInterface() {
+ @Override
+ public String getValue() {
+ return "";
+ }
+ @Override
+ public List<String> getArray() {
+ return Collections.emptyList();
+ }
+ };
+
+ String getValue();
+ ArrayList<String> getArray();
+ InnerInterface getInnerInterface();
+
+ @Immutable
+ interface InnerInterface {
+ String getValue();
+ List<String> getArray();
+ }
+ }
+ """.trimIndent()
+ ), errors = emptyList()
+ )
+
+ @Test
+ fun abstractClass() = test(
+ JavaFileObjects.forSourceString(
+ "$PACKAGE_PREFIX.$DATA_CLASS_NAME",
+ /* language=JAVA */ """
+ package $PACKAGE_PREFIX;
+
+ import $IMMUTABLE_ANNOTATION_NAME;
+ import java.util.Map;
+
+ @Immutable
+ public abstract class $DATA_CLASS_NAME {
+ public static final String IMMUTABLE = "";
+ public static final InnerClass NOT_IMMUTABLE = null;
+ public static InnerClass NOT_FINAL = null;
+
+ // Field finality doesn't matter, methods are always enforced so that future
+ // field compaction or deprecation is possible
+ private final String fieldFinal = "";
+ private String fieldNonFinal;
+ public abstract void sideEffect();
+ public abstract String[] getArray();
+ public abstract InnerClass getInnerClassOne();
+ public abstract InnerClass getInnerClassTwo();
+ @Immutable.Ignore
+ public abstract InnerClass getIgnored();
+ public abstract InnerInterface getInnerInterface();
+
+ public abstract Map<String, String> getValidMap();
+ public abstract Map<InnerClass, InnerClass> getInvalidMap();
+
+ public static final class InnerClass {
+ public String innerField;
+ public String[] getArray() { return null; }
+ }
+
+ public interface InnerInterface {
+ String[] getArray();
+ InnerClass getInnerClass();
+ }
+ }
+ """.trimIndent()
+ ), errors = listOf(
+ nonInterfaceClassFailure(line = 7),
+ nonInterfaceReturnFailure(line = 9),
+ staticNonFinalFailure(line = 10),
+ nonInterfaceReturnFailure(line = 10),
+ memberNotMethodFailure(line = 14),
+ memberNotMethodFailure(line = 15),
+ voidReturnFailure(line = 16),
+ arrayFailure(line = 17),
+ nonInterfaceReturnFailure(line = 18),
+ nonInterfaceReturnFailure(line = 19),
+ classNotImmutableFailure(line = 22, className = "InnerInterface"),
+ nonInterfaceReturnFailure(line = 25, prefix = "Key InnerClass"),
+ nonInterfaceReturnFailure(line = 25, prefix = "Value InnerClass"),
+ classNotImmutableFailure(line = 27, className = "InnerClass"),
+ nonInterfaceClassFailure(line = 27),
+ memberNotMethodFailure(line = 28),
+ arrayFailure(line = 29),
+ arrayFailure(line = 33),
+ nonInterfaceReturnFailure(line = 34),
+ )
+ )
+
+ @Test
+ fun finalClasses() = test(
+ JavaFileObjects.forSourceString(
+ "$PACKAGE_PREFIX.$DATA_CLASS_NAME",
+ /* language=JAVA */ """
+ package $PACKAGE_PREFIX;
+
+ import java.util.List;
+
+ @Immutable
+ public interface $DATA_CLASS_NAME {
+ NonFinalClassFinalFields getNonFinalFinal();
+ List<NonFinalClassNonFinalFields> getNonFinalNonFinal();
+ FinalClassFinalFields getFinalFinal();
+ List<FinalClassNonFinalFields> getFinalNonFinal();
+
+ @Immutable.Policy(exceptions = {Immutable.Policy.Exception.FINAL_CLASSES_WITH_FINAL_FIELDS})
+ NonFinalClassFinalFields getPolicyNonFinalFinal();
+
+ @Immutable.Policy(exceptions = {Immutable.Policy.Exception.FINAL_CLASSES_WITH_FINAL_FIELDS})
+ List<NonFinalClassNonFinalFields> getPolicyNonFinalNonFinal();
+
+ @Immutable.Policy(exceptions = {Immutable.Policy.Exception.FINAL_CLASSES_WITH_FINAL_FIELDS})
+ FinalClassFinalFields getPolicyFinalFinal();
+
+ @Immutable.Policy(exceptions = {Immutable.Policy.Exception.FINAL_CLASSES_WITH_FINAL_FIELDS})
+ List<FinalClassNonFinalFields> getPolicyFinalNonFinal();
+ }
+ """.trimIndent()
+ ), errors = listOf(
+ nonInterfaceReturnFailure(line = 7),
+ nonInterfaceReturnFailure(line = 8, index = 0),
+ nonInterfaceReturnFailure(line = 9),
+ nonInterfaceReturnFailure(line = 10, index = 0),
+ classNotFinalFailure(line = 13, "NonFinalClassFinalFields"),
+ ), otherErrors = listOf(
+ memberNotMethodFailure(line = 4) to FINAL_CLASSES[1],
+ memberNotMethodFailure(line = 4) to FINAL_CLASSES[3],
+ )
+ )
+
+ private fun test(
+ source: JavaFileObject,
+ errors: List<CompilationError>,
+ otherErrors: List<Pair<CompilationError, JavaFileObject>> = emptyList(),
+ ) {
+ val compilation = javac()
+ .withProcessors(ImmutabilityProcessor())
+ .compile(FINAL_CLASSES + ANNOTATION + listOf(source))
+ val allErrors = otherErrors + errors.map { it to source }
+ allErrors.forEach { (error, file) ->
+ try {
+ assertThat(compilation)
+ .hadErrorContaining(error.message)
+ .inFile(file)
+ .onLine(error.line)
+ } catch (e: AssertionError) {
+ // Wrap the exception so that the line number is logged
+ val wrapped = AssertionError("Expected $error, ${e.message}").apply {
+ stackTrace = e.stackTrace
+ }
+
+ // Wrap again with Expect so that all errors are reported. This is very bad code
+ // but can only be fixed by updating compile-testing with a better Truth Subject
+ // implementation.
+ expect.that(wrapped).isNull()
+ }
+ }
+
+ try {
+ assertThat(compilation).hadErrorCount(allErrors.size)
+ } catch (e: AssertionError) {
+ expect.withMessage(
+ compilation.errors()
+ .joinToString(separator = "\n") {
+ "${it.lineNumber}: ${it.getMessage(Locale.ENGLISH)?.trim()}"
+ }
+ ).that(e).isNull()
+ }
+ }
+
+ private fun classNotImmutableFailure(line: Long, className: String) =
+ CompilationError(line = line, message = MessageUtils.classNotImmutableFailure(className))
+
+ private fun nonInterfaceClassFailure(line: Long) =
+ CompilationError(line = line, message = MessageUtils.nonInterfaceClassFailure())
+
+ private fun nonInterfaceReturnFailure(line: Long) =
+ CompilationError(line = line, message = MessageUtils.nonInterfaceReturnFailure())
+
+ private fun nonInterfaceReturnFailure(line: Long, prefix: String = "", index: Int = -1) =
+ CompilationError(
+ line = line,
+ message = MessageUtils.nonInterfaceReturnFailure(prefix = prefix, index = index)
+ )
+
+ private fun memberNotMethodFailure(line: Long) =
+ CompilationError(line = line, message = MessageUtils.memberNotMethodFailure())
+
+ private fun voidReturnFailure(line: Long) =
+ CompilationError(line = line, message = MessageUtils.voidReturnFailure())
+
+ private fun staticNonFinalFailure(line: Long) =
+ CompilationError(line = line, message = MessageUtils.staticNonFinalFailure())
+
+ private fun arrayFailure(line: Long) =
+ CompilationError(line = line, message = MessageUtils.arrayFailure())
+
+ private fun classNotFinalFailure(line: Long, className: String) =
+ CompilationError(line = line, message = MessageUtils.classNotFinalFailure(className))
+
+ data class CompilationError(
+ val line: Long,
+ val message: String,
+ )
+} \ No newline at end of file
diff --git a/tools/processors/staledataclass/Android.bp b/tools/processors/staledataclass/Android.bp
index 1e5097662a0a..2169c49a91a5 100644
--- a/tools/processors/staledataclass/Android.bp
+++ b/tools/processors/staledataclass/Android.bp
@@ -22,17 +22,13 @@ java_plugin {
static_libs: [
"codegen-version-info",
],
- // The --add-modules/exports flags below don't work for kotlinc yet, so pin this module to Java language level 8 (see b/139342589):
- java_version: "1.8",
- openjdk9: {
- javacflags: [
- "--add-modules=jdk.compiler",
- "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
- "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
- "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
- "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
- ],
- },
+ javacflags: [
+ "--add-modules=jdk.compiler",
+ "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+ "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+ ],
use_tools_jar: true,
}
diff --git a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
index 27a8853a2219..1cef5b0c8dfb 100644
--- a/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
+++ b/tools/processors/staledataclass/src/android/processor/staledataclass/StaleDataclassProcessor.kt
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+@file:Suppress("JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE")
package android.processor.staledataclass